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功能
- 支持自定義背景顏色,默認(rèn)為紅色
- 支持自定義按鈕形狀, 默認(rèn)為圓形
- 支持自定義數(shù)字顏色, 默認(rèn)為白色
- 支持自定義數(shù)字大小, 默認(rèn)為200dp
- 支持自定義數(shù)字最值, 默認(rèn)最大為20, 最小為0
- 支持自定義初始值, 默認(rèn)為20
- 支持提供數(shù)字增加、減少的方法
- 支持?jǐn)?shù)字自動(dòng)增加
-
Demo所用到的技術(shù)
- 自定義控件
- 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)算 | ... ... |
其中,enum和flag給我的感覺就是鍵值對(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)系:
- 繼承已有控件進(jìn)行自定義控件
- 繼承布局文件進(jìn)行自定義控件
- 繼承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();
}
}