天天看點

自定義控件之仿豆瓣笑臉進度加載

最近剛開始接觸Android動畫,然後湊齊發現豆瓣的笑臉加載動畫,覺得比較友好。百度發現已經有同學實作過了,不過和自己的想法有點不一樣,覺得有點費解,于是自己重新實作一個。那位同學的連結[https://blog.csdn.net/qinbin2015/article/details/98481620]

連結位址

實作效果

自定義控件之仿豆瓣笑臉進度加載

實作思路

  1. 整個笑臉動圖由兩部分構成,眼睛+嘴巴
  2. 眼睛是線性加速器LinearInterpolator,嘴巴是先加速後減速的非線性加速器AccelerateDecelerateInterpolator
  3. 一個完整的動畫周期,眼睛旋轉一圈(360°),嘴巴旋轉兩圈(720°)
  4. 和連結裡同學的差別,使用ObjectAnimator代替ValueAnimator

眼睛部分代碼實作

public class EyesView extends View {
    int radius = 60;
    private Paint mEyesPaint;
    //左側眼睛起點x軸坐标
    private float leftEyeX = 0;
    //左側眼睛起點y軸坐标
    private float leftEyeY = 0;

    //右側眼睛起點x軸坐标
    private float rightEyeX;
    //右側眼睛起點y軸坐标
    private float rightEyeY;

    int eyeStartAngle = 60;

    public EyesView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        startAnimation();
    }

    public EyesView(Context context){
        super(context);
        init();
        startAnimation();
    }
    private void init(){

        //預設兩個眼睛坐标位置 角度轉弧度
        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));

        mEyesPaint = new Paint();
        mEyesPaint.setStrokeWidth(20.f);
        mEyesPaint.setColor(Color.GREEN);
        mEyesPaint.setAntiAlias(true);
        mEyesPaint.setStyle(Paint.Style.STROKE);
        mEyesPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth() / 2, getHeight() / 2);
        canvas.drawPoint(leftEyeX, leftEyeY, mEyesPaint);
        canvas.drawPoint(rightEyeX, rightEyeY, mEyesPaint);
    }

    void startAnimation(){
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "rotation",360f);
        objectAnimator.setDuration(2000);
        objectAnimator.setRepeatCount(Animation.INFINITE);
        objectAnimator.setRepeatMode(Animation.INFINITE);
        objectAnimator.setInterpolator(new LinearInterpolator());
        objectAnimator.start();
    }
}
           

嘴巴部分代碼實作

public class SmileView extends View {

    private Paint m_smilePaint;
    private float startAngle = 0;
    private float swipeAngle = 180;
    private int radius = 60;

    private SmileView(Context context){
        super(context);
        init();
        startAnimation();
    }

    public SmileView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        startAnimation();
    }

    private void init(){
        m_smilePaint = new Paint();
        m_smilePaint.setStrokeWidth(20.f);
        m_smilePaint.setColor(Color.GREEN);
        m_smilePaint.setAntiAlias(true);
        m_smilePaint.setStyle(Paint.Style.STROKE);
        m_smilePaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth() / 2, getHeight() / 2);
        canvas.drawArc(-radius, -radius, radius, radius, startAngle,
                swipeAngle, false, m_smilePaint);
    }

    private void startAnimation(){
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "rotation",720f);
        objectAnimator.setDuration(2000);
        objectAnimator.setRepeatCount(Animation.INFINITE);
        objectAnimator.setRepeatMode(ValueAnimator.RESTART);
        objectAnimator.start();
    }
}
           

整個自定義笑臉FaceView的實作

public class FaceView extends RelativeLayout {
    final static String TAG = "FaceView";
    public FaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public FaceView(Context context){
        super(context);
        initView(context);
    }

    private void initView(Context context){
        LayoutInflater.from(context).inflate(R.layout.faceview,this,true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth() / 2, getHeight() / 2);
        invalidate();
    }
}
           

補上剩餘布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.customView.douban.FaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>