如何在重绘任何子视图时触发父视图的重绘?

时间:2011-10-13 11:22:19

标签: android user-interface android-layout android-widget

背景

我根据LinearLayout编写了一个自定义Android视图,我称之为ReflectingLayout。这个想法非常简单,可以在ReflectingLayout内声明的任何子视图下面呈现反射效果。 e.g ...

Something like this

我通过覆盖dispatchDraw()中的ReflectingLayout来实现此目的,如下所示:

@Override
protected void dispatchDraw(Canvas canvas) {

  Bitmap bitmap = Bitmap.createBitmap(...); 
  Canvas tempCanvas = new Canvas(bitmap);

  // Draw child views to bitmap using tempCanvas
  super.dispatchDraw(tempCanvas);

  ...
  // some tranformations performed on bitmap
  ...

  // draw transformed image on to main canvas
  canvas.drawBitmap(bitmap, 0, 0, null);
}

问题

如果我的ReflectingLayout包含TextView,并且我更改了TextView的文字内容,则反映效果不会更新。

我相信这是因为Android只会重绘TextView本身而不是所有父视图,这意味着我的被覆盖的dispatchDraw()方法不会被调用。

是否有任何简单的方法可以触发重绘父视图以响应其中任何子视图的更改?

特别是,如何在重新绘制子视图时重新绘制ReflectingLayout

我已经考虑过的事情

  • 我注意到在API级别11中添加了新的View.OnLayoutChangeListener。据推测,这可以用于将回调注册到父视图并触发重绘(?)。无论如何,我需要一个适用于API Level 7以上的解决方案。

  • 我可以简单地扩展TextView以及我想在ReflectingLayout内声明的任何其他View类,以便在重绘它们时使其父项无效,但这是一个笨重的解决方案。

任何帮助都很受欢迎(包括你认为整体方法是错误的)。

感谢。

2 个答案:

答案 0 :(得分:6)

在您的视图中使用getParent()方法怎么样?像这样:

if (getParent() instanceof View)
    ((View) getParent()).invalidate();

答案 1 :(得分:1)

我刚遇到这个问题。我发现这是由于3.0+中的新硬件加速支持。我解决它的方式是使用View#setLayerType并使用LAYER_TYPE_SOFTWARE。为了继续针对API级别8我使用反射来调用此方法来支持Android平台。这解决了这个问题,现在我的自定义ViewGroup在子项无效时正确绘制。

我不知道这是否能完全解决您的问题,但它可能会帮助那些也对此问题感到困惑的人。

反思代码,非常基本:

try {
    Method method =
            CustomViewGroup.class.getMethod("setLayerType", new Class[] { Integer.TYPE, Paint.class });
    // 0x1 == LAYER_TYPE_SOFTWARE
    method.invoke(this, new Object[] { Integer.valueOf(0x1), null });
} catch(SecurityException e) {
    Log.e(TAG, "reflectSetLayerType", e);
} catch(NoSuchMethodException e) { // don't care
} catch(IllegalArgumentException e) {
    Log.e(TAG, "reflectSetLayerType", e);
} catch(IllegalAccessException e) {
    Log.e(TAG, "reflectSetLayerType", e);
} catch(InvocationTargetException e) {
    Log.e(TAG, "reflectSetLayerType", e);
}