2016-06-02 02:33:00 +08:00
|
|
|
## SearchView 源代码
|
|
|
|
|
|
2016-06-02 02:33:37 +08:00
|
|
|
``` java
|
2016-06-02 02:33:00 +08:00
|
|
|
/**
|
|
|
|
|
* Author: GcsSloop
|
|
|
|
|
* <p>
|
|
|
|
|
* Created Date: 16/5/31
|
|
|
|
|
* <p>
|
|
|
|
|
* Copyright (C) 2016 GcsSloop.
|
|
|
|
|
* <p>
|
|
|
|
|
* GitHub: https://github.com/GcsSloop
|
|
|
|
|
*/
|
|
|
|
|
public class SearchView extends View {
|
|
|
|
|
|
|
|
|
|
// 画笔
|
|
|
|
|
private Paint mPaint;
|
|
|
|
|
|
|
|
|
|
// View 宽高
|
|
|
|
|
private int mViewWidth;
|
|
|
|
|
private int mViewHeight;
|
|
|
|
|
|
|
|
|
|
// 这个视图拥有的状态
|
|
|
|
|
public static enum State {
|
|
|
|
|
NONE,
|
|
|
|
|
STARTING,
|
|
|
|
|
SEARCHING,
|
|
|
|
|
ENDING
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当前的状态(非常重要)
|
|
|
|
|
private State mCurrentState = State.NONE;
|
|
|
|
|
|
|
|
|
|
// 放大镜与外部圆环
|
|
|
|
|
private Path path_srarch;
|
|
|
|
|
private Path path_circle;
|
|
|
|
|
|
|
|
|
|
// 测量Path 并截取部分的工具
|
|
|
|
|
private PathMeasure mMeasure;
|
|
|
|
|
|
|
|
|
|
// 默认的动效周期 2s
|
|
|
|
|
private int defaultDuration = 2000;
|
|
|
|
|
|
|
|
|
|
// 控制各个过程的动画
|
|
|
|
|
private ValueAnimator mStartingAnimator;
|
|
|
|
|
private ValueAnimator mSearchingAnimator;
|
|
|
|
|
private ValueAnimator mEndingAnimator;
|
|
|
|
|
|
|
|
|
|
// 动画数值(用于控制动画状态,因为同一时间内只允许有一种状态出现,具体数值处理取决于当前状态)
|
|
|
|
|
private float mAnimatorValue = 0;
|
|
|
|
|
|
|
|
|
|
// 动效过程监听器
|
|
|
|
|
private ValueAnimator.AnimatorUpdateListener mUpdateListener;
|
|
|
|
|
private Animator.AnimatorListener mAnimatorListener;
|
|
|
|
|
|
|
|
|
|
// 用于控制动画状态转换
|
|
|
|
|
private Handler mAnimatorHandler;
|
|
|
|
|
|
|
|
|
|
// 判断是否已经搜索结束
|
|
|
|
|
private boolean isOver = false;
|
|
|
|
|
|
|
|
|
|
private int count = 0;
|
|
|
|
|
|
|
|
|
|
public SearchView(Context context) {
|
|
|
|
|
super(context);
|
|
|
|
|
|
|
|
|
|
initPaint();
|
|
|
|
|
|
|
|
|
|
initPath();
|
|
|
|
|
|
|
|
|
|
initListener();
|
|
|
|
|
|
|
|
|
|
initHandler();
|
|
|
|
|
|
|
|
|
|
initAnimator();
|
|
|
|
|
|
|
|
|
|
// 进入开始动画
|
|
|
|
|
mCurrentState = State.STARTING;
|
|
|
|
|
mStartingAnimator.start();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initPaint() {
|
|
|
|
|
mPaint = new Paint();
|
|
|
|
|
mPaint.setStyle(Paint.Style.STROKE);
|
|
|
|
|
mPaint.setColor(Color.WHITE);
|
|
|
|
|
mPaint.setStrokeWidth(15);
|
|
|
|
|
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
|
|
|
|
mPaint.setAntiAlias(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initPath() {
|
|
|
|
|
path_srarch = new Path();
|
|
|
|
|
path_circle = new Path();
|
|
|
|
|
|
|
|
|
|
mMeasure = new PathMeasure();
|
|
|
|
|
|
|
|
|
|
// 注意,不要到360度,否则内部会自动优化,测量不能取到需要的数值
|
|
|
|
|
RectF oval1 = new RectF(-50, -50, 50, 50); // 放大镜圆环
|
|
|
|
|
path_srarch.addArc(oval1, 45, 359.9f);
|
|
|
|
|
|
|
|
|
|
RectF oval2 = new RectF(-100, -100, 100, 100); // 外部圆环
|
|
|
|
|
path_circle.addArc(oval2, 45, -359.9f);
|
|
|
|
|
|
|
|
|
|
float[] pos = new float[2];
|
|
|
|
|
|
|
|
|
|
mMeasure.setPath(path_circle, false); // 放大镜把手的位置
|
|
|
|
|
mMeasure.getPosTan(0, pos, null);
|
|
|
|
|
|
|
|
|
|
path_srarch.lineTo(pos[0], pos[1]); // 放大镜把手
|
|
|
|
|
|
|
|
|
|
Log.i("TAG", "pos=" + pos[0] + ":" + pos[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initListener() {
|
|
|
|
|
mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onAnimationUpdate(ValueAnimator animation) {
|
|
|
|
|
mAnimatorValue = (float) animation.getAnimatedValue();
|
|
|
|
|
invalidate();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mAnimatorListener = new Animator.AnimatorListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onAnimationStart(Animator animation) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onAnimationEnd(Animator animation) {
|
|
|
|
|
// getHandle发消息通知动画状态更新
|
|
|
|
|
mAnimatorHandler.sendEmptyMessage(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onAnimationCancel(Animator animation) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onAnimationRepeat(Animator animation) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initHandler() {
|
|
|
|
|
mAnimatorHandler = new Handler() {
|
|
|
|
|
@Override
|
|
|
|
|
public void handleMessage(Message msg) {
|
|
|
|
|
super.handleMessage(msg);
|
|
|
|
|
switch (mCurrentState) {
|
|
|
|
|
case STARTING:
|
|
|
|
|
// 从开始动画转换好搜索动画
|
|
|
|
|
isOver = false;
|
|
|
|
|
mCurrentState = State.SEARCHING;
|
|
|
|
|
mStartingAnimator.removeAllListeners();
|
|
|
|
|
mSearchingAnimator.start();
|
|
|
|
|
break;
|
|
|
|
|
case SEARCHING:
|
|
|
|
|
if (!isOver) { // 如果搜索未结束 则继续执行搜索动画
|
|
|
|
|
mSearchingAnimator.start();
|
|
|
|
|
Log.e("Update", "RESTART");
|
|
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
if (count>2){ // count大于2则进入结束状态
|
|
|
|
|
isOver = true;
|
|
|
|
|
}
|
|
|
|
|
} else { // 如果搜索已经结束 则进入结束动画
|
|
|
|
|
mCurrentState = State.ENDING;
|
|
|
|
|
mEndingAnimator.start();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ENDING:
|
|
|
|
|
// 从结束动画转变为无状态
|
|
|
|
|
mCurrentState = State.NONE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initAnimator() {
|
|
|
|
|
mStartingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
|
|
|
|
|
mSearchingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
|
|
|
|
|
mEndingAnimator = ValueAnimator.ofFloat(1, 0).setDuration(defaultDuration);
|
|
|
|
|
|
|
|
|
|
mStartingAnimator.addUpdateListener(mUpdateListener);
|
|
|
|
|
mSearchingAnimator.addUpdateListener(mUpdateListener);
|
|
|
|
|
mEndingAnimator.addUpdateListener(mUpdateListener);
|
|
|
|
|
|
|
|
|
|
mStartingAnimator.addListener(mAnimatorListener);
|
|
|
|
|
mSearchingAnimator.addListener(mAnimatorListener);
|
|
|
|
|
mEndingAnimator.addListener(mAnimatorListener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
|
|
|
super.onSizeChanged(w, h, oldw, oldh);
|
|
|
|
|
mViewWidth = w;
|
|
|
|
|
mViewHeight = h;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onDraw(Canvas canvas) {
|
|
|
|
|
super.onDraw(canvas);
|
|
|
|
|
|
|
|
|
|
drawSearch(canvas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void drawSearch(Canvas canvas) {
|
|
|
|
|
|
|
|
|
|
mPaint.setColor(Color.WHITE);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
canvas.translate(mViewWidth / 2, mViewHeight / 2);
|
|
|
|
|
|
|
|
|
|
canvas.drawColor(Color.parseColor("#0082D7"));
|
|
|
|
|
|
|
|
|
|
switch (mCurrentState) {
|
|
|
|
|
case NONE:
|
|
|
|
|
canvas.drawPath(path_srarch, mPaint);
|
|
|
|
|
break;
|
|
|
|
|
case STARTING:
|
|
|
|
|
mMeasure.setPath(path_srarch, false);
|
|
|
|
|
Path dst = new Path();
|
|
|
|
|
mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dst, true);
|
|
|
|
|
canvas.drawPath(dst, mPaint);
|
|
|
|
|
break;
|
|
|
|
|
case SEARCHING:
|
|
|
|
|
mMeasure.setPath(path_circle, false);
|
|
|
|
|
Path dst2 = new Path();
|
|
|
|
|
float stop = mMeasure.getLength() * mAnimatorValue;
|
|
|
|
|
float start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * 200f));
|
|
|
|
|
mMeasure.getSegment(start, stop, dst2, true);
|
|
|
|
|
canvas.drawPath(dst2, mPaint);
|
|
|
|
|
break;
|
|
|
|
|
case ENDING:
|
|
|
|
|
mMeasure.setPath(path_srarch, false);
|
|
|
|
|
Path dst3 = new Path();
|
|
|
|
|
mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dst3, true);
|
|
|
|
|
canvas.drawPath(dst3, mPaint);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|