專案需要在啟動頁加上倒數計時的功能,所以自定義了一個倒數計時的View,下面的是具體的分析
因為不會製作GIF,但是隻貼幾張圖的話也體現不出什麼效果,所以寫了一個例子,大家如果想看效果,可以下載demo原始碼執行看看
github.com/chenTovae/W…
1、自定義View的基礎
一般情況下,自定義View可以有三種方式,
第一種:就是繼承View或者ViewGroup來自己從頭開始實現,
第二種:就是繼承系統已經實現了特定功能的View或者ViewGroup,例如TextView,ImageView,LinearLayout等等,這樣做的原因和好處就是,可以繼承部分功能,在此基礎上再進行自己需要的擴充套件
第三種:就是利用佈局將一些View進行特定的組合來組成一個複合的元件
2、倒數計時View的具體實現
因為本文的元件是使用第一種方式進行實現的,所以下面就是結合程式碼對第一種的實現方式進行分析,程式碼如下
<?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檔案中,當然這個attr.xml檔案需要我們自己建立,它的主要目的就是使我們可以在xml檔案中進行屬性的設定,如果自己實現的自定義View中沒有自定義的屬性,則這個可以忽略
public class CountDownView extends View {
//繪製內圓的畫筆物件
private Paint mInCirclePaint = new Paint();
//繪製文字的畫筆物件
private Paint mTxtPaint = new Paint();
//繪製圓弧的畫筆物件
private Paint mArcPaint = new Paint();
//計時類
private Timer mTimer = null;
//外部圓當前繪製的弧度
private int currentAngle = 360;
//外部圓最終繪製的弧度
private int progress = 0;
//當前的描述,這裡預設為4秒,不可修改
private int currentMillon = 4;
//外部圓的背景顏色
private int arcCircleColor;
//內部圓的背景顏色
private int inCircleColor;
//文字的顏色
private int txtTimeColor;
..............
}複製程式碼
以上是需要用到的屬性,程式碼中都有詳細的註釋了
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();
}
......
}複製程式碼
接下來是三個建構函式,這裡是最先被初始化的地方,所以在這裡我們要把在attr.xml檔案中的自定義屬性取出來,並且設定預設值
@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);
}
}複製程式碼
接下來就是自定義View中比較重要的一個方法,這個方法的主要作用就是測量,它的兩個傳入的引數分別是由父佈局測量後傳遞下來的測量寬度和測量高度,這個測量寬度包括了寬度的大小和測量模式,而測量高度也是一樣。這個方法的主要作用就是測量View的大小,如果說自定義View是畫畫的話,那麼這個方法就是先測量出一塊畫布的大小,以便我們後續在這個畫布上進行作畫
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//獲取螢幕的寬度
int width = getMeasuredWidth();
//獲取螢幕的高度
int height = getMeasuredHeight();
//繪製內部的圓
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);
}複製程式碼
這個方法是自定義View中的另一個比較重要的方法,它的主要作用就是繪製具體的內容,在這個方法裡面,我們會持有畫布的物件,因此我們可以利用系統提供的api來繪製出你想要的圖形,上面的程式碼就繪製了一個圓,文字,圓弧。那這一步就相當於作畫中的在畫布上畫畫了。
##3、最後一步
經過前面的程式碼,我們可以繪製出倒數計時View的大致的樣子,但是怎樣實現倒數計時呢,程式碼如下:
/**
* 動畫開始
*/
public void start() {
mTimer.schedule(new TimerTask() {
@Override
public void run() {
postInvalidate();
if (currentAngle <= progress) {
//到這裡,自動執行的任務已經結束,在這裡我們可以定義回撥介面來進行特定的處理
mTimer.cancel();
} else {
currentAngle -= 5;
}
if (currentAngle % 90 == 0 && currentMillon > 0) {
currentMillon--;
}
}
}, 50, 50);
}複製程式碼
以上的程式碼就是倒數計時之行的關鍵,它主要是這樣的,在onDraw方法中先在外部繪製出一個圓滿的圓形,但是繪製的弧度(currentAngle)是一個變數,我們在定時任務中不斷將這個變數進行自減,並且進行View的重繪(通過postInvalidate()方法),這樣就可以實現圓形圖案縮減的動畫了。另外,上面程式碼中的秒數的計算只是我粗略的計算,並不算準確,有興趣可以實現更佳精確的計算方法