一个简单的自定义按钮


date : 2019.10.15
author : lrcoder


Demo 介绍

  • Demo灵感

    阴阳师手游中,体力是一个重要的资源。在游戏的设定中,每位阴阳师的体力上限为100,每隔三分钟恢复一点体力。当体力值自动增长至100后,停止增长;当体力消耗至0后,体力不会为负,而是禁止阴阳师进行别的消耗体力操作。

  • Demo功能

    1. 支持自定义背景颜色,默认为红色
    2. 支持自定义按钮形状, 默认为圆形
    3. 支持自定义数字颜色, 默认为白色
    4. 支持自定义数字大小, 默认为200dp
    5. 支持自定义数字最值, 默认最大为20, 最小为0
    6. 支持自定义初始值, 默认为20
    7. 支持提供数字增加、减少的方法
    8. 支持数字自动增加
  • Demo所用到的技术

    1. 自定义控件
    2. Android多线程(RxJava2)

功能代码和分析

  • 用于用户自定义的xml配置文件

    如果希望该自定义控件可以和系统控件相似,可以通过android:XX="XX"的形式对控件属性进行设置,那么便需要在values/attrs.xml文件中添加一下内容:



    
    
        
        
        
        
            
            
        
        
        
        
        
        
        
        
        
        
        
    

有关declare-styleableattr就没什么好说的,主要来看一下format的值和含义:

format 含义 获取方式
reference 资源ID,类似于@strings/...的资源文件 mTypedArray.getResourceId() ... ...
color 颜色值 mTypedArray.getColor()
dimension 尺寸值 mTypedArray.getDimension()
integer 整型值 mTypedArray.getInt()
string 字符串 mTypedArray.getString()
boolean 布尔值 mTypedArray.getBoolean()
enum 枚举值
float 浮点值 mTypedArray.getFloat()
fraction 百分数 mTypedArray.getFraction()
flag 位或运算 ... ...

其中,enumflag给我的感觉就是键值对,我们可以通过其value值的类型来对控件作出相应的控制,接下来我们就可以在布局中使用我们刚才定义的属性值:

   
  • 画控件

自定义控件的继承关系:

  1. 继承已有控件进行自定义控件
  2. 继承布局文件进行自定义控件
  3. 继承View类来实现自定义控件

创建一个CountButton类,这个就是我们自定义的控件名称,他继承自View,下面先上码,具体的解释放在代码的注释中

/**
 * @ProjectName: CountButtonView
 * @Package: org.code.demo.countbuttonview
 * @ClassName: CountButton
 * @Description: 1. 自定义背景颜色,默认为红色
 *               2. 自定义按钮形状, 默认为圆形
 *               3. 自定义数字颜色, 默认为白色
 *               4. 自定义数字大小, 默认为200dp
 *               5. 自定义数字最值, 默认最大为20, 最小为0
 *               6. 自定义初始值, 默认为20
 *               7. 提供数字增加、减少的方法
 * @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);
        // 设置画笔背景 (1. 自定义背景颜色,默认为红色)
        mPaint.setColor(mBackgroundColor);
        // 确定绘制形状 (2. 自定义按钮形状, 默认为圆形)
        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 同时 Log.i 提示
                canvas.drawCircle(
                        getWidth() / 2,
                        getHeight() / 2,
                        getWidth() / 2,
                        mPaint);
                Log.e(TAG, "error settings!!!");
                break;
        }
        // 确定数字颜色 (3. 自定义数字颜色, 默认为白色)
        mPaint.setColor(mTextColor);
        // 确定数字大小 (4. 自定义数字大小, 默认为200dp)
        mPaint.setTextSize(mTextSize);
        // 确认数字初始值
        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() {
            @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();
    }
}

你可能感兴趣的:(一个简单的自定义按钮)