Android:使用PathMeasure绘制动画效果的搜索按钮

首先上效果图:
这里写图片描述

该搜索按钮有4种状态:
1. 默认状态:一个静态的放大镜;
2. 开始搜索状态:放大镜逐渐缩小为一个点;
3. 正在搜索状态:一个动态的圈;
4. 结束搜索状态:放大镜由一个点恢复初始状态。

此处点击一下模拟开始搜索,再点击一下搜索结束。

代码不多,直接全部贴上:

/**
 * 使用PathMeasure自定义的MySearchView.
 * Created by xxx on 17-5-15.
 */

public class MySearchView extends View {
    private static final int BG_COLOR = Color.parseColor("#FF37A1EC");
    private static final int PADDING = 8;

    private Path bgPath;  //圆角矩形背景
    private Path searchPath;  //放大镜Path
    private Path circlePath;  //圆圈Path
    private static final int PAINT_WIDTH = 4;

    private PathMeasure measure;

    private Paint mPaint;
    private Paint mBgPaint;

    private static final int STATE_INIT = 0;  //初始状态
    private static final int STATE_STARTING = 1;  //开始搜索状态
    private static final int STATE_SEARCHING = 2;  //正在搜索状态
    private static final int STATE_ENDDING = 3;  //结束搜索状态

    //标记当前状态
    private int state = STATE_INIT;

    public MySearchView(Context context) {
        super(context);
    }

    public MySearchView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MySearchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private float searchLen;  //放大镜路径总长度
    private float circleLen;  //大圆圈总长度

    private void init() {
        measure = new PathMeasure();

        //小圆的半径
        int smallR = (mWidth - PADDING * 2) / 4;
        //中心点坐标
        int centerXY = mWidth / 2;

        //放大镜
        searchPath = new Path();
        RectF rect1 = new RectF(centerXY - smallR, centerXY - smallR, centerXY + smallR, centerXY + smallR);
        searchPath.addArc(rect1, 45, 359.9f);
        //放大镜把手
        searchPath.rLineTo(((float) (smallR * Math.sqrt(2) / 2)), ((float) (smallR * Math.sqrt(2) / 2)));

        searchPath_dst = new Path();

        //大圆圈
        circlePath = new Path();
        RectF rect2 = new RectF(centerXY - smallR * 2, centerXY - smallR * 2, centerXY + smallR * 2, centerXY + smallR * 2);
        circlePath.addArc(rect2, 45, 359.9f);

        circlePath_dst = new Path();

        //计算放大镜和圆圈的长度
        measure.setPath(searchPath, false);
        searchLen = measure.getLength();
        measure.setPath(circlePath, false);
        circleLen = measure.getLength();

        bgPath = new Path();
        RectF rect3 = new RectF(0, 0, mWidth, mHeight);
        bgPath.addRoundRect(rect3, 12, 12, Path.Direction.CW);

        mPaint = new Paint();
        mPaint.setColor(Color.WHITE);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(PAINT_WIDTH);
        mPaint.setStyle(Paint.Style.STROKE);

        mBgPaint = new Paint();
        mBgPaint.setColor(BG_COLOR);
        mBgPaint.setAntiAlias(true);
        mBgPaint.setStyle(Paint.Style.FILL);
    }

    //开始搜索
    private void starting() {
        state = STATE_STARTING;
        measure.setPath(searchPath, false);

        //动画
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(START_DURATION);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float v = (float) animation.getAnimatedValue();
                searchPath_dst.reset();
                measure.getSegment(searchLen * v, searchLen, searchPath_dst, true);
                searchPath_dst.rLineTo(0, 0);
                invalidate();

                if (v == 1) {
                    searching();  //进入搜索状态
                }
            }
        });
        animator.start();
    }

    private static final int START_DURATION = 1200;  //开始搜索时动画周期
    private static final int SEARCH_DURATION = 1200;  //正在搜索时动画周期

    //正在搜索
    private void searching() {
        state = STATE_SEARCHING;
        measure.setPath(circlePath, false);

        ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(SEARCH_DURATION);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float v = (float) animation.getAnimatedValue();
                circlePath_dst.reset();

                float stop = v * circleLen;
                float start = (float) (stop - ((0.5 - Math.abs(v - 0.5)) * 160f));
                measure.getSegment(start, stop, circlePath_dst, true);
                circlePath_dst.rLineTo(0, 0);
                invalidate();
            }
        });
        animator.start();

        postDelayed(new Runnable() {
            @Override
            public void run() {
                if (!isEnd) {
                    searching();
                } else {
                    ending();
                }
            }
        }, SEARCH_DURATION);
    }

    private boolean isEnd = false;

    //结束搜索
    private void ending() {
        state = STATE_ENDDING;
        measure.setPath(searchPath, false);

        //动画
        ValueAnimator animator = ValueAnimator.ofFloat(1, 0).setDuration(START_DURATION);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float v = (float) animation.getAnimatedValue();
                searchPath_dst.reset();
                measure.getSegment(searchLen * v, searchLen, searchPath_dst, true);
                searchPath_dst.rLineTo(0, 0);

                if (v == 0) {
                    state = STATE_INIT;
                    isEnd = false;
                }
                invalidate();
            }
        });
        animator.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawSearchView(canvas);
    }

    private Path searchPath_dst;
    private Path circlePath_dst;

    private void drawSearchView(final Canvas canvas) {
        canvas.drawPath(bgPath, mBgPaint);

        if (state == STATE_INIT) {
            //初始状态,绘制静态的放大镜
            canvas.drawPath(searchPath, mPaint);
        } else if (state == STATE_STARTING) {
            //开始搜索状态,绘制放大镜缩小到一个点的过程
            canvas.drawPath(searchPath_dst, mPaint);
        } else if (state == STATE_SEARCHING) {
            //正在搜索状态,绘制外层大圆圈转圈
            canvas.drawPath(circlePath_dst, mPaint);
        } else if (state == STATE_ENDDING) {
            //搜索结束,绘制放大镜恢复
            canvas.drawPath(searchPath_dst, mPaint);
        }
    }

    //模拟搜索过程,点击一下开始搜索,再次点击搜索完成
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_UP) {
            if (state == STATE_INIT) {
                starting();
            } else if (state == STATE_SEARCHING) {
                isEnd = true;
            }
            return true;
        }

        return super.onTouchEvent(event);
    }

    private int mWidth, mHeight;

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        boolean first = (mWidth == 0);
        if (changed) {
            mWidth = getWidth();
            mHeight = getHeight();
        }

        if (first) {
            init();
        }
    }

}

使用方法也很简单,放在xml布局文件中即可,注意需加上android:clickable="true"属性,否则组件无法接收ACTION_UP事件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <com.chinark.appandroidtest.widget.MySearchView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_margin="30dp"
        android:clickable="true" />

</LinearLayout>

参考文章: http://blog.csdn.net/u013831257/article/details/51565591

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值