一個(gè)簡(jiǎn)單的自定義按鈕


date : 2019.10.15
author : lrcoder


Demo 介紹

  • Demo靈感

    陰陽師手游中,體力是一個(gè)重要的資源。在游戲的設(shè)定中,每位陰陽師的體力上限為100,每隔三分鐘恢復(fù)一點(diǎn)體力。當(dāng)體力值自動(dòng)增長(zhǎng)至100后,停止增長(zhǎng);當(dāng)體力消耗至0后,體力不會(huì)為負(fù),而是禁止陰陽師進(jìn)行別的消耗體力操作。

  • Demo功能

    1. 支持自定義背景顏色,默認(rèn)為紅色
    2. 支持自定義按鈕形狀, 默認(rèn)為圓形
    3. 支持自定義數(shù)字顏色, 默認(rèn)為白色
    4. 支持自定義數(shù)字大小, 默認(rèn)為200dp
    5. 支持自定義數(shù)字最值, 默認(rèn)最大為20, 最小為0
    6. 支持自定義初始值, 默認(rèn)為20
    7. 支持提供數(shù)字增加、減少的方法
    8. 支持?jǐn)?shù)字自動(dòng)增加
  • Demo所用到的技術(shù)

    1. 自定義控件
    2. Android多線程(RxJava2)

功能代碼和分析

  • 用于用戶自定義的xml配置文件

    如果希望該自定義控件可以和系統(tǒng)控件相似,可以通過android:XX="XX"的形式對(duì)控件屬性進(jìn)行設(shè)置,那么便需要在values/attrs.xml文件中添加一下內(nèi)容:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 自定義控件的類名 -->
    <declare-styleable name="CountButton">
        <!-- 自定義背景顏色 -->
        <attr name="background_color" format="color|reference"/>
        <!-- 自定義按鈕形狀  -->
        <attr name="button_style">
            <flag name="circle" value="0"/>
            <flag name="rect" value="1"/>
        </attr>
        <!-- 自定義數(shù)字顏色 -->
        <attr name="number_color" format="color|reference"/>
        <!-- 自定義數(shù)字大小 -->
        <attr name="number_size" format="dimension"/>
        <!-- 自定義數(shù)字最小值 -->
        <attr name="number_min" format="integer"/>
        <!-- 自定義數(shù)字最大值 -->
        <attr name="number_max" format="integer"/>
        <!-- 自定義數(shù)字初始值 -->
        <attr name="number_default" format="integer"/>
    </declare-styleable>
</resources>

有關(guān)declare-styleable、attr就沒什么好說的,主要來看一下format的值和含義:

format 含義 獲取方式
reference 資源ID,類似于@strings/...的資源文件 mTypedArray.getResourceId() ... ...
color 顏色值 mTypedArray.getColor()
dimension 尺寸值 mTypedArray.getDimension()
integer 整型值 mTypedArray.getInt()
string 字符串 mTypedArray.getString()
boolean 布爾值 mTypedArray.getBoolean()
enum 枚舉值
float 浮點(diǎn)值 mTypedArray.getFloat()
fraction 百分?jǐn)?shù) mTypedArray.getFraction()
flag 位或運(yùn)算 ... ...

其中,enumflag給我的感覺就是鍵值對(duì),我們可以通過其value值的類型來對(duì)控件作出相應(yīng)的控制,接下來我們就可以在布局中使用我們剛才定義的屬性值:

   <org.code.demo.countbuttonview.CountButton
       android:id="@+id/count_button"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:background_color="#0744DF"
       app:button_style="circle"
       app:number_color="#ffffff"
       app:number_max="20"
       app:number_min="0"
       app:number_size="200sp"
       app:number_default="30"/>
  • 畫控件

自定義控件的繼承關(guān)系:

  1. 繼承已有控件進(jìn)行自定義控件
  2. 繼承布局文件進(jìn)行自定義控件
  3. 繼承View類來實(shí)現(xiàn)自定義控件

創(chuàng)建一個(gè)CountButton類,這個(gè)就是我們自定義的控件名稱,他繼承自View,下面先上碼,具體的解釋放在代碼的注釋中

/**
 * @ProjectName: CountButtonView
 * @Package: org.code.demo.countbuttonview
 * @ClassName: CountButton
 * @Description: 1. 自定義背景顏色,默認(rèn)為紅色
 *               2. 自定義按鈕形狀, 默認(rèn)為圓形
 *               3. 自定義數(shù)字顏色, 默認(rèn)為白色
 *               4. 自定義數(shù)字大小, 默認(rèn)為200dp
 *               5. 自定義數(shù)字最值, 默認(rèn)最大為20, 最小為0
 *               6. 自定義初始值, 默認(rèn)為20
 *               7. 提供數(shù)字增加、減少的方法
 * @Author: lrcoder
 * @CreateDate: 2019/10/15
 */
public class CountButton extends View implements View.OnClickListener {

    // TAG ---> log
    private static final String TAG = CountButton.class.getSimpleName();

    public CompositeDisposable mCompositeDisposable = new CompositeDisposable();

    private int mBackgroundColor;
    private int mButtonStyle;
    private int mTextColor;
    private int mMinNumber;
    private int mMaxNumber;
    protected Paint mPaint;
    private Rect mRect;
    private int mNumber;
    float mTextSize;

