6

我正在编写一个应用程序,其唯一目的是试图了解 Android 中的视图层次结构是如何工作的。我现在遇到了一些非常严重的问题。我将在此处的解释中尽量简洁。

设置:

目前我有三个视图。2 是ViewGroups,1 只是一个View. 假设它们按以下顺序排列:

    TestA extends ViewGroup
    TestB extends ViewGroup
    TestC extends View
    TestA->TestB->TestC
    Where TestC is in TestB and TestB is in TestA.

在我的Activity我只是像这样显示视图:

TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(myView);

问题:

  1. onDraw(Canvas canvas)TestA的方法永远不会被调用。我已经看到了一些解决方案,即我的视图没有任何尺寸(高度/宽度 = 0),但是事实并非如此。当我覆盖时onLayout(),我得到了布局的尺寸并且它们是正确的。此外,getHeight()/Width() 完全符合它们应有的状态。我也可以覆盖dispatchDraw()并让我的基本视图自己绘制。

  2. 我想在TestB. 传统上,我会覆盖onDraw()调用invalidate()自身的方法,直到对象完成它应该做的动画。但是,TestB当我调用视图时invalidate(),视图永远不会重绘。我的印象是再次调用该方法是我的父视图的工作onDraw(),但我的父视图不会再次调用该方法dispatchDraw()

我想我的问题是,为什么onDraw()我的父视图方法永远不会被调用?当其中一个孩子使自己失效时,应该调用我父母视图中的哪些方法?父母是负责确保孩子被吸引的人,还是Android会处理这个问题?如果 Android 响应invalidate(),为什么我TestB再也不会被绘制?

4

2 回答 2

5

好的,经过一些研究和大量尝试,我发现我在问题 2 方面做错了三件事。很多都是简单的答案,但不是很明显。

  • 您需要覆盖onMeasure()每个视图。在onMeasure()中,您需要调用包含在MeasureSpec 中传递的每个孩子需要的measure()孩子ViewGroup

  • 您需要为您想要包括addView()每个孩子打电话。最初,我只是简单地创建了一个视图对象并直接使用它。这允许我绘制一次,但视图不包含在视图树中,因此当我调用invalidate()它时它并没有使视图树无效并且没有重绘。例如:

在测试A中:

 TestB childView;
 TestA(Context context){
   ****** setup code *******
   LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
   childView = new TestB(context);
   childView.setLayoutParams(params);
 }

 @Override
 protected void dispatchDraw(Canvas canvas){
   childView.draw(canvas);
 }

这将绘制一次子视图。但是,如果该视图需要更新动画或其他内容,就是这样。我放入addView(childView)TestA 的构造函数以将其添加到视图树中。最终代码如下:

 TestB childView;
 TestA(Context context){
   ****** setup code *******
   LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
   childView = new TestB(context);
   childView.setLayoutParams(params);
   addView(childView);
 }

 @Override
 protected void dispatchDraw(Canvas canvas){
   childView.draw(canvas);
 }

dispatchDraw(Canvas canvas)或者,如果我有更多的孩子要绘制,我可以像这样覆盖,但是我需要在每个绘图之间添加一些自定义元素,例如网格线或其他东西。

@Override
protectd void dispatchDraw(Canvas canvas){
   int childCount = getChildCount();
   for(int i = 0; i < size; i++)
   {
       drawCustomElement();
       getChildAt(i).draw(canvas);
   }
}
  • 您必须覆盖onLayout()(无论如何它都是抽象的ViewGroup,所以无论如何它都是必需的)。在此方法中,您必须调用layout每个孩子。即使做了前两件事,我的观点也不会失效。一旦我这样做了,一切都完美无缺。

更新: 问题 #1 已解决。另一个非常简单但不那么明显的解决方案。

当我创建 的实例时TestA,我必须调用setWillNotDraw(false),否则出于优化原因,Android 不会绘制它。所以完整的设置是:

TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
myView.setWillNotDraw(false);
setContentView(myView);
于 2011-09-16T14:57:08.463 回答
2

这不是一个直接的答案,但这是一个关于如何绘制自定义视图的精彩教程,并提供了如何为视图设置动画的精彩速成课程。代码也非常简单和干净。

希望这可以帮助!

于 2011-09-15T21:36:41.223 回答