Android中如何让MyView绘制的六边形沿指定路径实现位移动画
核心问题定位
直接拆解你当前代码里导致动画不生效的硬伤:
- 自定义的
setStartX/setStartY仅做了变量赋值,完全没有触发View的重绘流程,就算动画系统改了x/y的值,页面也不会刷新 - onDraw方法里绘制六边形的所有坐标,完全没有用到x/y这两个动态变量,哪怕x/y改对了,绘制位置也不会变
- 自定义的
getX()方法直接覆盖了View原生的同名方法,会导致整个视图的坐标计算混乱 - 定义路径时多写了冗余的
moveTo,把连续轨迹拆成了孤立的线段,动画路径不连贯 - ObjectAnimator对自定义属性的匹配逻辑有前提要求,你现在的setter不满足触发条件
分步修改方案
1. 补全setter的重绘触发逻辑
View要刷新必须主动调用invalidate(),把原来的setter方法改成赋值后立刻请求重绘的形式:
public void setStartX(float ex) { x = ex; invalidate(); } public void setStartY(float ey) { y = ey; invalidate(); }
注意:建议把自定义的x、y对应的属性名改成不冲突的名称,比如hexX、hexY,对应的setter/getter写成setHexX()、getHexX(),避免和View系统自带的getX()/setX()方法重名,从根源上避免坐标计算混乱。
2. 修正onDraw的坐标映射逻辑
你现在的六边形绘制逻辑是基于自身画布的相对坐标,必须在绘制前把画布平移到动态更新的x/y位置,才能让图形跟着坐标移动:
@Override protected void onDraw(Canvas canvas) { Paint myBrush = this.getBrush(); float comp = myBrush.getStrokeWidth()/2; float canWidth = getSize(); float canHeight = getSize(); // 保存当前画布状态,平移到动态坐标位置 canvas.save(); canvas.translate(x, y); // 你原来的六边形绘制代码不需要任何修改 canvas.drawLine(canWidth/3, 0+comp, 2*canWidth/3, 0+comp, myBrush); canvas.drawLine(2*canWidth/3, 0+comp, canWidth-comp, canHeight/3, myBrush); canvas.drawLine(canWidth-comp, canHeight/3, canWidth-comp, 2*canHeight/3, myBrush); canvas.drawLine(canWidth-comp, 2*canHeight/3, 2*canWidth/3, canHeight-comp, myBrush); canvas.drawLine(2*canWidth/3, canHeight-comp, canWidth/3, canHeight-comp, myBrush); canvas.drawLine(canWidth/3, canHeight-comp, 0+comp, 2*canHeight/3, myBrush); canvas.drawLine(0+comp, 2*canHeight/3, 0+comp, canHeight/3, myBrush); canvas.drawLine(0+comp, canHeight/3, canWidth/3, 0+comp, myBrush); // 恢复画布状态,避免影响其他绘制逻辑 canvas.restore(); }
3. 修正路径定义的冗余逻辑
你原来的路径写了两次moveTo,打断了连续的竖线轨迹,删掉多余的moveTo即可:
Path hexPath = new Path(); hexPath.moveTo(10, 10); // 仅在路径起点调用一次 hexPath.lineTo(10, PHONE_DIMS.y * 4/5); // 连续绘制到底部
4. 匹配ObjectAnimator的属性要求
ObjectAnimator.ofFloat(target, "属性1", "属性2", path)这个构造方法会自动插值算出路径上的每一个x、y值,传给对应的setter方法。如果按照之前的建议把属性名改成不冲突的hexX、hexY,对应的调用逻辑就是:
// 匹配setHexX、setHexY方法 ObjectAnimator hexAn = ObjectAnimator.ofFloat(hex, "hexX", "hexY", hexPath); hexAn.setInterpolator(new OvershootInterpolator()); hexAn.setDuration(5000); hexAn.start();
如果需要兼容Android 5.0以下的低版本,可以通过PropertyValuesHolder配合PathInterpolator实现同样效果,不需要额外重写复杂逻辑。
关键注意事项
- 所有和动画绑定的自定义属性,setter方法必须在赋值后调用
invalidate(),否则动画系统就算修改了属性值,视图也不会重绘,看起来就像动画完全没生效 - 路径绘制时
moveTo只需要在轨迹最开始调用一次,后续连续的轨迹点直接用lineTo连接即可,多余的moveTo会把路径拆成孤立的节点,导致动画跳变 - 不要让自定义的方法和View系统内置方法重名,比如你现在的
getX()完全覆盖了系统方法,会导致布局测量、触摸事件的坐标计算全部出错
内容的提问来源于stack exchange,提问作者Jon Walzer
相关产品推荐
相关产品推荐

