先看张动图:
要实现的效果清楚了,接下来分析实现过程:
动画过程分为两个阶段:
阶段一:圆弧不断减小,直至消失,同时向右位移
阶段二:停止向右位移,AB1线段长度减少,同时CB2线段长度增加
实现思路:
先画一个360度的圆弧,为了容易计算A、B1点坐标,先将画布逆时针旋转45度,再画弧度和AB1.
界面就变成:
圆心坐标(centerX, centerY)圆半径为Radius,则
A(centerX + radius, centerY)
B1(centerX + DH, centerY) DH为圆心距离B1的距离
然后把画布还原,计算B2、C的坐标
通过上图很容易计算出B2坐标(centerX + DH, centerY + DH)
这样静态的图片就绘制好了,如果要有动画效果,只需要加一些变量来修改对应点的坐标值即可。
private float l_centerX;//centerX坐标增加长度
private float l_AX;//A点X坐标增加的长度
private float l_AB1_Max;//AB1线段的最大长度
private float l_B2C;//B2_C线段长度
private float l_B2C_Max;//B2_C线段最大长度
关键点已经介绍完了,下面上全部代码:
package com.test.paintdemo.component;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* Created by ygdx_lk on 17/6/22.
*/
public class SearchView extends View {
private final Paint mPaint;//画笔
private float centerX, centerY;//圆心坐标
private final int RADIUS = 50;//圆半径
private final int DH = RADIUS * 2;//圆心距离底部线的长度
private final int DURATION = 1000;//动画执行时间
private RectF rectF;//圆弧的矩形区域
private float sweepAngle = -360;//圆弧角度
//画布旋转45度后的AB坐标
private float aX, aY;//线与圆弧的连接点A
private float b1X, b1Y;//线与线的连接点B1
//画布未旋转的BC坐标
private float b2X, b2Y;//底部线的右坐标点B2
private float cX, cY;//底部线的左坐标点C
private float l_centerX;//centerX坐标增加长度
private float l_AX;//A点X坐标增加的长度
private float l_AB1_Max;//AB1线段的最大长度
private float l_B2C;//B2_C线段长度
private float l_B2C_Max;//B2_C线段最大长度
private ValueAnimator oa; //动画 search -> line
private ValueAnimator oar;//动画 line -> search
public SearchView(Context context) {
this(context, null);
}
public SearchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setStrokeWidth(5);
mPaint.setAntiAlias(false);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLUE);
}
private void startAnim() {
l_centerX = 0;
l_AB1_Max = 0;
l_B2C_Max = 0;
l_AX = 0;
l_B2C = 0;
sweepAngle = 360;
b2X = 0;
b2Y = 0;
cX = 0;
cY = 0;
if(oa == null) {
oa = ObjectAnimator.ofFloat(0, 1);
oa.setDuration(DURATION);
oa.setInterpolator(new LinearInterpolator());
oa.setRepeatMode(ValueAnimator.REVERSE);
oa.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float frac = animation.getAnimatedFraction();
if (frac <= 0.5) { //0~0.5圆弧变化,圆心后移
l_centerX = RADIUS * frac * 2;//0~1
sweepAngle = 360 * (frac * 2 - 1);//-1~0
l_AX = 0;
l_B2C = 0;
} else {//0.5~1线段变化
sweepAngle = 0;
l_AX = (l_AB1_Max == 0 ? 3 * RADIUS : l_AB1_Max) * (frac * 2 - 1);//0~1
l_B2C = (l_B2C_Max > 0 ? l_B2C_Max : 5 * RADIUS) * (frac * 2 - 1);//0~1
}
invalidate();
}
});
}
oa.start();
}
private void startReverseAnim() {
if(oar == null) {
oar = ObjectAnimator.ofFloat(1, 0);
oar.setDuration(DURATION);
oar.setInterpolator(new LinearInterpolator());
oar.setRepeatMode(ValueAnimator.REVERSE);
oar.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float frac = 1 - animation.getAnimatedFraction();
if (frac <= 0.5) { //0~0.5圆弧变化,圆心后移
l_centerX = RADIUS * frac * 2;//0~1
sweepAngle = 360 * (frac * 2 - 1);//-1~0
l_AX = 0;
l_B2C = 0;
} else {//0.5~1线段变化
sweepAngle = 0;
l_AX = (l_AB1_Max == 0 ? 3 * RADIUS : l_AB1_Max) * (frac * 2 - 1);//0~1
l_B2C = (l_B2C_Max > 0 ? l_B2C_Max : 5 * RADIUS) * (frac * 2 - 1);//0~1
}
invalidate();
}
});
}
oar.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//计算圆心
centerX = getWidth() / 2f + l_centerX;
centerY = getHeight() / 2f - DH / 2;
//计算圆弧区域
rectF = new RectF();
rectF.left = centerX - RADIUS;
rectF.right = centerX + RADIUS;
rectF.top = centerY - RADIUS;
rectF.bottom = centerY + RADIUS;
//计算与圆弧相连的线段A_B1坐标(此处坐标计算基于旋转画布45度)
aX = centerX + RADIUS + l_AX;
aY = centerY;
b1X = (float) (centerX + Math.sqrt(2) * DH);
b1Y = centerY;
if(l_AX == 0){
l_AB1_Max = b1X - aX;
}
//计算B2_C点坐标
b2X = centerX + DH;
b2Y = centerY + DH;
cX = b2X - l_B2C;
cY = b2Y;
if(l_B2C == 0 && l_AX == 0){
l_B2C_Max = b2X * 2 - getWidth();
}
canvas.save();
//绘制圆弧度
canvas.rotate(45, centerX, centerY);
canvas.drawArc(rectF, 0, sweepAngle, false, mPaint);
//绘制线
canvas.drawLine(aX, aY, b1X, b1Y, mPaint);
canvas.restore();
if(sweepAngle == 0) {
canvas.drawLine(cX, cY, b2X, b2Y, mPaint);
}
}
public void Search() {
if(sweepAngle == -360) {
startAnim();
}
if(sweepAngle == 0){
startReverseAnim();
}
}
}