Android中怎么通過(guò)自定義View實(shí)現(xiàn)打鉤動(dòng)畫功能

Android中怎么通過(guò)自定義View實(shí)現(xiàn)打鉤動(dòng)畫功能,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

公司主營(yíng)業(yè)務(wù):成都網(wǎng)站制作、網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)推出華坪免費(fèi)做網(wǎng)站回饋大家。

//計(jì)數(shù)器
private int ringCounter = 0;

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  ...
  return;
 }
 //畫圓弧進(jìn)度,每次繪制都自加12個(gè)單位,也就是圓弧又掃過(guò)了12度
 //這里的12個(gè)單位先寫死,后面我們可以做一個(gè)配置來(lái)實(shí)現(xiàn)自定義
 ringCounter += 12;
 if (ringCounter >= 360) {
  ringCounter = 360;
 }
 canvas.drawArc(mRectF, 90, ringCounter, false, mPaintRing);
 ...
 //強(qiáng)制重繪
 postInvalidate();
}

這里,我們定義了一個(gè)計(jì)數(shù)器ringCounter, 當(dāng)繪制的時(shí)候,是根據(jù)12個(gè)單位進(jìn)行自增到達(dá)360,從而模擬進(jìn)度的變化。

仔細(xì)想想

通過(guò)改變自增的單位來(lái)控制動(dòng)畫速度的變化,這很難調(diào)整得使自己滿意,此時(shí)我們可以想到,使動(dòng)畫速度執(zhí)行快慢的根本就是控制時(shí)間啊,如果可以用時(shí)間來(lái)控制動(dòng)畫速度那得方便多了動(dòng)畫分為4步執(zhí)行,如果每一步動(dòng)畫都用手寫計(jì)數(shù)器來(lái)實(shí)現(xiàn),那得定義4個(gè)成員變量或者更多,太多成員變量只會(huì)讓代碼更加混亂如果動(dòng)畫要加上插值器,那手寫的計(jì)數(shù)器根本無(wú)法滿足看到上面的分析,我無(wú)法接受了

3. 改改改

那么怎么去改善上面所說(shuō)的問(wèn)題呢,答案就是用自定義的屬性動(dòng)畫來(lái)解決了,所以這篇文章主要的講的地方就是用屬性動(dòng)畫來(lái)替換手寫的計(jì)數(shù)器,盡可能的保證代碼邏輯的清晰,特別是onDraw()方法中的代碼。

使用屬性動(dòng)畫的一個(gè)好處就是,給定數(shù)值的范圍,它會(huì)幫你生成一堆你想要的數(shù)值,配合插值器還要意想不到的效果呢,下一面就一步一步針對(duì)動(dòng)畫執(zhí)行的部分進(jìn)行重構(gòu)

3.1 繪制圓環(huán)進(jìn)度條

首先,使用自定義的ObjectAnimator來(lái)模擬進(jìn)度

//ringProgress是自定義的屬性名稱,生成數(shù)值的范圍是0 - 360,就是一個(gè)圓的角度
ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
//定義動(dòng)畫執(zhí)行的時(shí)間,很好的替代之前使用自增的單位來(lái)控制動(dòng)畫執(zhí)行的速度
mRingAnimator.setDuration(mRingAnimatorDuration);
//暫時(shí)不需要插值器
mRingAnimator.setInterpolator(null);

自定義屬性動(dòng)畫,還需要配置相應(yīng)的settergetter,因?yàn)樵趧?dòng)畫執(zhí)行的時(shí)候,會(huì)找相應(yīng)的setter去改變相應(yīng)的值。

private int getRingProgress() {
 return ringProgress;
}
private void setRingProgress(int ringProgress) {
 //動(dòng)畫執(zhí)行的時(shí)候,會(huì)調(diào)用setter
 //這里我們可以將動(dòng)畫生成的數(shù)值記錄下來(lái),用變量存起來(lái),在ondraw的時(shí)候用
 this.ringProgress = ringProgress;
 //記得重繪
 postInvalidate();
}

最后,在onDraw()中畫圖

//畫圓弧進(jìn)度canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);

3.2 繪制向圓心收縮的動(dòng)畫

同理,也是造一個(gè)屬性動(dòng)畫

//這里自定義的屬性是圓收縮的半徑
ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
//加一個(gè)減速的插值器
mCircleAnimator.setInterpolator(new DecelerateInterpolator());
mCircleAnimator.setDuration(mCircleAnimatorDuration);

setter/getter也是類似就不說(shuō)了

最后onDraw()中繪制

//畫背景
mPaintCircle.setColor(checkBaseColor);
canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
//當(dāng)進(jìn)度圓環(huán)繪制好了,就畫收縮的圓
if (ringProgress == 360) {
 mPaintCircle.setColor(checkTickColor);
 canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
}

3.3 繪制鉤和放大再回彈的效果

這是兩個(gè)獨(dú)立的效果,這里同時(shí)執(zhí)行,我就合在一起說(shuō)了

首先也是定義屬性動(dòng)畫

