本文由 Luzhuo 编写,转发请保留该信息.
原文: https://blog.csdn.net/rozol/article/details/79730582
绘制动画, 由Android的绘画功能 + 属性动画 组成的一种动画
主要方法
valueAnimator.addUpdateListener(AnimatorUpdateListener) // 监听动画数值更新
估值器
ValueAnimator.ofObject(new TypeEvaluator<PointF>() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
// fraction(时间因子[0,1]), startValue(开始值), endValue(结束值)
return null; // 返回计算结果值
}
});
波浪动画
原理: 通过动画计算的数值, 不断将波浪右移.
代码
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
mPath.moveTo(-waveLength + offset, centerY);
// 绘制2个贝塞尔曲线
for (int i = 0; i < waveCount; i++){
mPath.quadTo( - waveLength * 3 / 4 + i * waveLength + offset, centerY + 60, - waveLength / 2 + i * waveLength + offset, centerY);
mPath.quadTo( - waveLength / 4 + i * waveLength + offset, centerY - 60, i * waveLength + offset, centerY);
}
// 封闭波浪
mPath.lineTo(screenWidth, screenHeight);
mPath.lineTo(0, screenHeight);
mPath.close();
canvas.drawPath(mPath, mPaintBezier);
}
public void onClick(View v) {
// 设置属性动画
valueAnimator = valueAnimator.ofInt(0, waveLength);
valueAnimator.setDuration(1000);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
offset = (int) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
}
效果
轨迹动画
-
原理: 使用估值器计算出动画执行时间点的坐标结果, 然后将黑色小球按该坐标结果绘制
-
代码
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(movePointX, movePointY, 20, paintCircle);
}
public void onClick(View v) {
// 运动轨迹
BezierEvaluator evaluator = new BezierEvaluator(new PointF(flagPointX, flagPointY));
ValueAnimator animator = ValueAnimator.ofObject(evaluator,
new PointF(startPointX, startPointY), new PointF(endPointX, endPointY));
animator.setDuration(600);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
movePointX = (int) pointF.x;
movePointY = (int) pointF.y;
invalidate();
}
});
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
public class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF flagPoint;
public BezierEvaluator(PointF flagPoint){
this.flagPoint = flagPoint;
}
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
// fraction(时间因子[0,1]), startValue(开始值), endValue(结束值)
return BezierUtil.getCalculateBezierPointForQuadratic(fraction, startValue, flagPoint, endValue);
}
}
/**
* 获取二阶贝塞尔曲线点的坐标
* B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1]
*
* @param t 曲线长度比例
* @param p0 起始点
* @param p1 控制点
* @param p2 终止点
* @return t对应的点
*/
public static PointF getCalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
PointF point = new PointF();
float temp = 1 - t;
point.x = temp * temp * p0.x + 2 * t * temp * p1.x + t * t * p2.x;
point.y = temp * temp * p0.y + 2 * t * temp * p1.y + t * t * p2.y;
return point;
}
效果
PathMeasure (Path测量类)
基本使用
public void onclick(View view){
Bitmap copybitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888); // 白纸
Canvas canvas = new Canvas(copybitmap); // 画布
Paint paint = new Paint(); // 画笔
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
Path dst = new Path();
path.addCircle(300, 300, 200, Path.Direction.CW);
PathMeasure pm;
// 测量Path的类
pm= new PathMeasure();
// pm = new PathMeasure(path, true);
pm.setPath(path, true); // 设置path
float length = pm.getLength(); // 获取Path长度
// 截取片段 (参数:起始截取位置, 结束截取位置, 截取的path添加到dst, 是否从起点开始截) 返回:true存入dst中
boolean segment = pm.getSegment(0 + 100, length - 100, dst, true);
// 选择下个path
boolean next = pm.nextContour();
// 获取指定位置的 坐标 和 该坐标的正切值 (参数:位置, 坐标, 正切值) 返回:true将数据存入 pos 和 tan 中,
float[] pos = new float[2], tan = new float[2];
boolean postan = pm.getPosTan(300, pos, tan);
// 获取指定位置的 坐标 和 该坐标的矩阵 (参数:位置, 矩阵, 将什么存入矩阵[POSITION_MATRIX_FLAG(坐标) / TANGENT_MATRIX_FLAG(正切)]) 返回:true将数据存入 matrix 中
Matrix matrix = new Matrix();
boolean m = pm.getMatrix(300, matrix, PathMeasure.TANGENT_MATRIX_FLAG);
// 如果清除dst里的内容, 修改后加上该代码, 用以解决 Android 4.4- 开启硬件加速的bug
// dst.reset();
// dst.lineTo(0, 0);
// 记算路径上某点的切线的角度(Math.atan2(纵坐标, 横坐标))
float degree = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);
canvas.drawPath(dst, paint);
iv.setImageBitmap(copybitmap);
}
案例: 缓冲圆圈
-
实现
public PathView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
path = new Path();
dst = new Path();
// 画一个圆, 关联pathMeasure
path.addCircle(400, 400, 100, Path.Direction.CW);
pathMeasure = new PathMeasure(); // 测量Path的类
pathMeasure.setPath(path, true);
length = pathMeasure.getLength(); // 路径长度
// 创建属性动画
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
animValue = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
animator.start();
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
dst.reset();
dst.lineTo(0, 0); // 解决硬件加速的bug
float stop = length * animValue;
float start = (float) (stop - ((0.5 - Math.abs(animValue - 0.5)) * length));
// 从起点开始截取, 路径将越来越长
pathMeasure.getSegment(start, stop, dst, true); // 截取整个path的任何片段(开始长度 / 结束长度 / 保存截取的路径 / 是否从起点开始截取)
canvas.drawPath(dst, paint);
}
上述主要使用pathMeasure.getSegment(start, stop, dst, true);
对path进行截取, 然后进行重绘; 除此之外还可以使用Path虚线类, 改变起始偏移量实现类似截取的效果, 由于是通过起始偏移量实现的, 所以只能从头部开始.
public void onAnimationUpdate(ValueAnimator valueAnimator) {
animValue = (float) valueAnimator.getAnimatedValue();
// 初始化路径风格(float intervals[]:实现的长度, float phase:起始偏移量)
effect = new DashPathEffect(new float[]{length, length}, length * animValue);
paint.setPathEffect(effect);
invalidate();
}
效果
————————————————
版权声明:本文为CSDN博主「LZ_Luzhuo」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Rozol/article/details/79730582