    public CountButton(Context context) {
        super(context, null);
    }

    public CountButton(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs, 0);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {

        // 獲取自定義的屬性值
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CountButton);
        mBackgroundColor = mTypedArray
                .getColor(R.styleable.CountButton_background_color, Color.RED);
        mButtonStyle = mTypedArray
                .getInt(R.styleable.CountButton_button_style, -1);
        mMinNumber = mTypedArray
                .getInt(R.styleable.CountButton_number_min, 0);
        mMaxNumber = mTypedArray
                .getInt(R.styleable.CountButton_number_max, 20);
        mTextColor = mTypedArray
                .getInt(R.styleable.CountButton_number_color, Color.WHITE);
        mTextSize = mTypedArray
                .getDimension(R.styleable.CountButton_number_size, 200);
        int mDefaultNumber = mTypedArray
                .getInt(R.styleable.CountButton_number_default, 20);
        mTypedArray.recycle();
        mNumber = mDefaultNumber;
        mRect = new Rect();
        mPaint = new Paint();
        this.setOnClickListener(this);
        startCount();
    }

    /**
     * 繪制控件
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 設(shè)置畫筆背景 (1. 自定義背景顏色,默認(rèn)為紅色)
        mPaint.setColor(mBackgroundColor);
        // 確定繪制形狀 (2. 自定義按鈕形狀, 默認(rèn)為圓形)
        switch(mButtonStyle) {
            case 0:
                // todo: draw Circle
                canvas.drawCircle(
                        getWidth() / 2,
                        getHeight() / 2,
                        getWidth() / 2,
                        mPaint);
                break;
            case 1:
                // todo: draw Rect
                canvas.drawRect(
                        getLeft(),
                        getTop(),
                        getRight(),
                        getBottom(),
                        mPaint);
                break;
            default:
                // todo: draw Circle 同時(shí) Log.i 提示
                canvas.drawCircle(
                        getWidth() / 2,
                        getHeight() / 2,
                        getWidth() / 2,
                        mPaint);
                Log.e(TAG, "error settings!!!");
                break;
        }
        // 確定數(shù)字顏色 (3. 自定義數(shù)字顏色, 默認(rèn)為白色)
        mPaint.setColor(mTextColor);
        // 確定數(shù)字大小 (4. 自定義數(shù)字大小, 默認(rèn)為200dp)
        mPaint.setTextSize(mTextSize);
        // 確認(rèn)數(shù)字初始值
        String text =String.valueOf(mNumber);
        mPaint.getTextBounds(text, 0, text.length(), mRect);
        int width = mRect.width();
        int height = mRect.height();
        canvas.drawText(
                text,
                getWidth() / 2 - width / 2,
                getHeight() / 2 + height / 2,
                mPaint);
    }

    /**
     * add your number
     *
     * @param index
     */
    protected void add_number(int index) {

        if (mNumber + index < mMaxNumber) {
            mNumber += index;
        } else if (mNumber + index >= mMaxNumber) {
            mNumber = mMaxNumber;
        }

        Log.i("panzh", mMinNumber + "    _____   " + mNumber);

        // 通知更新視圖
        invalidate();
    }

    /**
     * subtract your number
     *
     * @param index
     */
    protected void subtract_number(int index) {
        if (mNumber - index > mMinNumber) {
            mNumber -= index;
        } else if (mNumber - index <= mNumber) {
            mNumber = mMinNumber;
        }
        Log.i("panzh", mMinNumber + "    _____   " + mNumber);

        invalidate();
    }

    @Override
    public void onClick(View v) {
        subtract_number(1);
    }

    private void startCount() {
        mCompositeDisposable.add(Observable.interval(0, 5, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(getObserver(1)));
    }

    private DisposableObserver  getObserver(final int id) {
        DisposableObserver disposableObserver = new DisposableObserver<Object>() {
            @Override
            public void onNext(Object o) {
                add_number(1);
            }

            @Override
            public void onComplete() {
                Log.d(id + TAG, "onComplete");
            }

            @Override
            public void onError(Throwable e) {
                Log.e(id + TAG, e.toString(), e);
            }
        };

        return disposableObserver;
    }

}
public class MainActivity extends AppCompatActivity {
    CountButton countButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        countButton = new CountButton(MainActivity.this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        countButton.mCompositeDisposable.clear();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 2014年。 初識(shí)趙雷是在大冰的書里。 “在我來看,他一個(gè)流浪歌手出身,經(jīng)過了那么強(qiáng)的市場(chǎng)驗(yàn)證,他唱的歌讓那么多在...
    我不是企鵝閱讀 437評(píng)論 0 2
  • 先帝亦言:‘吾周旋陳元方、鄭康成間,每見啟告,治亂之道悉矣,曾不及赦也?!魟⒕吧缸?,歲歲赦宥,何益于治乎?”及...
    牧開閱讀 751評(píng)論 0 0
  • 我總是想盡辦法可以拖延連載。 有一天突然想到一個(gè)好主意。我真的覺得可以。 事情是這樣的,在連載《核》的過程中,我一...
    赤米黑眼圈閱讀 393評(píng)論 0 0

友情鏈接更多精彩內(nèi)容