date : 2019.10.15
author : lrcoder
Demo 介绍
-
Demo灵感
阴阳师手游中,体力是一个重要的资源。在游戏的设定中,每位阴阳师的体力上限为100,每隔三分钟恢复一点体力。当体力值自动增长至100后,停止增长;当体力消耗至0后,体力不会为负,而是禁止阴阳师进行别的消耗体力操作。
-
Demo功能
- 支持自定义背景颜色,默认为红色
- 支持自定义按钮形状, 默认为圆形
- 支持自定义数字颜色, 默认为白色
- 支持自定义数字大小, 默认为200dp
- 支持自定义数字最值, 默认最大为20, 最小为0
- 支持自定义初始值, 默认为20
- 支持提供数字增加、减少的方法
- 支持数字自动增加
-
Demo所用到的技术
- 自定义控件
- Android多线程(RxJava2)
功能代码和分析
-
用于用户自定义的xml配置文件
如果希望该自定义控件可以和系统控件相似,可以通过
android:XX="XX"
的形式对控件属性进行设置,那么便需要在values/attrs.xml
文件中添加一下内容:
有关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 | 浮点值 | mTypedArray.getFloat() |
fraction | 百分数 | mTypedArray.getFraction() |
flag | 位或运算 | ... ... |
其中,enum
和flag
给我的感觉就是键值对,我们可以通过其value
值的类型来对控件作出相应的控制,接下来我们就可以在布局中使用我们刚才定义的属性值:
- 画控件
自定义控件的继承关系:
- 继承已有控件进行自定义控件
- 继承布局文件进行自定义控件
- 继承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
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();
}
}