Trying to understand the hierarchy of views

I am writing an application for the sole purpose of trying to understand how the presentation hierarchy works in Android. Now I have some serious problems. I will try to be brief in my explanations here.

Setup:

I currently have three views. 2 ViewGroups , and 1 is just a View . Say they are in this order:

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

In my Activity I just show the following views:

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

Problems:

  • The onDraw(Canvas canvas) TestA method is never called. I saw a couple of solutions to this phrase saying that my view has no dimensions (height / width = 0), however this is not so. When I override onLayout() , I get the dimensions of my layout and they are correct. In addition, getHeight () / Width () is exactly what they should be. I can also override dispatchDraw() and get my basic representations for drawing.

  • I want to animate an object in TestB . Traditionally, I redefined the onDraw() method when calling invalidate() on its own until the object finished the animation that it was supposed to do. However, in TestB , when I call invalidate() , the view is never redrawn. I get the impression that the work of my parent view again calls the onDraw() method, but the parent view again does not call dispatchDraw() .

I think my questions are: why will my onDraw() method of my parent view never be called for a start? What methods in my parent view should be called when one of its children is invalid? Is the parent responsible for ensuring that the children are painted or Android takes care of this? If Android responds to invalidate() , why won't my TestB be drawn anymore?

+6
source share
2 answers

Well, after some research and many attempts, I found that I was doing three things wrong with respect to Problem 2. Many of them were simple answers, but not very obvious.

  • You need to override onMeasure() for each view. Within onMeasure() you need to call measure() for each child contained in the ViewGroup passing in the MeasureSpec that the child needs.

  • You need to call addView() for each child you want to include. Initially, I just created a view object and used it directly. This allowed me to draw it once, but the view was not included in the view tree, so when I called invalidate() it did not invalidate the view tree, not redraw it. For instance:

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

This will draw the child view once. However, if this view needs to be updated for animation or something else, that is. I put addView(childView) in the TestA constructor to add it to the view tree. The final code is as follows:

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

Alternatively, I could redefine dispatchDraw(Canvas canvas) so that I would have many more children, but I need some kind of custom element between each drawing, like grid lines or something like that.

 @Override protectd void dispatchDraw(Canvas canvas){ int childCount = getChildCount(); for(int i = 0; i < size; i++) { drawCustomElement(); getChildAt(i).draw(canvas); } } 
  • You must override onLayout() (this is abstract in the ViewGroup , one way or another, as it was required). As part of this method, you must call layout for each child. Even after the first two things, my views will not be invalidated. As soon as I did this, everything worked perfectly.

UPDATE: Problem number 1 solved. Another extremely simple, but not so obvious solution.

When I create an instance of TestA , I have to call setWillNotDraw(false) , otherwise Android will not draw it for optimization reasons. Thus, the full setup:

 TestA myView = new TestA(context); myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)); myView.setWillNotDraw(false); setContentView(myView); 
+5
source

This is not a direct answer, but here is a fantastic tutorial on how to draw custom views and gives great help on how to animate the presentation. The code is very simple and clean.

Hope this helps!

+2
source

Source: https://habr.com/ru/post/897411/


All Articles