天天看點

Android自定義View:一個精緻的打鈎小動畫

這個簡單,直接使用

drawArc()

即可實作

  1. 繪制向圓心收縮的動畫

這個一開始的時候想用

drawArc()

加上設定畫筆的寬度

strokeWidth

來實作,不過改變的寬度是往外擴張的,是以這個想法果斷放棄。

之後,我的想法是這樣的,看下圖

Android自定義View:一個精緻的打鈎小動畫

我就打算先繪制一個黃色的背景,然後在這個圖層上面繪制一個白色的圓,半徑不斷的縮小,直至為0,這就反過來得到了一個向中心收縮的動畫,這可以叫逆轉思維吧,最近看的一本書裡面說到有時候反過來思考也許會有不一樣的效果。

  1. 顯示勾出來

關于這個√,我在網上搜了一波,也沒有明确的指明怎麼畫法才是标準的,是以這裡可以随意發揮,自己覺得好看就行。這裡直接可以使用

drawLine()

可以一步搞定。

  1. 最後是圓環放大再回彈的效果

放大回彈可以使用

drawArc()

,配合改變畫筆的寬度來實作即可

3.具體實作

3.1 确定進度圓環和鈎的位置

經過上面分析,無論是選中狀态還是未選中狀态,進度圓環和鈎的位置是不變的,是以我們先來确定圓環的位置和鈎的位置

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//設定圓圈的外切矩形,radius是圓的半徑,centerX,centerY是控件中心的坐标

mRectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);

//設定打鈎的幾個點坐标(具體坐标點的位置不用怎麼理會,自己定一個就好,沒有統一的标準)

//畫一個√,需要确定3個坐标點的位置

//是以這裡我先用一個float數組來記錄3個坐标點的位置,

//最後在onDraw()的時候使用canvas.drawLines(mPoints, mPaintTick)來畫出來

//其中這裡mPoint[0]~mPoint[3]是确定第一條線""的兩個坐标點位置

//mPoint[4]~mPoint[7]是确定第二條線"/"的兩個坐标點位置

mPoints[0] = centerX - tickRadius + tickRadiusOffset;

mPoints[1] = (float) centerY;

mPoints[2] = centerX - tickRadius / 2 + tickRadiusOffset;

mPoints[3] = centerY + tickRadius / 2;

mPoints[4] = centerX - tickRadius / 2 + tickRadiusOffset;

mPoints[5] = centerY + tickRadius / 2;

mPoints[6] = centerX + tickRadius * 2 / 4 + tickRadiusOffset;

mPoints[7] = centerY - tickRadius * 2 / 4;

}

3.2 定義變量,标記狀态

既然分選中狀态和未選中狀态,那個繪制過程中,就必須判斷目前究竟是繪制未選中的呢還是選中了的呢。

是以在這裡,我定義了一個變量

isChecked

//是否被點亮

private boolean isChecked = false;

//暴露外部接口,改變繪制狀态

public void setChecked(boolean checked) {

if (this.isCh 《Android學習筆記總結+最新移動架構視訊+大廠安卓面試真題+項目實戰源碼講義》無償開源 徽信搜尋公衆号【程式設計進階路】 ecked != checked) {

isChecked = checked;

reset();

}

}

3.3 繪制未選中狀态

繪制過程中那些畫筆就不詳細說了,一開始初始化畫筆最後繪制的時候調用即可

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (!isChecked) {

//繪制圓環,mRectF就是之前确定的外切矩形

//因為是靜态的,是以設定掃過的角度為360度

canvas.drawArc(mRectF, 90, 360, false, mPaintRing);

//根據之前定好的鈎的坐标位置,進行繪制

canvas.drawLines(mPoints, mPaintTick);

return;

}

}

3.4 繪制選中狀态

選中狀态是個動畫,是以我們這裡需要調用

postInvalidate()

不斷進行重繪,直到動畫執行完畢;另外,我這裡用計數器的方式來控制繪制的進度。

3.4.1 繪制圓環進度條

繪制進度圓環這裡,我們定義一個計數器

ringCounter

,峰值為360(也就是360度),每執行一次

onDraw()

方法,我們對

ringCounter

進行自加,進而模拟進度。

最後記得調用

postInvalidate()

進行重繪

//計數器

private int ringCounter = 0;

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (!isChecked) {

return;

}

//畫圓弧進度,每次繪制都自加12個機關,也就是圓弧又掃過了12度

//這裡的12個機關先寫死,後面我們可以做一個配置來實作自定義

ringCounter += 12;

if (ringCounter >= 360) {

ringCounter = 360;

}

