试图了解视图层次结构

时间:2011-09-15 18:04:53

标签: android android-layout android-view

我正在编写一个应用程序,其唯一目的是尝试了解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. 永远不会调用TestA的onDraw(Canvas canvas)方法。我已经看到了一些解决方案,说我的视图没有任何尺寸(高度/宽度= 0),但事实并非如此。当我覆盖onLayout()时,我会得到布局的尺寸并且它们是正确的。此外,getHeight()/ Width()完全符合它们的要求。我也可以覆盖dispatchDraw()并让我的基本视图自我绘制。

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

  3. 我想我的问题是,为什么我父视图的onDraw()方法永远不会被调用开始?我的父视图中的哪些方法应该在其中一个孩子使自己无效时被调用?父母是负责确保孩子被吸引还是Android负责的人?如果Android响应invalidate(),为什么我的TestB永远不会再次被提取?

2 个答案:

答案 0 :(得分:5)

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

  • 您需要为每个视图覆盖onMeasure()。在onMeasure()内,您需要致电measure(),以获取{strong}中包含在ViewGroup中的孩子所需的MeasureSpec中包含的每个子项。

  • 您需要为{strong>要包含的每个孩子致电addView()。最初,我只是创建了一个视图对象并直接使用它。这允许我绘制一次,但视图不包含在视图树中,因此当我调用invalidate()时,它不会使视图树无效而不重绘。例如:

在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);
 }

 @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);

答案 1 :(得分:2)

这不是一个直接的答案,而是here is a fantastic tutorial关于如何绘制自定义视图并在如何为视图设置动画方面给出了很好的崩溃。代码也非常简单和干净。

希望这有帮助!