<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android仿豆瓣笑臉進度加載

          共 16069字,需瀏覽 33分鐘

           ·

          2021-05-01 21:41

          最近看到豆瓣的笑臉loading很有意思,看一張效果圖:



          下面分析一下如何實現(xiàn)這樣的效果:


          1、默認狀態(tài)是一張笑臉的狀態(tài)(一個嘴巴,兩個眼睛,默認狀態(tài))

          2、開始旋轉(zhuǎn),嘴巴追上眼睛(合并狀態(tài))

          3、追上以后自轉(zhuǎn)一周(自轉(zhuǎn)狀態(tài))

          4、然后逐漸釋放眼睛(分離狀態(tài))

          5、回到初始笑臉狀態(tài)(默認狀態(tài))


          一、默認狀態(tài)


          首先需要確定好嘴巴和眼睛的初始位置,我這里的初始化嘴巴是一個半圓,在橫軸下方。眼睛分別與橫軸夾角60度,如下圖:



          這兩部分可以使用pathMeasure,我這里使用最簡單的兩個api:canvas.drawArc()和canvas.drawPoint()。


          1、畫嘴巴
           //畫起始笑臉canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false,facePaint);


          這里的startAngle初始值為0,swiperAngle為180,半徑radius為40。


          2、畫眼睛

          (1)初始化眼睛坐標

             /**     * 初始化眼睛坐標     */    private void initEyes() {        //默認兩個眼睛坐標位置 角度轉(zhuǎn)弧度        leftEyeX = (float) (-radius * Math.cos(eyeStartAngle * Math.PI / 180));        leftEyeY = (float) (-radius * Math.sin(eyeStartAngle * Math.PI / 180));        rightEyeX = (float) (radius * Math.cos(eyeStartAngle * Math.PI / 180));        rightEyeY = (float) (-radius * Math.sin(eyeStartAngle * Math.PI / 180));    }


          注意:需要將角度轉(zhuǎn)弧度


          (2)開始畫眼睛

             //畫起始眼睛  canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);  canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);


          二、合并狀態(tài)

          這個狀態(tài)可以分為兩部分

          1. 嘴巴的旋轉(zhuǎn)

          2. 眼睛的旋轉(zhuǎn)

          1、嘴巴的旋轉(zhuǎn)

          開啟動畫

               faceLoadingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);     faceLoadingAnimator.setInterpolator(new AccelerateDecelerateInterpolator());     faceLoadingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                faceValue = (float) animation.getAnimatedValue();                invalidate();            }        });        //動畫延遲500ms啟動        faceLoadingAnimator.setStartDelay(200);
          faceLoadingAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {
          }
          @Override public void onAnimationEnd(Animator animation) { //恢復(fù)起始狀態(tài) currentStatus = smileStatus; }
          @Override public void onAnimationCancel(Animator animation) {
          }
          @Override public void onAnimationRepeat(Animator animation) {
          } });


          動畫執(zhí)行時間1s,記錄動畫當(dāng)前執(zhí)行進度值,存放在faceValue中。當(dāng)動畫執(zhí)行結(jié)束的時候,需要將狀態(tài)恢復(fù)到默認狀態(tài),調(diào)用invalidate的時候,進入onDraw()方法,開始重新繪制嘴巴。

                          //記錄時刻的旋轉(zhuǎn)角度                startAngle = faceValue * 360;

          //追上右邊眼睛 if (startAngle >= 120 + startAngle / 2) {
          canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false, facePaint);
          //開始自轉(zhuǎn)一圈 mHandler.sendEmptyMessage(2);
          //此時記錄自轉(zhuǎn)一圈起始的角度 circleStartAngle = 120 + startAngle / 2;
          } else {
          //追眼睛的過程 canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false, facePaint);
          }


          這里的每次旋轉(zhuǎn)角度為startAngle。當(dāng)完全追趕上右側(cè)眼睛的時候,開始執(zhí)行自轉(zhuǎn)一周,并停止當(dāng)前動畫。

          2、眼睛的旋轉(zhuǎn)

          眼睛的開始旋轉(zhuǎn)速度明顯是慢于嘴巴的旋轉(zhuǎn)速度,所以每次的旋轉(zhuǎn)速度可以設(shè)置為嘴巴的一半

            //畫左邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的一半,這樣笑臉才能追上眼睛  leftEyeX = (float) (-radius * Math.cos((60 + startAngle / 2) * Math.PI / 180));  leftEyeY = (float) (-radius * Math.sin((60 + startAngle / 2) * Math.PI / 180));  canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);
          //畫右邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的一半,這樣笑臉才能追上眼睛 rightEyeX = (float) (radius * Math.cos((60 - startAngle / 2) * Math.PI / 180)); rightEyeY = (float) (-radius * Math.sin((60 - startAngle / 2) * Math.PI / 180)); canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);


          三、自轉(zhuǎn)狀態(tài)

          1、開啟動畫
                  circleAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);
          circleAnimator.setInterpolator(new LinearInterpolator());
          circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleValue = (float) animation.getAnimatedValue(); invalidate(); } });
          circleAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { }
          @Override public void onAnimationEnd(Animator animation) { mHandler.sendEmptyMessage(3); }
          @Override public void onAnimationCancel(Animator animation) {
          }
          @Override public void onAnimationRepeat(Animator animation) {
          } });


          2、重新繪制
           canvas.drawArc(-radius, -radius, radius, radius,                        circleStartAngle + circleValue * 360,                        swipeAngle, false, facePaint);


          四、分離狀態(tài)

          主要的注意點就是眼睛的旋轉(zhuǎn)角度設(shè)置為嘴巴旋轉(zhuǎn)角度的2倍,這樣才會達到眼睛超過嘴巴的效果,主要的旋轉(zhuǎn)代碼如下:

                          startAngle = faceValue * 360;                //判斷當(dāng)前笑臉的起點是否已經(jīng)走過260度 (吐出眼睛的角度,角度可以任意設(shè)置)                if (startAngle >= splitAngle) {
          //畫左邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的2倍,這樣眼睛才能快于笑臉旋轉(zhuǎn)速度 leftEyeX = (float) (-radius * Math.cos((eyeStartAngle + startAngle * 2) * Math.PI / 180)); leftEyeY = (float) (-radius * Math.sin((eyeStartAngle + startAngle * 2) * Math.PI / 180)); canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);
          //畫右邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的2倍,這樣眼睛才能快于笑臉旋轉(zhuǎn)速度 rightEyeX = (float) (radius * Math.cos((eyeStartAngle - startAngle * 2) * Math.PI / 180)); rightEyeY = (float) (-radius * Math.sin((eyeStartAngle - startAngle * 2) * Math.PI / 180)); canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);
          } //畫笑臉 canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false, facePaint);


          最后附上完整代碼

          public class FaceView2 extends View {

          //圓弧半徑 private int radius = 40;
          //圓弧畫筆寬度 private float paintWidth = 15;
          //笑臉狀態(tài)(一個臉,兩個眼睛) private final int smileStatus = 0;
          //加載狀態(tài) 合并眼睛,旋轉(zhuǎn) private final int loadingStatus = 1;
          //合并完成 轉(zhuǎn)一圈 private final int circleStatus = 2;
          //轉(zhuǎn)圈完成 吐出眼睛 private final int splitStatus = 3;
          //當(dāng)前狀態(tài) private int currentStatus = smileStatus;
          //笑臉畫筆 private Paint facePaint; //眼睛畫筆 private Paint eyePaint;
          //笑臉開始角度 private float startAngle; //笑臉弧度 private float swipeAngle;
          //左側(cè)眼睛起點x軸坐標 private float leftEyeX = 0; //左側(cè)眼睛起點y軸坐標 private float leftEyeY = 0;
          //右側(cè)眼睛起點x軸坐標 private float rightEyeX; //右側(cè)眼睛起點y軸坐標 private float rightEyeY;
          //一開始默認狀態(tài)笑臉轉(zhuǎn)圈動畫 private ValueAnimator faceLoadingAnimator;
          //吞并完成后,自轉(zhuǎn)一圈動畫 private ValueAnimator circleAnimator;
          //faceLoadingAnimator動畫進度值 private float faceValue;
          //circleAnimator動畫進度值 private float circleValue;
          //記錄開始自轉(zhuǎn)一圈的起始角度 private float circleStartAngle;
          //吐出眼睛的角度 private float splitAngle;
          private float initStartAngle;
          //眼睛起始角度 private float eyeStartAngle = 60;
          public FaceView2(Context context) { this(context, null); }
          public FaceView2(Context context, AttributeSet attrs) { this(context, attrs, 0); }
          public FaceView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);
          //自定義屬性 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FaceView2, defStyleAttr, 0);
          initStartAngle = typedArray.getFloat(R.styleable.FaceView2_startAngle, 0); swipeAngle = typedArray.getFloat(R.styleable.FaceView2_swipeAngle, 180); splitAngle = typedArray.getFloat(R.styleable.FaceView2_splitAngle, 260);
          typedArray.recycle();
          startAngle = initStartAngle; eyeStartAngle += startAngle;
          initEyes();
          initPaint();
          //開始默認動畫 initAnimator();
          }
          /** * 初始化畫筆 */ private void initPaint() { //初始化畫筆 facePaint = new Paint(); facePaint.setStrokeWidth(paintWidth); facePaint.setColor(Color.RED); facePaint.setAntiAlias(true); facePaint.setStyle(Paint.Style.STROKE); facePaint.setStrokeCap(Paint.Cap.ROUND);
          eyePaint = new Paint(); eyePaint.setStrokeWidth(paintWidth); eyePaint.setColor(Color.RED); eyePaint.setAntiAlias(true); eyePaint.setStyle(Paint.Style.STROKE); eyePaint.setStrokeCap(Paint.Cap.ROUND);
          }
          /** * 初始化眼睛坐標 */ private void initEyes() { //默認兩個眼睛坐標位置 角度轉(zhuǎn)弧度 leftEyeX = (float) (-radius * Math.cos(eyeStartAngle * Math.PI / 180)); leftEyeY = (float) (-radius * Math.sin(eyeStartAngle * Math.PI / 180)); rightEyeX = (float) (radius * Math.cos(eyeStartAngle * Math.PI / 180)); rightEyeY = (float) (-radius * Math.sin(eyeStartAngle * Math.PI / 180));
          }
          private Handler mHandler = new Handler(new Handler.Callback() { @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public boolean handleMessage(Message msg) { switch (msg.what) { case 1: //啟動一開始笑臉轉(zhuǎn)圈動畫,并且開始合并眼睛 currentStatus = loadingStatus; faceLoadingAnimator.start(); break;
          case 2: //暫停眼睛和笑臉動畫 currentStatus = circleStatus; faceLoadingAnimator.pause(); //啟動笑臉自轉(zhuǎn)一圈動畫 circleAnimator.start(); break; case 3: //恢復(fù)笑臉轉(zhuǎn)圈動畫,并且開始分離眼睛 currentStatus = splitStatus; circleAnimator.cancel(); faceLoadingAnimator.resume(); invalidate();
          break; } return false; } });
          @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //畫布移到中間 canvas.translate(getWidth() / 2, getHeight() / 2);
          switch (currentStatus) { //起始狀態(tài) case smileStatus: //起始角度為0 startAngle = initStartAngle;
          //畫起始笑臉 canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false, facePaint);
          //重置起始眼睛坐標 initEyes();
          //畫起始眼睛 canvas.drawPoint(leftEyeX, leftEyeY, eyePaint); canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);
          //更改狀態(tài),進行笑臉合并眼睛 mHandler.sendEmptyMessage(1); break;
          //合并狀態(tài) case loadingStatus:
          //記錄時刻的旋轉(zhuǎn)角度 startAngle = faceValue * 360;

          //追上右邊眼睛 if (startAngle >= 120 + startAngle / 2) {
          canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false, facePaint);
          //開始自轉(zhuǎn)一圈 mHandler.sendEmptyMessage(2);
          //此時記錄自轉(zhuǎn)一圈起始的角度 circleStartAngle = 120 + startAngle / 2;
          } else {
          //追眼睛的過程 canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false, facePaint);
          }
          //畫左邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的一半,這樣笑臉才能追上眼睛 leftEyeX = (float) (-radius * Math.cos((60 + startAngle / 2) * Math.PI / 180)); leftEyeY = (float) (-radius * Math.sin((60 + startAngle / 2) * Math.PI / 180)); canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);
          //畫右邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的一半,這樣笑臉才能追上眼睛 rightEyeX = (float) (radius * Math.cos((60 - startAngle / 2) * Math.PI / 180)); rightEyeY = (float) (-radius * Math.sin((60 - startAngle / 2) * Math.PI / 180)); canvas.drawPoint(rightEyeX, rightEyeY, eyePaint); break;
          //自轉(zhuǎn)一圈狀態(tài) circleValue * 360 為旋轉(zhuǎn)角度 case circleStatus: canvas.drawArc(-radius, -radius, radius, radius, circleStartAngle + circleValue * 360, swipeAngle, false, facePaint); break;
          //笑臉眼睛分離狀態(tài) case splitStatus:
          startAngle = faceValue * 360; //判斷當(dāng)前笑臉的起點是否已經(jīng)走過260度 (吐出眼睛的角度,角度可以任意設(shè)置) if (startAngle >= splitAngle) {
          //畫左邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的2倍,這樣眼睛才能快于笑臉旋轉(zhuǎn)速度 leftEyeX = (float) (-radius * Math.cos((eyeStartAngle + startAngle * 2) * Math.PI / 180)); leftEyeY = (float) (-radius * Math.sin((eyeStartAngle + startAngle * 2) * Math.PI / 180)); canvas.drawPoint(leftEyeX, leftEyeY, eyePaint);
          //畫右邊眼睛 ,旋轉(zhuǎn)的角度設(shè)置為笑臉旋轉(zhuǎn)角度的2倍,這樣眼睛才能快于笑臉旋轉(zhuǎn)速度 rightEyeX = (float) (radius * Math.cos((eyeStartAngle - startAngle * 2) * Math.PI / 180)); rightEyeY = (float) (-radius * Math.sin((eyeStartAngle - startAngle * 2) * Math.PI / 180)); canvas.drawPoint(rightEyeX, rightEyeY, eyePaint);
          } //畫笑臉 canvas.drawArc(-radius, -radius, radius, radius, startAngle, swipeAngle, false, facePaint);
          break;
          } }
          /** * 初始化動畫 */ private void initAnimator() {

          faceLoadingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000); faceLoadingAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); faceLoadingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { faceValue = (float) animation.getAnimatedValue(); invalidate(); } }); //動畫延遲500ms啟動 faceLoadingAnimator.setStartDelay(200);
          faceLoadingAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {
          }
          @Override public void onAnimationEnd(Animator animation) { //恢復(fù)起始狀態(tài) currentStatus = smileStatus; }
          @Override public void onAnimationCancel(Animator animation) {
          }
          @Override public void onAnimationRepeat(Animator animation) {
          } });

          circleAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);
          circleAnimator.setInterpolator(new LinearInterpolator());
          circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { circleValue = (float) animation.getAnimatedValue(); invalidate(); } });
          circleAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { }
          @Override public void onAnimationEnd(Animator animation) { mHandler.sendEmptyMessage(3); }
          @Override public void onAnimationCancel(Animator animation) {
          }
          @Override public void onAnimationRepeat(Animator animation) {
          } });    }}


          自定義屬性

          <declare-styleable name="FaceView2">        <attr name="startAngle" format="dimension" />        <attr name="swipeAngle" format="dimension" />        <attr name="splitAngle" format="dimension" /></declare-styleable>


          布局文件中使用

          <com.example.viewdemo.FaceView2     android:layout_width="match_parent"     android:layout_height="match_parent"/>


          完整代碼都在上面啦.


          到這里就結(jié)束啦.


          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋成人在线观看 | 色婷婷精品国产一区二区三区 | 天天操你小骚逼 | av一区网站 | 亚洲综合免费观看高清完整版在线 |