canvas.drawArc(mRectF, 90, ringCounter, false, mPaintRing);

//強制重繪

postInvalidate();

}

這一步後效果圖如下

Android自定義View:一個精緻的打鈎小動畫

3.4.2 繪制向圓心收縮的動畫

圓心收縮的動畫在圓環進度達到100%的時候才進行,同理,也采用計數器

circleCounter

的方法來控制繪制的時間和速度

//計數器

private int circleCounter = 0;

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//在圓環進度達到100%的時候才開始繪制

if (ringCounter == 360) {

//先繪制背景的圓

mPaintCircle.setColor(checkBaseColor);

canvas.drawCircle(centerX, centerY, radius, mPaintCircle);

//然後在背景圓的圖層上,再繪制白色的圓(半徑不斷縮小)

//半徑不斷縮小,背景就不斷露出來,達到向中心收縮的效果

mPaintCircle.setColor(checkTickColor);

//收縮的機關先試着設定為6,後面可以進行自己自定義

circleCounter += 6;

canvas.drawCircle(centerX, centerY, radius - circleCounter, mPaintCircle);

}

//必須重繪

postInvalidate();

}

這一步後效果圖如下

Android自定義View:一個精緻的打鈎小動畫

3.4.3 繪制鈎

當白色的圓半徑收縮到0後,就該繪制打鈎了。

繪制打鈎,這裡問題不大,因為在

onMeasure()

中已經将鈎的三個坐标點已經計算出來了,直接使用

drawLine()

即可畫出來。

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

canvas.drawCircle(centerX, centerY, radius - circleCounter, mPaintCircle);

//當白色的圓半徑收縮到0後,

//也就是計數器circleCounter大于背景圓的半徑的時候,就該将鈎√顯示出來了

//這裡加40是為了加一個延遲時間,不那麼倉促的将鈎顯示出來

if (circleCounter >= radius + 40) {

//顯示打鈎(外加一個透明的漸變)

alphaCount += 20;

if (alphaCount >= 255) alphaCount = 255;

mPaintTick.setAlpha(alphaCount);

//最後就将之前在onMeasure中計算好的坐标傳進去,繪制鈎出來

canvas.drawLines(mPoints, mPaintTick);

}

postInvalidate();

}

這一步後效果圖如下

Android自定義View:一個精緻的打鈎小動畫

3.4.4 繪制放大再回彈的效果

放大再回彈的效果,開始的時機應該也是收縮動畫結束後開始,也就是說跟打鈎的動畫同時進行

因為這裡要放大并且回彈,是以這裡的計數器我設定成一個不為0的數值,先設定成45(随意,這不是标準),然後沒重繪一次,自減4個機關。

最後畫筆的寬度是關鍵的地方,畫筆的寬度根據

scaleCounter

的正負來決定是加還是減

//計數器

private int scaleCounter = 45;

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (circleCounter >= radius + 40) {

//顯示打鈎

//顯示放大并回彈的效果

scaleCounter -= 4;

if (scaleCounter <= -45) {

scaleCounter = -45;

}

//放大回彈,主要看畫筆的寬度

float strokeWith = mPaintRing.getStrokeWidth() +

(scaleCounter > 0 ? dp2px(mContext, 1) : -dp2px(mContext, 1));

mPaintRing.setStrokeWidth(strokeWith);

canvas.drawArc(mRectF, 90, 360, false, mPaintRing);

}

//動畫執行完畢,就補在需要重繪了

if (scaleCounter != -45) {

postInvalidate();

}

}

完成最後一步的最終效果圖

Android自定義View:一個精緻的打鈎小動畫

3.5 暴露外部接口

為了靈活的可以控制繪制的狀态,我們可以暴露一個接口給外部設定是否選中

public void setChecked(boolean checked) {

if (this.isChecked != checked) {

isChecked = checked;

reset();

}

}

private void reset() {

//畫筆重置

//計數器重置

ringCounter = 0;

circleCounter = 0;

scaleCounter = 45;

alphaCount = 0;

invalidate();

}

3.6 添加點選事件

控件到這裡已經基本做好了,但還不是特别的完善。

想想

checkbox

,它不需要暴露外部接口也能通過點選控件來實作選中還是取消選中,是以接下來要實作的就是為控件添加點選事件

先定義一個接口

OnCheckedChangeListener

,實作監聽此控件的監聽事件

private OnCheckedChangeListener mOnCheckedChangeListener;

public interface OnCheckedChangeListener {

void onCheckedChanged(TickView tickView, boolean isCheck);

}

public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {

this.mOnCheckedChangeListener = listener;

}

接下來,初始化控件的點選事件

/**

繼續閱讀