我正在编写一个应用程序,其唯一目的是试图了解Android中的 View 层次结构是如何工作的。我现在遇到一些非常棘手的问题。在这里,我将尽量简化我的解释。
设置:
目前我有三个观点。 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
中,我只显示如下 View :TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(myView);
问题:
onDraw(Canvas canvas)
方法。我已经看到了几种解决方案,说我的 View 没有任何尺寸(height/width = 0),但是事实并非如此。当我覆盖onLayout()
时,我得到了布局的尺寸,它们是正确的。另外,getHeight()/Width()完全符合其应有的状态。我还可以覆盖dispatchDraw()
并获取基本 View 以进行绘制。 TestB
中的对象设置动画。传统上,我会覆盖自身调用onDraw()
上的invalidate()
方法,直到对象完成它应该做的动画为止。但是,在TestB
中,当我调用invalidate()
时, View 永远不会重绘。我的印象是,再次调用onDraw()
方法是我的父 View 的工作,但是我的父 View 不再再次调用dispatchDraw()
。 我想我的问题是,为什么我的父 View 的
onDraw()
方法永远不会被调用?当其中一个 child 使自己无效时,应该调用父 View 中的哪些方法?家长是负责确保 child 被画成 child 的人吗?还是Android会照顾 child ?如果Android响应invalidate()
,为什么我的TestB
再也不会被绘制? 最佳答案
好的,经过一些研究和大量尝试,我发现我在问题2上做错了三件事。其中很多是简单的答案,但不是很明显。
onMeasure()
。在onMeasure()
中,您需要为中包含的每个子级调用的子级,并传递该子级所需的MeasureSpec。 measure()
。最初,我只是创建了一个 View 对象并直接使用它。这使我可以绘制一次,但是 View 不包含在 View 树中,因此当我调用ViewGroup
时,它并没有使 View 树无效且没有重新绘制。例如:在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);
}
这将绘制一次 subview 。但是,如果该 View 需要更新动画或其他内容,就可以了。我将
addView()
放入TestA的构造函数中,以将其添加到 View 树中。最终代码如下: 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);
}
另外,如果要绘制更多的子级,可以像这样重写
invalidate()
,但是我需要在每个图形之间添加一些自定义元素,例如网格线或其他东西。@Override
protectd void dispatchDraw(Canvas canvas){
int childCount = getChildCount();
for(int i = 0; i < size; i++)
{
drawCustomElement();
getChildAt(i).draw(canvas);
}
}
addView(childView)
(无论如何,它都是dispatchDraw(Canvas canvas)
中的抽象内容,因此还是必须的)。在此方法中,必须为每个 child 调用onLayout()
。即使完成了前两件事,我的 View 也不会失效。我这样做后,一切都变得非常完美。 更新:
问题1已解决。另一个极其简单但不太明显的解决方案。
当我创建
ViewGroup
实例时,我必须调用layout
,否则出于优化原因,Android将不会绘制它。因此完整的设置是:TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
myView.setWillNotDraw(false);
setContentView(myView);