I've a custom ViewGroup, to which a custom ImageView is added as a child. Now, my problem is that whenever I change the image in the child, by overriding the onDraw(), the onLayout method in the parent is called. Is it not possible to just change the image in the child without calling the onLayout method in the Custom ViewGroup? Thanks.
Interestingly, no one answers this simple question.
The onLayout() method is called by default when the viewgroup needs to be redrawn.
see https://developer.android.com/guide/topics/ui/how-android-draws.html
you can actually extend the viewgroup to implement your custom onLayout() and onMeasure() method to control the behavior it is called.
Related
I have created a custom control which extends ViewGroup. Eventually, the implementation of the control is using only a canvas to draw some shapes on, without adding any Views.
Should onLayout be still overridden in that scenario with some sort of general implementation, or should I leave it an empty method?
Should onLayout be still overridden in that scenario
If you are extending ViewGroup then yes, you have to implement onLayout() even if it does nothing as it's abstract method. Alternatively extend i.e. FrameLayout instead of ViewGroup.
How would a general implementation be?
If you got no children to layout just keep empty method body.
EDIT
Yeah I meant if I should have some sort of a general implementation or just leave it empty
See docs:
void onLayout ()
Called from layout when this view should assign a
size and position to each of its children. Derived classes with
children should override this method and call layout on each of their
children.
There's no point for implementation if there's no use for it. There's no universal implementation that's why this method is abstract in ViewGroup. You got no childer then you not need to layout anything. Yet you need the
(empty) method due to extending abstract class.
A have a custom view. This view works on principle ListView, but all items draws on Canvas, so they are not are children of ViewGroup. Say more: my View is not a ViewGroup, it extends View. Now I want to use Adapter interface to provide data to my view via getView(...) method. But. I have been stucked with drawing issue. I trying to change my extending from View into ViewGroup and here is my confusing. onDraw() method is not called! I really want to avoid posting my code, that works under NDA, so I want to know - is the ViewGroup drawing logic not the same if we extends from View, not from ViewGroup?
that's the expected behavior, unless the flag setWillNotDraw is forced to false. ViewGroup are not expected to do any drawing on their own, but to delegate the drawing to their children. If you want to override this behavior, you can either call setWillNotDraw(false) or override dispatchDraw(Canvas canvas) instead of onDraw
I am new comer to the android and got stuck with this concept.
I have extended FrameLayout and added child views. My child view's onDraw is called even without setting setWillNotDraw(false). Then what is the actual need of it and when this flag should be set.
setWillNotDraw is not required in a View class.
However, it is usually set to true in a ViewGroup. Therefore, is disables the onDraw method there.
If you need the onDraw method to be called inside a ViewGroup, you just set the flag to false like so: setWillNotDraw(false).
Also, if you don't want that onDraw in your view's subclass to be called, you have to call subclassInstance.setWillNotDraw(true)
does adding a child to a ViewGroup (via ViewGroup.addView() ) automatically invoke onMeasure() for all childs?
ViewGroup should measure it's width and height by first measuring all children. If ViewGroup's width or height is wrap_content it will call onMeasure() for all children for sure. Can't say that about match_parent. One thing for sure that addView() calls requestLayout() which will re-calculate layout children's position call layout(l, t, r, b) on all children.
To know for sure if it calls you can create a custom View and log every call to onMeasure() and add those to a ViewGroup. Could be that different ViewGroup implementation will act differently.
I'm a bit confused about the roles of forceLayout(), requestLayout() and invalidate() methods of the View class.
When shall they be called?
To better understand answers provided by François BOURLIEUX and Dalvik I suggest you take a look at this awesome view lifecycle diagram by Arpit Mathur:
invalidate()
Calling invalidate() is done when you want to schedule a redraw of the view. It will result in onDraw being called eventually (soon, but not immediately). An example of when a custom view would call it is when a text or background color property has changed.
The view will be redrawn but the size will not change.
requestLayout()
If something about your view changes that will affect the size, then you should call requestLayout(). This will trigger onMeasure and onLayout not only for this view but all the way up the line for the parent views.
Calling requestLayout() is not guaranteed to result in an onDraw (contrary to what the diagram in the accepted answer implies), so it is usually combined with invalidate().
invalidate();
requestLayout();
An example of this is when a custom label has its text property changed. The label would change size and thus need to be remeasured and redrawn.
forceLayout()
When there is a requestLayout() that is called on a parent view group, it does not necessary need to remeasure and relayout its child views. However, if a child should be included in the remeasure and relayout, then you can call forceLayout() on the child. forceLayout() only works on a child if it occurs in conjunction with a requestLayout() on its direct parent. Calling forceLayout() by itself will have no effect since it does not trigger a requestLayout() up the view tree.
Read this Q&A for a more detailed description of forceLayout().
Further study
Creating a View Class: Add Properties and Events (helpful docs)
View documentation
View source code
Here you can find some response:
http://developer.android.com/guide/topics/ui/how-android-draws.html
For me a call to invalidate() only refreshes the view and a call to requestLayout() refreshes the view and compute the size of the view on the screen.
invalidate() ---> onDraw() from UI thread
postInvalidate() ---> onDraw() from background thread
requestLayout() ---> onMeasure() and onLayout() AND NOT Necessarily onDraw()
IMPORTANT: Calling this method does not affect the called class's child.
forceLayout() ---> onMeasure() and onLayout() JUST IF the direct parent called requestLayout().
you use invalidate() on a view that you want to redraw, it'll make its onDraw(Canvas c) to invoked, and requestLayout() will make the whole layout rendering ( measurement phase and positioning phase) run again. You should use it if you are changing child view's size on runtime but only in particular cases like constraints from the parent view(by that I mean that the parent height or width are WRAP_CONTENT and so match measure the children before they can wrap them again)
This answer is not correct about forceLayout().
As you can see in the code of forceLayout() it merely marks the view as "needs a relayout" but it does neither schedule nor trigger that relayout. The relayout will not happen until at some point in the future the view's parent was laid out for some other reason.
There is also a much bigger issue when using forceLayout() and requestLayout():
Let's say you've called forceLayout() on a view. Now when calling requestLayout() on a descendent of that view, Android will recursively call requestLayout() on that descendent's ancestors. The problem is that it will stop the recursion at the view on which you've called forceLayout(). So the requestLayout() call will never reach the view root and thus never schedule a layout pass. An entire subtree of the view hierarchy is waiting for a layout and calling requestLayout() on any view of that subtree will not cause a layout. Only calling requestLayout() on any view outside that subtree will break the spell.
I'd consider the implementation of forceLayout() (and how it affects requestLayout() to be broken and you should never use that function in your code.