国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android自定義View app更新動畫詳解

2019-10-21 21:46:17
字體:
來源:轉載
供稿:網友

為了做一個有溫度的IT男,我決定在以后的文章中給大家分享一些看到的,聽到的一些東西,如果你不喜歡請留言讓我知道,如果你喜歡請點個贊。你也可留言寫下自己想分享的東西,溫暖你我他。這次分享的是一首歌,毛不易的《消愁》,分享這首歌主要是這首歌的歌詞,借用薛之謙的評價:“我是研究歌詞的人,我研究了十幾年,但是你寫到我想給你跪!”,下面貼部分歌詞供大家欣賞

一杯敬朝陽,一杯敬月光

喚醒我的向往,溫柔了寒窗

于是可以不回頭的逆風飛翔

不怕心頭有雨,眼底有霜

一杯敬故鄉(xiāng),一杯敬遠方

守著我的善良,催著我成長

所以南北的路從此不再漫長

靈魂不再無處安放

好了,言歸正傳,本篇文章是實現(xiàn)項目中的更新功能,效果如下

Android,自定義View,app更新動畫

觀察動畫,可以分為幾個階段:

  • 初始化階段 顯示立即升級按鈕,在點擊立即升級按鈕后,執(zhí)行放大再縮小至消失動畫
  • 準備階段 進度條背景從中間向兩端擴散,然后進度提示圖片顯示,進度提示文字顯示0%
  • 更新階段 進度更新時,進度提示圖片和文字旋轉向前移動,如果一定時間內進度沒更新的話,進度提示圖片和文字要置回水平狀態(tài)
  • 成功階段,進度提示圖片縮放消失,進度條背景從兩端向中間縮小至消失
  • 安裝階段 馬上安裝圖片放大顯示

1.首選看初始化階段,我們要判斷用戶是否點擊了立即升級按鈕,我們通過監(jiān)聽onTouchEvent事件判斷手指是否落在可點擊區(qū)域

//如果點擊生效,執(zhí)行動畫

if (rectClickRange.contains(event.getX(), event.getY()))
startBtnDisappear();//立即更新按鈕消失動畫

其中rectClickRange是我們定義的可點擊區(qū)域,也就是立即更新圖片的顯示區(qū)域

rectClickRange = new RectF(getWidth() / 2 - startDrawable.getWidth() / 2, getHeight() / 2 - startDrawable.getHeight() / 2, getWidth() / 2 + startDrawable.getWidth() / 2, getHeight() / 2 + startDrawable.getHeight() / 2);//startDrawable是立即更新圖片

點擊生效后我們執(zhí)行立即更新按鈕消失動畫,代碼如下

/** * 點擊立即升級的時候,立即升級按鈕執(zhí)行消失動畫 * 動畫效果是按鈕放大一點之后縮小至消失 * 根據(jù)效果選擇插值器AnticipateInterpolator(開始的時候向后然后向前甩) * 將bitmapscale設置到立即升級圖片上 * 動畫結束后狀態(tài)更新為準備狀態(tài) */private void startBtnDisappear() { initStateData(); ValueAnimator va = ValueAnimator.ofInt(0, 1); va.setInterpolator(new AnticipateInterpolator()); va.setDuration(800); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  @Override  public void onAnimationUpdate(ValueAnimator animation) {   bitmapScale = 1 - animation.getAnimatedFraction();   invalidate();  } }); va.addListener(new MyAnimationListener() {  @Override  public void onAnimationEnd(Animator animation) {   cancleValueAnimator(va_List);   state = PREPARE;   toPrepare();  } }); va.start(); va_List.add(va);}
然后在onDraw里面繪制立即升級按鈕動畫,代碼如下:    matrix.reset();    matrix.setScale(bitmapScale, bitmapScale);//縮放圖片    matrix.preTranslate(0, 0);    matrix.postTranslate(width / 2 - startDrawable.getWidth() / 2 * bitmapScale, height / 2 - startDrawable.getHeight() / 2 * bitmapScale);//不斷的改變縮放的中心點    canvas.drawBitmap(startDrawable, matrix, bitmapPaint);

2.接著我們看一下準備階段,我們通過畫path,并不斷的改變path的起點和終點達到所需要的動畫效果,代碼如下:

/** * PREPARE狀態(tài) * 進度條從中間向兩端擴散 * 具體做法是不斷改變path的起點和終點坐標 * 動畫結束的時候開始下載更新 */private void toPrepare() { final ValueAnimator va = ValueAnimator.ofFloat(0, width / 2 - pbPaint.getStrokeWidth() * 3 - pbProgerssDrawable.getWidth()); va.setInterpolator(new LinearInterpolator()); va.setDuration(200); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  @Override  public void onAnimationUpdate(ValueAnimator animation) {   float value = (Float) animation.getAnimatedValue();   startX = (int) (width / 2 - value);   endX = (int) (width / 2 + value);   if (animation.getAnimatedFraction() == 1) prepareDone = true;   invalidate();  } }); va.addListener(new MyAnimationListener() {  @Override  public void onAnimationEnd(Animator animation) {   if (startDownLoadListener != null && !isSetListener) {    isSetListener = true;    postDelayed(new Runnable() {     @Override     public void run() {      state = UPDATEING;           startDownLoadListener.downLoad();//動畫結束,通知界面開始下載apk      text = progress * 100 / max + "%";     }    }, 200);   }  } }); va.start(); va_List.add(va);}
具體的繪制代碼如下    pbPath.reset();    pbPath.moveTo(startX, height / 2);    pbPath.lineTo(endX, height / 2);    canvas.drawPath(pbPath, pbPaint);//繪制path    //進度條完全顯示后,畫進度提示圖片和文字    if (prepareDone) {     canvas.drawBitmap(pbProgerssDrawable, startX - pbProgerssDrawable.getWidth() / 2, height / 2 - pbProgerssDrawable.getHeight() - pbPaint.getStrokeWidth(), bitmapPaint);     String text = "0%";     textPaint.getTextBounds(text.toCharArray(), 0, text.toCharArray().length, textRect);     canvas.drawText(text, startX - textRect.right / 2, height / 2 - pbProgerssDrawable.getHeight() / 2 - pbPaint.getStrokeWidth() + textRect.bottom, textPaint);    }

3.這個時候界面就開始下載apk(代碼不貼了),然后通知view來更新進度,更新的動畫是圖片和文字旋轉向前移動(我們的做法是將畫布旋轉),如果一定時間進度沒有變化,更新的圖片和文字置回正常狀態(tài)(我們通過啟動線程不斷的將畫布旋轉回來并更新view,如果這個階段進度有更新的話,我們把線程remove掉),繪制代碼如下

   pbPath.reset();   pbPath.moveTo(startX, height / 2);   pbPath.lineTo(endX, height / 2);   pm.setPath(pbPath, false);   //不斷截取進度到pbPathSec并繪制   if (progressOffsetX >= pm.getLength()) {    pm.getSegment(0, pm.getLength(), pbPathSec, true);    pm.getPosTan(pm.getLength(), POS, null);   } else {    pm.getSegment(0, progressOffsetX, pbPathSec, true);    pm.getPosTan(progressOffsetX, POS, null);   }   matrix.reset();   matrix.setTranslate(POS[0] - pbProgerssDrawable.getWidth() / 2, POS[1] - pbProgerssDrawable.getHeight() - pbPaint.getStrokeWidth());   canvas.drawPath(pbPath, pbPaint);   canvas.drawPath(pbPathSec, pbUpdatePaint);   canvas.save();   //如果進度沒有到達100%,并且進度在更新的時候,畫布旋轉,然后畫進度提示圖片和文字   if (progressOffsetX < pm.getLength() && !isRotate) {    canvas.rotate(-15, POS[0], POS[1] - pbPaint.getStrokeWidth() / 2);   }   canvas.drawBitmap(pbProgerssDrawable, matrix, bitmapPaint);   if (progressOffsetX >= pm.getLength())    progressOffsetX = pm.getLength();   text = (int) (progressOffsetX * 100 / pm.getLength()) + "%";   textPaint.getTextBounds(text.toCharArray(), 0, text.toCharArray().length, textRect);   canvas.drawText(text, progressOffsetX + startX - textRect.right / 2, height / 2 - pbProgerssDrawable.getHeight() / 2 - pbPaint.getStrokeWidth() + textRect.bottom, textPaint);   //我們啟動一個線程,如果300毫秒進度沒有更新,將畫布旋轉回來畫進度提示圖片和文字   if (progressOffsetX < pm.getLength()) postDelayed(rotateRunnable, 300);   else toSucc();   canvas.restore();
其中rotateRunnable的代碼如下 //每隔一段時間刷新界面,如果進度沒有更新,將畫布旋轉回來 private Runnable rotateRunnable = new Runnable() {  @Override  public void run() {   isRotate = true;   invalidate();  } };

4.當進度達到100%的時候,我們將進度提示圖片縮放至消失,并且進度背景執(zhí)行兩端向中間縮小動畫,也是改變path的起始點,代碼如下

 //下載進度達到100時,進度提示圖片進行縮放 private void toSuccBitmapScale() {  cancleValueAnimator(va_List);  ValueAnimator va = ValueAnimator.ofFloat(1, 0);  va.setInterpolator(new AccelerateInterpolator());  va.setDuration(100);  va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {   @Override   public void onAnimationUpdate(ValueAnimator animation) {    bitmapScale = (Float) animation.getAnimatedValue();    state = SUCCESS;    invalidate();   }  });  va.start();  va.addListener(new MyAnimationListener() {   @Override   public void onAnimationEnd(Animator animation) {    toSuccPathAnim();   }  });  va_List.add(va); } //成功后進度條縮放動畫 private void toSuccPathAnim() {  cancleValueAnimator(va_List);  ValueAnimator va = ValueAnimator.ofInt(0, (endX - startX) / 2);  va.setInterpolator(new AccelerateInterpolator());  va.setDuration(300);  va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {   @Override   public void onAnimationUpdate(ValueAnimator animation) {    transx = (int) animation.getAnimatedValue();    state = SUCCESS;    invalidate();   }  });  va.start();  va.addListener(new MyAnimationListener() {   @Override   public void onAnimationEnd(Animator animation) {    toInstall();   }  });  va_List.add(va); }

繪制代碼如下

   pbPath.reset();   pbPath.moveTo(startX + transx, height / 2);//不斷的改變起點   pbPath.lineTo(endX - transx, height / 2);//改變終點   pm.setPath(pbPath, false);   pm.getSegment(0, (endX - startX), pbPathSec, true);   pm.getPosTan(endX - startX, POS, null);   matrix.reset();   matrix.preTranslate(POS[0] - pbProgerssDrawable.getWidth() / 2, POS[1] - pbProgerssDrawable.getHeight() - pbPaint.getStrokeWidth());   matrix.postScale(bitmapScale, bitmapScale, POS[0], POS[1] - pbPaint.getStrokeWidth());   canvas.drawPath(pbPath, pbUpdatePaint);//path縮放動畫   canvas.drawBitmap(pbProgerssDrawable, matrix, bitmapPaint);//bitmap縮放動畫

5.最后就是顯示馬上安裝圖片動畫了,一個簡單的縮放

 //顯示馬上安裝圖片動畫 private void toInstall() {  cancleValueAnimator(va_List);  ValueAnimator va = ValueAnimator.ofInt(0, 1);  va.setInterpolator(new LinearInterpolator());  va.setDuration(400);  va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {   @Override   public void onAnimationUpdate(ValueAnimator animation) {    bitmapScale = animation.getAnimatedFraction();    state = INSTALL;    invalidate();   }  });  va.start();  va_List.add(va); }  //繪制代碼如下    matrix.reset();    matrix.setScale(bitmapScale, bitmapScale);    matrix.preTranslate(0, 0);    matrix.postTranslate(width / 2 - succDrawable.getWidth() / 2 * bitmapScale, height / 2 - succDrawable.getHeight() / 2 * bitmapScale);    canvas.drawBitmap(succDrawable, matrix, bitmapPaint);

回過頭來看看,其實當我們把動畫不斷的分解之后,發(fā)現(xiàn)其實每個動畫并沒有那么難,我們這里用到的有path繪制及截取,getPosTan(獲取路徑上某點的坐標及其切線的坐標),利用Matrix做動畫,使用屬性動畫ValueAnimator。本篇還有好多功能沒有實現(xiàn),比如下載失敗動畫,失敗后恢復至初始化動畫,不過任何輪子都不一定能完全適合你,學習到知識之后自己造一個適合自己的才是最重要。

github地址:https://github.com/MrAllRight/BezierView


注:相關教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 黑河市| 德江县| 杨浦区| 白朗县| 锦州市| 巴马| 德保县| 纳雍县| 德兴市| 堆龙德庆县| 罗定市| 淄博市| 清远市| 山东省| 嘉善县| 五原县| 屏边| 横山县| 东乌| 邢台市| 盘锦市| 云南省| 南昌县| 桂阳县| 广宗县| 望奎县| 双桥区| 平乡县| 襄垣县| 敦化市| 元阳县| 中超| 庄河市| 阿尔山市| 信宜市| 泗阳县| 阿克| 滦南县| 收藏| 休宁县| 香格里拉县|