返回
彻底解决Android小红点在控件动画下的绘制问题
Android
2024-02-17 01:07:59
前言
在上一篇文章中,我们介绍了一种通过在父控件绘制前景的方式实现小红点,并在布局文件中配置标记控件来为任意子控件添加小红点的方案。这个方案的优点是实现简单,只需在布局文件中配置一个带小红点控件的 id,就能为任意子控件添加小红点。然而,这个方案存在一个漏洞,当子控件执行动画,即子控件尺寸发生变化时,小红点不会联动更新。
问题分析
要解决这个问题,我们需要理解父控件绘制前景的过程。在父控件的 onDraw() 方法中,会遍历子控件并调用它们的 draw() 方法。在这个过程中,子控件的绘制事件被父控件拦截,而父控件会根据子控件的坐标和尺寸在其右上角绘制圆圈。
当子控件执行动画时,它的尺寸和坐标会发生变化。然而,父控件的 onDraw() 方法并不会立即更新子控件的坐标和尺寸,而是等到下一次布局或绘制事件发生时才会更新。因此,在子控件执行动画期间,父控件绘制的小红点位置是不准确的。
解决方案
为了解决这个问题,我们需要一种方法来强制父控件在子控件执行动画时立即更新其坐标和尺寸。一种可行的方法是在父控件中拦截子控件的绘制事件,并在每次子控件绘制之前更新其坐标和尺寸。
具体实现步骤如下:
- 在父控件的 onLayout() 方法中,遍历子控件并记录它们的坐标和尺寸。
- 在父控件的 onDraw() 方法中,遍历子控件并调用它们的 draw() 方法。
- 在调用子控件的 draw() 方法之前,使用子控件的最新坐标和尺寸更新父控件的 onLayout() 方法中记录的坐标和尺寸。
- 调用子控件的 draw() 方法绘制子控件。
- 在子控件的动画过程中,每当子控件的尺寸或坐标发生变化时,调用父控件的 requestLayout() 方法触发父控件的 onLayout() 方法,更新子控件的坐标和尺寸。
通过这种方式,父控件可以始终保持子控件的最新坐标和尺寸,并在子控件执行动画时立即更新小红点的绘制位置。
示例代码
以下示例代码展示了如何实现这种解决方案:
public class MyParentView extends ViewGroup {
private Map<View, Rect> childCoordinates = new HashMap<>();
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
childCoordinates.put(child, new Rect(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()));
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
// 更新子控件的坐标和尺寸
Rect childCoordinate = childCoordinates.get(child);
childCoordinate.left = child.getLeft();
childCoordinate.top = child.getTop();
childCoordinate.right = child.getRight();
childCoordinate.bottom = child.getBottom();
// 绘制子控件
child.draw(canvas);
// 绘制小红点
canvas.drawCircle(childCoordinate.right, childCoordinate.top, 10, Paint.RED);
}
}
}
总结
通过在父控件中拦截子控件的绘制事件并更新其坐标和尺寸,我们可以解决 Android 自定义控件小红点在控件执行动画时绘制不准确的问题。这种解决方案确保小红点始终保持准确定位,无论子控件执行何种动画。