Android高级UICanvas和Path使用——自定义SearchView搜索框动画

预览:

自定义搜索框动画

一、实现思路:
圆形搜索框长度应该与下面的横线的长度相同
整个图形分成三部分:圆、与圆相连的手柄、与手柄相连的横线
动画过程中圆与手柄是一体的,其长度与下面的横线的长度成相反的关系
二、实现步骤:
1,初始化画布及画笔
2,分别绘制圆,手柄及下边的横线
3,启动动画,更改相应的比例参数实现该效果

1,初始化画布及画笔:

        path = new Path();
        paint = new Paint();
        paint.setColor(Color.RED);
        paint.setFlags(Paint.DITHER_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(8);
        paint.setAntiAlias(true);
        paint.setDither(true);

2、计算总的路径的长度

round = (float) (2*Math.PI*radius);
length = round+lLength;

round为圆的周长,lLength为定义的手柄的长度,length则为总长度,也就是下边横线的总长度

3、重新ondraw方法开始绘制

绘制圆弧使用path.addArc(RectF oval, float startAngle, float sweepAngle)方法:
path.addArc方法用于绘制圆弧,这个圆弧取自RectF矩形的内接椭圆上的一部分,圆弧长度由后两个参数决定
startAngle:起始位置的角度值
sweepAngle:旋转的角度值

@Override
    protected void onDraw(Canvas canvas) {
        path.reset();
        //mpro 用于更改圆弧的角度,-360*mpro 为顺时针旋转
        if(mpro>0){
            path.addArc(mRectF,45,-360*mpro);
        }

        canvas.drawPath(path,paint);
        canvas.save();
        //绘制手柄的直线,此处也可以将画布旋转45度以后直接绘制,此处采用正切值计算起始点的坐标
        canvas.drawLine(
                pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength),
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength),
                pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                paint);
//绘制下边的直线
        canvas.drawLine(pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-lineLen,
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                paint);
    }

Math.sin(2*Math.PI/360*45):此处计算正切值传入的参数是弧度值,而不是角度值
4、动画

展开动画

public void startViewAnimation(){
        if(valueAnimator!=null&&valueAnimator.isRunning()){
            return;
        }
        valueAnimator = ValueAnimator.ofFloat(0,length);
        valueAnimator.setDuration((long) (2000));
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float  curLen = (float) animation.getAnimatedValue();
                float lingTh = curLen -round;
                //修改圆弧坐标点
                pointF.x+=2;
                mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius);
                if(lingTh>0){
                    lLength = (int) (80-lingTh);
                }else{
                    if(curLen<=round){
                        mpro= 1-curLen/round - 0.009f;
                    }
                }
                lineLen = curLen;
              invalidate();
            }
        });
         valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                lLength = 80;
                pointF.x = 100;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                endViewAnimation();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();

还原动画

public void endViewAnimation(){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(length,0);
        valueAnimator.setDuration((long) (2000));
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float  curLen = (float) animation.getAnimatedValue();
                float lingTh = curLen -round;
                pointF.x-=2;
                mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius);
                if(lingTh>0){
                    lLength = (int) (80-lingTh);
                }else{
                    if(curLen<=round){
                        mpro= 1-curLen/round;
                    }
                }
                lineLen = curLen;
                invalidate();
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                startViewAnimation();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();
    }

最后在MotionEvent.ACTION_DOWN 的时候启动动画就行了

完整代码:

package com.example.flowproject;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;


public class DrawView extends View {
    private float round;
    private Path path;
    private Paint paint = null;
    private int VIEW_WIDTH = 800;
    private int VIEW_HEIGHT = 600;
    Bitmap cacheBitmap = null;
    //定义缓冲区Cache的Canvas对象
    Canvas cacheCanvas = null;
    private RectF mRectF;
    public float mpro = -1;
    float length;
    float lineLen;
    PointF pointF;
    int radius = 50;
    int lLength = LINE_LENGTH;
    private static final int LINE_LENGTH = 80;
    private ValueAnimator valueAnimator;

    public DrawView(Context context) {
        this(context,null);
        pointF = new PointF(100,800);
        mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius);
        mpro= 0;
    }

    public DrawView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //创建一个与该VIew相同大小的缓冲区
        cacheBitmap = Bitmap.createBitmap(VIEW_WIDTH,VIEW_HEIGHT,Bitmap.Config.ARGB_8888);
        //创建缓冲区Cache的Canvas对象
        cacheCanvas = new Canvas();
        path = new Path();
        //设置cacheCanvas将会绘制到内存的bitmap上
        cacheCanvas.setBitmap(cacheBitmap);
        paint = new Paint();
        paint.setColor(Color.RED);
        paint.setFlags(Paint.DITHER_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(8);
        paint.setAntiAlias(true);
        paint.setDither(true);

        round = (float) (2*Math.PI*radius);
        length = round+lLength;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        path.reset();
        //mpro为圆的角度的进度,当mpro==0时,则整个圆绘制完成
        if(mpro>0){
            path.addArc(mRectF,45,-360*mpro);
        }
        paint.setStrokeCap(Paint.Cap.ROUND);
        canvas.drawPath(path,paint);
        canvas.save();
        canvas.drawLine(
                pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength),
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-(float)(Math.sin(2*Math.PI/360*45)*lLength),
                pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                paint);

        canvas.drawLine(pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH))-lineLen,
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                pointF.x+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                pointF.y+(float)(Math.sin(2*Math.PI/360*45)*(radius+LINE_LENGTH)),
                paint);
    }

    public void startViewAnimation(){
        if(valueAnimator!=null&&valueAnimator.isRunning()){
            return;
        }
        valueAnimator = ValueAnimator.ofFloat(0,length);
        valueAnimator.setDuration((long) (2000));
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float  curLen = (float) animation.getAnimatedValue();
                float lingTh = curLen -round;
                pointF.x+=2;
                mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius);
                if(lingTh>0){
                    lLength = (int) (80-lingTh);
                }else{
                    if(curLen<=round){
                        mpro= 1-curLen/round - 0.009f;
                    }
                }
                lineLen = curLen;
              invalidate();
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                lLength = 80;
                pointF.x = 100;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                endViewAnimation();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();
    }

    public void endViewAnimation(){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(length,0);
        valueAnimator.setDuration((long) (2000));
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float  curLen = (float) animation.getAnimatedValue();
                float lingTh = curLen -round;
                pointF.x-=2;
                mRectF = new RectF(pointF.x-radius,pointF.y-radius,pointF.x+radius,pointF.y+radius);
                if(lingTh>0){
                    lLength = (int) (80-lingTh);
                }else{
                    if(curLen<=round){
                        mpro= 1-curLen/round;
                    }
                }
                lineLen = curLen;
                invalidate();
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                startViewAnimation();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                startViewAnimation();
                break;
        }
        return true;
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值