您的位置:首页 > 移动开发 > Android开发

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

2017-05-17 15:32 666 查看
首先上效果图:



该搜索按钮有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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 动画