上面效果是使用Canvas实现的一个搜索效果,涉及到三个部分的绘制:搜索放大镜的圆、放大镜的手柄、线条,根据属性动画执行的多少及移动的距离进行绘制;
1、extends View 并重写onDraw方法
public class MySearchView extends View{
private Paint mPaint;
private BaseController mController;
public MySearchView(Context context) {
this(context,null);
}
public MySearchView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MySearchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.RED);
}
public void setController(BaseController controller){
this.mController=controller;
mController.setSearchView(this);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mController.draw(canvas,mPaint);
}
/**
* 开启动画
*/
public void startAnimation(){
if(mController!=null){
mController.startAnim();
}
}
/**
* 重置动画
*/
public void resetAnimation(){
if(mController!=null){
mController.resetAnim();
}
}
}
在这里类里面会发现只有一个Paint画笔,开启动画,重置动画的方法,正真onDraw绘制的时候是在BaseController里面进行绘制;
public abstract class BaseController {
private MySearchView mySearchView;
public static final int STATE_ANIM_NONE = 0;
public static final int STATE_ANIM_START = 1;
public static final int STATE_ANIM_STOP = 2;
public static final int DEFAULT_ANIM_TIME = 5000;
public static final float DEFAULT_ANIM_STARTF = 0;
public static final float DEFAULT_ANIM_ENDF = 1;
public int mState = STATE_ANIM_NONE;
private AnimatorListenter animatorListenter;
/**
* 开始动画
*/
public void startAnim(){
}
/**
* 重置动画
*/
public void resetAnim(){
}
/**
* 绘制
* @param canvas
* @param paint
*/
public abstract void draw(Canvas canvas, Paint paint);
public void setSearchView(MySearchView mySearchView){
this.mySearchView=mySearchView;
}
/**
* 获取宽度
* @return 返回MySearchView的宽度
*/
public int getWidth(){
return mySearchView.getWidth();
}
/**
* 获取高度
* @return 返回MySearchView的高度
*/
public int getHeight(){
return mySearchView.getHeight();
}
public float mpro=-1;
/**
* 设置动画效果
* @return 设置好的ValueAnimator
*/
public ValueAnimator startViewAnimation(){
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(800l);
//设置动画插值器
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mpro = (float) animation.getAnimatedValue();
mySearchView.invalidate();
}
});
//监听动画完成
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(animatorListenter!=null){
animatorListenter.animatorListenter();
}
}
});
valueAnimator.start();
mpro=0;
return valueAnimator;
}
public void addAnimatorFishListener(AnimatorListenter listenter){
this.animatorListenter=listenter;
}
}
这里的BaseController是一个抽象类,onDraw方法也是抽象方法,子类必须要实现,这里主要对ValueAnimator进行设置已经监听回调;真正的绘制动作还是在BaseController子类中完成;
public class SearchController extends BaseController{
private RectF mRectF;
private String mColor="#4CAF50";
private int cx,cy,cr;
private int j=15;
public SearchController(){
mRectF=new RectF();
}
@Override
public void draw(Canvas canvas, Paint paint) {
canvas.drawColor(Color.parseColor(mColor));
switch (mState){
case STATE_ANIM_NONE:
drawNormalView(canvas,paint);
break;
case STATE_ANIM_START:
drawStartAnimView(canvas,paint);
break;
case STATE_ANIM_STOP:
drawStopAnimView(canvas,paint);
break;
}
}
/**
* 绘制停止动画view
* @param canvas
* @param paint
*/
private void drawStopAnimView(Canvas canvas, Paint paint) {
}
/**
* 绘制开启动画的view
* @param canvas
* @param paint
*/
private void drawStartAnimView(Canvas canvas, Paint paint) {
canvas.save();
if(mpro<=0.5f){
/**
* -360 ~ 0 需要变换的范围
* 0 ~ 0.5 mpro实际的变化范围
* 转换公式:360*(mpro*2-1),
*/
//绘制圆和把手
canvas.drawArc(
mRectF,
45,
360*(mpro*2-1),
false,
paint
);
canvas.drawLine(
mRectF.right - j,
mRectF.bottom - j,
mRectF.right+cr-j,
mRectF.bottom+cr-j,
paint
);
}else{
/**
* 0 ~ 1 需要变换的范围
* 0.5 ~ 1 mpro实际的变化范围
* 转换公式:(mpro*2-1),
*/
//绘制把手
canvas.drawLine(
mRectF.right - j + cr*(mpro*2-1),
mRectF.bottom - j + cr*(mpro*2-1),
mRectF.right - j + cr,
mRectF.bottom+cr-j,
paint);
}
//绘制下面的横线
canvas.drawLine(
(mRectF.right - j + cr)*(1-mpro*0.8f),
mRectF.bottom+cr-j,
mRectF.right - j + cr,
mRectF.bottom+cr-j,
paint);
canvas.restore();
mRectF.left = cx -cr + mpro*250;
mRectF.right = cx + cr +mpro*250;
mRectF.top = cy - cr;
mRectF.bottom = cy + cr;
}
/**
* 绘制普通view
* @param canvas
* @param paint
*/
private void drawNormalView(Canvas canvas, Paint paint) {
cr=getWidth()/20;
cx=getWidth()/2;
cy=getHeight()/2;
mRectF.left=cx-cr;
mRectF.right=cx+cr;
mRectF.top=cy-cr;
mRectF.bottom=cy+cr;
//保存画布
canvas.save();
//重置画笔
paint.reset();
//设置抗锯齿
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
//将画布选择45度
canvas.rotate(45,cx,cy);
//绘制线条
canvas.drawLine(cx+cr,cy,cx+cr*2,cy,paint);
canvas.drawArc(
mRectF,
0,
360,
false,
paint
);
canvas.restore();
}
@Override
public void startAnim() {
super.startAnim();
mState = STATE_ANIM_START;
startViewAnimation();
}
@Override
public void resetAnim() {
super.resetAnim();
mState = STATE_ANIM_STOP;
startViewAnimation();
}
@Override
public void addAnimatorFishListener(AnimatorListenter listenter) {
super.addAnimatorFishListener(listenter);
}
}
SearchController是BaseController的子类,在SearchController的onDraw方法中进行绘制,如果需要不同的效果,直接写一个新的类 extends BaseController,调用就可以了,并不需要去修改SearchController里面的代码,具体的代码中都有注释;
2、使用
public class MainActivity extends AppCompatActivity {
private MySearchView sv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sv = (MySearchView) findViewById(R.id.sv);
SearchController searchController=new SearchController();
sv.setController(searchController);
searchController.addAnimatorFishListener(new AnimatorListenter() {
@Override
public void animatorListenter() {
Toast.makeText(MainActivity.this,"执行完毕",Toast.LENGTH_LONG).show();
}
});
}
public void start(View v){
sv.startAnimation();
}
}
找到MySearchView,调用它的setController方法,将创建好的SearchController对象传入就可以了。