14

I'm learning about custom views and wanted to learn about invalidate() and requestLayout().

Please refer to this answer and its diagram:

https://stackoverflow.com/a/25846243/4243687

invalidate() tells Android that the state of the view has changed and needs to be re-drawn.

requestLayout() means the size of the view may have changed and needs to be remeasured, and then re-drawn.

invalidate() will invoke dispatchDraw(), draw(), and onDraw() hence it re-renders the view.

requestLayout() on the other hand does pretty much everything from measuring to re-rendering again.

Why do so many of the examples out there (even the TextView source code) call invalidate() and then requestLayout() right on the next line?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Ersen Osman
  • 7,067
  • 8
  • 47
  • 80

4 Answers4

17

invalidate() is used specifically for redrawing the content of your view. The redraw does not happen synchronously. Instead, it flags the region of your view as invalid so that it will be redrawn during the next render cycle.

requestLayout() should be used when something within it has possibly changed its dimensions. In this case, the parent view and all other parents up the view hierarchy will need to readjust themselves via a layout pass.

If you are not doing anything to your view that would change its size, then you do not have to call requestLayout().

If you go back and look at the places in the code for TextView where requestLayout() is being called, it will be on methods where the view's bounds will be affected. For example, setPadding(), setTypeface(), setCompoundDrawables(), etc.

So, when requestLayout() is called, it should be paired with a call to invalidate to ensure that the entire view is redrawn.

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Michael Krause
  • 4,689
  • 1
  • 21
  • 25
  • Thanks for the great explanation so invalidate() and requestLayout() are called together to re-draw our view and to also let the parent view group (which has our view as child) adjust itself is well to account for possible size changes to our view when we re-draw – Ersen Osman Feb 09 '16 at 22:19
  • 2
    I'm a little unclear as to why you would call `invalidate()` when also calling `requestLayout()`. It seems like `requestLayout()` will always cause your entire view to be redrawn; calling `invalidate()` in addition to this seems redundant. – pathfinderelite Sep 08 '16 at 12:32
  • @pathfinderelite imagine if you have a linear layout that is set to wrap content, it only goes as big as what is inside the linear layout. So imagine I have a text view, when I call setText() I must call invalidate() to render that new text so that it is shown. Now this text could make the TextView bigger so I must call requestLayout() to tell the linear layout (the parent that contains the text view) that hey you need to resize yourself so you can fit the textView that now has more text – Ersen Osman Sep 08 '16 at 21:48
  • @pathfinderelite note that the setText() method contains an invalidate() call inside it, I did not mean call setText() and invalidate() separately, sorry for the confusion! – Ersen Osman Sep 08 '16 at 21:57
  • @ErsenOsman if text changed but the size of text not change,they take same size area,now is invalidate will call? – chefish Sep 09 '16 at 05:56
  • 3
    @ErsenOsman _"I must call invalidate() to render that new text so that it is shown"_ My point is that this statement is not entirely true; calling `requestLayout()` (without `invalidate()`) will also render the new text. So, my question is, in a situation where `requestLayout()` _needs_ to be called, why would you _also_ call `invalidate()`? – pathfinderelite Sep 09 '16 at 10:46
  • @pathfinderelite It depends on your situation. requestLayout will render the new text (like invalidate()) but remember that requestLayout() tells the parent view and other parents up the view hierarchy to re-adjust themselves so that of course takes up more processing time. The example I gave is a situation where requestLayout needs to be called after invalidate. Sometimes we have to re-draw our view in which the view could get bigger therefore we have to call requestLayout to ensure the view hierarchy adjusts its bounds accordingly. – Ersen Osman Sep 10 '16 at 20:55
  • @chefish Apologies, but I do not understand what you mean, could you rephrase please. From my understanding, if you change something that affects the size of the textView (more text or text size) then that should be an invalidate and requestLayout. If it were to be a colour change then that would be a invalidate only – Ersen Osman Sep 10 '16 at 20:55
  • @ErsenOsman Yes,you are right.I have test it.If a TextView is wrap_content, invalidate and requestLayout will called if we call setText(...).And setTextColor will only call invalidate.But is there any case only requestLayout called? – chefish Sep 11 '16 at 14:45
  • 1
    @ErsenOsman I have the same question as pathfinderelite as well. I think the confusion here is would requestLayout() prompt a call to onDraw() after onMeasure() & onLayout(). If it does then why would we need to call invalidate() after requestLayout(). – boxme Dec 01 '16 at 19:09
  • 1
    @ErsenOsman `requestLayout()` does not trigger `onDraw()`. So you're correct. Link: http://grokbase.com/p/gg/android-developers/12amz5pqv8/does-an-ondraw-get-called-if-i-dont-do-invalidate-but-did-the-requestlayout – boxme Dec 01 '16 at 19:23
15

After seeing the following diagram, I was under the impression that calling requestLayout() would eventually result in an onDraw.

enter image description here

Therefore, there would be no need to call these together because it would be redundant.

invalidate();
requestLayout();

However, it turns out that that diagram is misleading. Some views might in fact invalidate themselves when there is a layout change, but this is not a certainty. Calling requestLayout() is not guaranteed to result in onDraw being called.

My source (thanks to this comment) is the Romain Guy (who is an Android engineer at Google):

requestLayout() itself does not lead to a draw pass but some views might react to a Layout change by calling invalidate.

Therefore, to be certain a relayout will result in a redraw, then you should pair an invalidate() with the requestLayout(). (The opposite is not true, though. If you only need a redraw, then there is no need to call requestLayout(). A single invalidate() will do.)

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • The article was written in 2012. Is statement _Calling requestLayout() is not guaranteed to result in onDraw being called_ valid in 2018? – Johnny Five Jul 18 '18 at 12:23
  • @JohnnyFive, I've seen no update to the source code or documentation that would suggest otherwise. So, yes, it is still valid. – Suragch Aug 19 '18 at 04:18
2

Relevant excerpt from the book Expert Android that answers the question:

Because the onClick event has caused the dimensions to change, our view needs to become bigger and take more space. How do we express that need to Android, Well, we request Layout(). This method goes up the chain, marking every view parent that it needs to be remeasured. When the final parent gets this request (the view root), the parent schedules a layout traversal. A layout traversal may or may not result in onDraw, although in this case it should. As a good programming practice, we also call invalidate() to ensure the drawing phase as well.

a-hegde
  • 31
  • 2
0

Android docs: Creating a View class

public boolean isShowText() {
   return mShowText;
}

public void setShowText(boolean showText) {
   mShowText = showText;
   invalidate();
   requestLayout();
}

Notice that setShowText calls invalidate() and requestLayout(). These calls are crucial to ensure that the view behaves reliably. You have to invalidate the view after any change to its properties that might change its appearance, so that the system knows that it needs to be redrawn. Likewise, you need to request a new layout if a property changes that might affect the size or shape of the view. Forgetting these method calls can cause hard-to-find bugs.

Sotti
  • 14,089
  • 2
  • 50
  • 43