項(xiàng)目需要在啟動(dòng)頁(yè)加上倒計(jì)時(shí)的功能,所以自定義了一個(gè)倒計(jì)時(shí)的View,下面的是具體的分析
1糖赔、自定義View的基礎(chǔ)
一般情況下,自定義View可以有三種方式轩端,
第一種:就是繼承View或者ViewGroup來(lái)自己從頭開(kāi)始實(shí)現(xiàn)放典,
第二種:就是繼承系統(tǒng)已經(jīng)實(shí)現(xiàn)了特定功能的View或者ViewGroup,例如TextView基茵,ImageView奋构,LinearLayout等等,這樣做的原因和好處就是拱层,可以繼承部分功能弥臼,在此基礎(chǔ)上再進(jìn)行自己需要的擴(kuò)展
第三種:就是利用布局將一些View進(jìn)行特定的組合來(lái)組成一個(gè)復(fù)合的組件
2、倒計(jì)時(shí)View的具體實(shí)現(xiàn)
因?yàn)楸疚牡慕M件是使用第一種方式進(jìn)行實(shí)現(xiàn)的根灯,所以下面就是結(jié)合代碼對(duì)第一種的實(shí)現(xiàn)方式進(jìn)行分析径缅,代碼如下
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CountDownView">
<attr name="arc_circle_color" format="color"/>
<attr name="in_circle_color" format="color"/>
<attr name="txt_time_color" format="color"/>
</declare-styleable>
</resources>
上面的代碼是自定義的屬性,它放在values/attr.xml文件中烙肺,當(dāng)然這個(gè)attr.xml文件需要我們自己創(chuàng)建纳猪,它的主要目的就是使我們可以在xml文件中進(jìn)行屬性的設(shè)置,如果自己實(shí)現(xiàn)的自定義View中沒(méi)有自定義的屬性茬高,則這個(gè)可以忽略
public class CountDownView extends View {
//繪制內(nèi)圓的畫(huà)筆對(duì)象
private Paint mInCirclePaint = new Paint();
//繪制文字的畫(huà)筆對(duì)象
private Paint mTxtPaint = new Paint();
//繪制圓弧的畫(huà)筆對(duì)象
private Paint mArcPaint = new Paint();
//計(jì)時(shí)類(lèi)
private Timer mTimer = null;
//外部圓當(dāng)前繪制的弧度
private int currentAngle = 360;
//外部圓最終繪制的弧度
private int progress = 0;
//當(dāng)前的描述兆旬,這里默認(rèn)為4秒,不可修改
private int currentMillon = 4;
//外部圓的背景顏色
private int arcCircleColor;
//內(nèi)部圓的背景顏色
private int inCircleColor;
//文字的顏色
private int txtTimeColor;
..............
}
以上是需要用到的屬性怎栽,代碼中都有詳細(xì)的注釋了
public class CountDownView extends View {
.......
public CountDownView(Context context) {
this(context, null);
}
public CountDownView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CountDownView,
defStyleAttr, 0);
arcCircleColor = a.getColor(R.styleable.CountDownView_arc_circle_color, Color.RED);
inCircleColor = a.getColor(R.styleable.CountDownView_in_circle_color, Color.parseColor
("#FFB7B6B6"));
txtTimeColor = a.getColor(R.styleable.CountDownView_txt_time_color, Color.WHITE);
a.recycle();
init();
}
private void init() {
mTimer = new Timer();
}
......
}
接下來(lái)是三個(gè)構(gòu)造函數(shù),這里是最先被初始化的地方宿饱,所以在這里我們要把在attr.xml文件中的自定義屬性取出來(lái)熏瞄,并且設(shè)置默認(rèn)值
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = (int) ViewUtils.dp2px(50);
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
if (getLayoutParams().height == WindowManager.LayoutParams.WRAP_CONTENT) {
height = (int) ViewUtils.dp2px(50);
} else {
height = heightSize;
}
}
if (width <= height) {
setMeasuredDimension(width, width);
} else {
setMeasuredDimension(height, height);
}
}
接下來(lái)就是自定義View中比較重要的一個(gè)方法,這個(gè)方法的主要作用就是測(cè)量谬以,它的兩個(gè)傳入的參數(shù)分別是由父布局測(cè)量后傳遞下來(lái)的測(cè)量寬度和測(cè)量高度强饮,這個(gè)測(cè)量寬度包括了寬度的大小和測(cè)量模式,而測(cè)量高度也是一樣为黎。這個(gè)方法的主要作用就是測(cè)量View的大小邮丰,如果說(shuō)自定義View是畫(huà)畫(huà)的話(huà),那么這個(gè)方法就是先測(cè)量出一塊畫(huà)布的大小铭乾,以便我們后續(xù)在這個(gè)畫(huà)布上進(jìn)行作畫(huà)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//獲取屏幕的寬度
int width = getMeasuredWidth();
//獲取屏幕的高度
int height = getMeasuredHeight();
//繪制內(nèi)部的圓
mInCirclePaint.setStyle(Paint.Style.FILL);
mInCirclePaint.setAntiAlias(true);
mInCirclePaint.setColor(inCircleColor);
canvas.drawCircle(width / 2, height / 2, width / 2 - 10, mInCirclePaint);
//繪制文字
int mTxtSize = (int) ViewUtils.dp2px(14);
mTxtPaint.setTextSize(mTxtSize);
Rect mBound = new Rect();
mTxtPaint.getTextBounds(String.valueOf(currentMillon), 0, String.valueOf(currentMillon)
.length(), mBound);
mTxtPaint.setColor(txtTimeColor);
canvas.drawText(String.valueOf(currentMillon), width / 2 - mBound.width() / 2, height / 2
+ mBound.height() / 2, mTxtPaint);
// 繪制圓弧
mArcPaint.setStrokeWidth(10);
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setAntiAlias(true);
mArcPaint.setColor(arcCircleColor);
RectF rect = new RectF(10, 10, width - 10, height - 10);
canvas.drawArc(rect, -90, currentAngle, false, mArcPaint);
}
這個(gè)方法是自定義View中的另一個(gè)比較重要的方法剪廉,它的主要作用就是繪制具體的內(nèi)容,在這個(gè)方法里面炕檩,我們會(huì)持有畫(huà)布的對(duì)象斗蒋,因此我們可以利用系統(tǒng)提供的api來(lái)繪制出你想要的圖形,上面的代碼就繪制了一個(gè)圓,文字泉沾,圓弧捞蚂。那這一步就相當(dāng)于作畫(huà)中的在畫(huà)布上畫(huà)畫(huà)了。
3跷究、最后一步
經(jīng)過(guò)前面的代碼姓迅,我們可以繪制出倒計(jì)時(shí)View的大致的樣子,但是怎樣實(shí)現(xiàn)倒計(jì)時(shí)呢俊马,代碼如下:
/**
* 動(dòng)畫(huà)開(kāi)始
*/
public void start() {
mTimer.schedule(new TimerTask() {
@Override
public void run() {
postInvalidate();
if (currentAngle <= progress) {
//到這里队贱,自動(dòng)執(zhí)行的任務(wù)已經(jīng)結(jié)束,在這里我們可以定義回調(diào)接口來(lái)進(jìn)行特定的處理
mTimer.cancel();
} else {
currentAngle -= 5;
}
if (currentAngle % 90 == 0 && currentMillon > 0) {
currentMillon--;
}
}
}, 50, 50);
}
以上的代碼就是倒計(jì)時(shí)之行的關(guān)鍵潭袱,它主要是這樣的柱嫌,在onDraw方法中先在外部繪制出一個(gè)圓滿(mǎn)的圓形,但是繪制的弧度(currentAngle)是一個(gè)變量屯换,我們?cè)诙〞r(shí)任務(wù)中不斷將這個(gè)變量進(jìn)行自減编丘,并且進(jìn)行View的重繪(通過(guò)postInvalidate()方法),這樣就可以實(shí)現(xiàn)圓形圖案縮減的動(dòng)畫(huà)了彤悔。另外嘉抓,上面代碼中的秒數(shù)的計(jì)算只是我粗略的計(jì)算,并不算準(zhǔn)確晕窑,有興趣可以實(shí)現(xiàn)更佳精確的計(jì)算方法