2011-09-15 98 views
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)方法是永远调用。我已经看到这个说法的一些解决方案,我的观点没有任何尺寸(高度/宽度= 0),但是,情况并非如此。当我覆盖onLayout()时,我得到了我的布局的尺寸,它们是正确的。另外,getHeight()/ Width()与它们应该完全相同。我也可以覆盖dispatchDraw()并获得我的基本观点来绘制自己。

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

我想我的问题是,为什么就永远也不会打电话给我我父视图onDraw()方法来开始?当我的父视图中的某个方法在其中一个孩子失效时应该调用什么方法?父母是否负责确保孩子被吸引,或者Android是否负责照顾?如果Android响应invalidate(),为什么我的TestB永远不会再被绘制?

回答

5

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

  • 您需要覆盖每个视图的onMeasure()。在onMeasure()之内,您需要拨打measure()获取子包含在ViewGroup传递给孩子需要的MeasureSpec。

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

在种皮:

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。即使做了前两件事,我的观点也不会失效。一旦我做到了这一点,一切工作都完美无缺。

UPDATE: 问题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); 
+0

你可以在你的博客中分享这个Viewgroup的演示项目吗?或者它会是非常有用的地方..谢谢你的伙伴.. – user1201239

+0

即使我们在XML中指定它,我是否需要添加childView? – user1201239

+0

对不起。我没有博客或任何要上传的内容。我会尽力尽可能地提供帮助。回答你的第一个问题,不。当你膨胀xml时,这个孩子被包含在'ViewGroup'中。最早可以检索它的是'onFinishInflate()'。之后,你有一个选择。或者通过'getChildAt(int index)'方法让你的孩子或者使用'findViewById(int id)'。索引将是孩子在xml中的顺序。我不知道索引是如何与儿童内的孩子一起工作的。 – DeeV

2

这不是一个直接的答案,但here is a fantastic tutorial关于如何绘制自定义视图,并给出了如何为视图设置动画的巨大碰撞。代码非常简单,也很干净。

希望这会有所帮助!

+0

谢谢。这并没有解决我的问题,但它确实有助于解决问题。 – DeeV