//勾出來(lái)的透明漸變
ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
mAlphaAnimator.setDuration(200);
//最后的放大再回彈的動(dòng)畫,改變畫筆的寬度來(lái)實(shí)現(xiàn)
//而畫筆的寬度,則是的變化范圍是
//首先從初始化寬度開(kāi)始,再到初始化寬度的n倍,最后又回到初始化的寬度
ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
mScaleAnimator.setInterpolator(null);
mScaleAnimator.setDuration(mScaleAnimatorDuration);
//打鉤和放大回彈的動(dòng)畫一起執(zhí)行
AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);

getter/setter

private int getTickAlpha() {
 return 0;
}
private void setTickAlpha(int tickAlpha) {
 //設(shè)置透明度,可以不用變量來(lái)保存了
 //直接將透明度的值設(shè)置到畫筆里面即可
 mPaintTick.setAlpha(tickAlpha);
 postInvalidate();
}
private float getRingStrokeWidth() {
 return mPaintRing.getStrokeWidth();
}
private void setRingStrokeWidth(float strokeWidth) {
 //設(shè)置畫筆寬度,可以不用變量來(lái)保存了
 //直接將畫筆寬度設(shè)置到畫筆里面即可
 mPaintRing.setStrokeWidth(strokeWidth);
 postInvalidate();
}

最后,同理在onDraw()中繪制即可

if (circleRadius == 0) {
 canvas.drawLines(mPoints, mPaintTick);
 canvas.drawArc(mRectF, 0, 360, false, mPaintRing);
}

3.4 依次執(zhí)行動(dòng)畫

執(zhí)行多個(gè)動(dòng)畫,可以用到AnimatorSet,其中playTogether()是一起執(zhí)行,playSequentially()是一個(gè)挨著一個(gè),step by step執(zhí)行。

mFinalAnimatorSet = new AnimatorSet();
mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet);

最后在onDraw()中執(zhí)行動(dòng)畫

//這里定義了一個(gè)標(biāo)識(shí)符,用于告訴程序,動(dòng)畫每次只能執(zhí)行一次
if (!isAnimationRunning) {
 isAnimationRunning = true;
 //執(zhí)行動(dòng)畫
 mFinalAnimatorSet.start();
}

3.5 每個(gè)方法最好能有單一的職責(zé)

如果將定義屬性動(dòng)畫的方法放在onDraw()中,我個(gè)人感覺(jué)很亂,并且再仔細(xì)看看,這幾個(gè)屬性動(dòng)畫是不需要?jiǎng)討B(tài)變化的,為什么不抽出來(lái)在一開(kāi)始的時(shí)候就初始化呢?

so,我們將定義屬性動(dòng)畫的代碼抽出來(lái),并且放到構(gòu)造函數(shù)中初始化

public TickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 ...
 initAnimatorCounter();
}
/**
 * 用ObjectAnimator初始化一些計(jì)數(shù)器
 */
private void initAnimatorCounter() {
 //圓環(huán)進(jìn)度
 ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
 ...
 //收縮動(dòng)畫
 ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
 ...
 //勾出來(lái)的透明漸變
 ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
 ...
 //最后的放大再回彈的動(dòng)畫,改變畫筆的寬度來(lái)實(shí)現(xiàn)
 ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
 ...
 //打鉤和放大回彈的動(dòng)畫一起執(zhí)行
 AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
 mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);

 mFinalAnimatorSet = new AnimatorSet();
 mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet);
}

最后,onDraw()方法中,只負(fù)責(zé)簡(jiǎn)單的繪制,什么都不管

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  canvas.drawArc(mRectF, 90, 360, false, mPaintRing);
  canvas.drawLines(mPoints, mPaintTick);
  return;
 }
 //畫圓弧進(jìn)度
 canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);
 //畫黃色的背景
 mPaintCircle.setColor(checkBaseColor);
 canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
 //畫收縮的白色圓
 if (ringProgress == 360) {
  mPaintCircle.setColor(checkTickColor);
  canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
 }
 //畫勾,以及放大收縮的動(dòng)畫
 if (circleRadius == 0) {
  canvas.drawLines(mPoints, mPaintTick);
  canvas.drawArc(mRectF, 0, 360, false, mPaintRing);
 }
 //ObjectAnimator動(dòng)畫替換計(jì)數(shù)器
 if (!isAnimationRunning) {
  isAnimationRunning = true;
  mFinalAnimatorSet.start();
 }
}

看完上述內(nèi)容,你們掌握Android中怎么通過(guò)自定義View實(shí)現(xiàn)打鉤動(dòng)畫功能的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

分享文章:Android中怎么通過(guò)自定義View實(shí)現(xiàn)打鉤動(dòng)畫功能
標(biāo)題路徑:http://muchs.cn/article48/pidsep.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、虛擬主機(jī)、網(wǎng)站內(nèi)鏈、網(wǎng)站排名、品牌網(wǎng)站設(shè)計(jì)網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁(yè)設(shè)計(jì)公司