Android 自定义View实现打钩(签到)的动画

先看效果图:

Android 自定义View实现打钩(签到)的动画_第1张图片

这里,我没有添加打钩的图片,而是单纯的用canvas来实现动画效果

中间的钩,我用了路径Path来进行描绘并实现它的动画效果。首先,这个钩由两条线段,三个顶点组成的,其实将这三个顶点作为参数传入Path对象中的lineTo()方法,再调用一下canvas.drawPath(),我们就可以得到图中这个钩的样式了

然后说说动画效果的实现,postInvalidateDelay()这个方法就很重要了,它能让onDraw()方法每隔一段时间被调用一次

所以,外部的圆环我们可以用drawArc()绘制圆弧的方式实现,每次调用onDraw()方法时绘制一定的角度,直到绘制出完整的圆环,就产生以上图中的动画效果

绘制圆环代码如下:

private int perAngle=5,currentAngle=0;     //每次绘制的角度;当前已经绘制的角度
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //......
    //防止绘制的角度超过360度
    if(currentAngle+perAngle>360)currentAngle=360;
    else currentAngle+=perAngle;
    canvas.drawArc(circleRectF,0,currentAngle,false,circlePaint);
    //......
    postInvalidateDelayed(5);
}

最后是钩的动画实现,我首先计算了两条线段的斜率和截距:

//提供打钩的三个坐标点(radius/2,radius),(radius,(radius*3)/2),((radius*3)/2,radius/2)
pathX=new int[]{radius/2,radius,(radius*3)/2};
pathY=new int[]{radius,(radius*3)/2,radius/2};
slopes=new float[pathX.length-1];
intercepts=new float[pathX.length-1];
//根据上面三个坐标点,得到两条线段,计算斜率和截距。
for(int i=0;i

pathX,pathY (int[]) 记录三个顶点的坐标

slopes (float[]) 记录两条线段的斜率

intercepts (float[]) 记录两条线段的截距

 然后,我们设想一下,钩的移动方向一直是向右边的,也就是x轴的正方向,所以,我们从第一个顶点出发,每次调用onDraw()方法时,沿着上面的两个线段往右跑,即每次让x坐标增大一定的值,再通过y=ax+b,就可以计算出相应的y值。将(x,y)存入Path路径中,最后通过canvas.drawPath()将新保存的路径画出来,就可以实现动画效果

具体代码如下:

private int moveX=5,currentMoveX; //钩每次绘制的长度;目前移动到的位置
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //......
    //绘制完圆环后,绘制打钩
    //每次向x轴的正方向移动moveX的距离来实现动画效果
    if(currentAngle>=360){
        //防止绘制时出边界
        if(currentMoveX+moveX>pathX[pathX.length-1])currentMoveX=pathX[pathX.length-1];
        else currentMoveX+=moveX;
        //判断目前已经绘制到哪一条线段上
        for(int i=1;i

下面是自定义View的完整代码:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class MyProgressFinishView extends View {
    private Paint circlePaint;  //环的画笔
    private RectF circleRectF;  //环的外切矩形
    private Path path;          //钩的路径
    private Paint pathPaint;    //钩的画笔
    private int radius=200;     //环的半径
    private int perAngle=5,currentAngle=0;     //每次绘制的角度;当前已经绘制的角度
    private int[] pathX,pathY;
    private float[] slopes; //每条直线的斜率
    private float[] intercepts; //每条直线的截距
    private int moveX=5,currentMoveX; //钩每次绘制的长度;目前移动到的位置
    private boolean isFirstMeasure=true;
    public MyProgressFinishView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    //初始化画笔
    private void initPaint(){
        circlePaint=new Paint();
        circlePaint.setColor(Color.BLUE);
        circlePaint.setStrokeWidth(8);
        circlePaint.setStyle(Paint.Style.STROKE);
        pathPaint=new Paint();
        pathPaint.setColor(Color.BLUE);
        pathPaint.setStrokeWidth(10);
        pathPaint.setStyle(Paint.Style.STROKE);
    }

    //初始化钩的路径
    private void initPath(){
        circleRectF=new RectF(circlePaint.getStrokeWidth(),circlePaint.getStrokeWidth(),radius*2,radius*2);
        path=new Path();
        //提供打钩的三个坐标点(radius/2,radius),(radius,(radius*3)/2),((radius*3)/2,radius/2)
        pathX=new int[]{radius/2,radius,(radius*3)/2};
        pathY=new int[]{radius,(radius*3)/2,radius/2};
        slopes=new float[pathX.length-1];
        intercepts=new float[pathX.length-1];
        //根据上面三个坐标点,得到两条线段,计算斜率和截距。
        for(int i=0;i360)currentAngle=360;
        else currentAngle+=perAngle;
        canvas.drawArc(circleRectF,0,currentAngle,false,circlePaint);
        //绘制完圆环后,绘制打钩
        //每次向x轴的正方向移动moveX的距离来实现动画效果
        if(currentAngle>=360){
            //防止绘制时出边界
            if(currentMoveX+moveX>pathX[pathX.length-1])currentMoveX=pathX[pathX.length-1];
            else currentMoveX+=moveX;
            //判断目前已经绘制到哪一条线段上
            for(int i=1;i

xml布局文件



    

 

你可能感兴趣的:(Android,JAVA,android,安卓,canvas)