Search code examples
androidandroid-layoutandroid-custom-view

Why view.onLayout() is called repetitively when changed=false?


From the official docs:

Android Docs - View

protected void onLayout (boolean changed, int left, int top, int right, int bottom)

Parameters
changed This is a new size or position for this view
left    Left position, relative to parent
top     Top position, relative to parent
right   Right position, relative to parent
bottom  Bottom position, relative to parent 

Why my custom view is called repetitively with changed=false? Is it normal?

Here's the call tree:

Synoptic.onLayout(boolean, int, int, int, int) line: 130    
Synoptic(View).layout(int, int, int, int) line: 11282   
Synoptic(ViewGroup).layout(int, int, int, int) line: 4224   
RelativeLayout.onLayout(boolean, int, int, int, int) line: 925  
RelativeLayout(View).layout(int, int, int, int) line: 11282 
RelativeLayout(ViewGroup).layout(int, int, int, int) line: 4224 
LinearLayout.setChildFrame(View, int, int, int, int) line: 1628 
LinearLayout.layoutHorizontal() line: 1617  
LinearLayout.onLayout(boolean, int, int, int, int) line: 1401   
LinearLayout(View).layout(int, int, int, int) line: 11282   
LinearLayout(ViewGroup).layout(int, int, int, int) line: 4224   
SwipeView(FrameLayout).onLayout(boolean, int, int, int, int) line: 431  
SwipeView(HorizontalScrollView).onLayout(boolean, int, int, int, int) line: 1382    
SwipeView.onLayout(boolean, int, int, int, int) line: 154   
SwipeView(View).layout(int, int, int, int) line: 11282  
SwipeView(ViewGroup).layout(int, int, int, int) line: 4224  
LinearLayout.setChildFrame(View, int, int, int, int) line: 1628 
LinearLayout.layoutVertical() line: 1486    
LinearLayout.onLayout(boolean, int, int, int, int) line: 1399   
LinearLayout(View).layout(int, int, int, int) line: 11282   
LinearLayout(ViewGroup).layout(int, int, int, int) line: 4224   
FrameLayout.onLayout(boolean, int, int, int, int) line: 431 
FrameLayout(View).layout(int, int, int, int) line: 11282    
FrameLayout(ViewGroup).layout(int, int, int, int) line: 4224    
LinearLayout.setChildFrame(View, int, int, int, int) line: 1628 
LinearLayout.layoutVertical() line: 1486    
LinearLayout.onLayout(boolean, int, int, int, int) line: 1399   
LinearLayout(View).layout(int, int, int, int) line: 11282   
LinearLayout(ViewGroup).layout(int, int, int, int) line: 4224   
PhoneWindow$DecorView(FrameLayout).onLayout(boolean, int, int, int, int) line: 431  
PhoneWindow$DecorView(View).layout(int, int, int, int) line: 11282  
PhoneWindow$DecorView(ViewGroup).layout(int, int, int, int) line: 4224  
ViewRootImpl.performTraversals() line: 1514 
ViewRootImpl.handleMessage(Message) line: 2467  
ViewRootImpl(Handler).dispatchMessage(Message) line: 99 
Looper.loop() line: 137 
ActivityThread.main(String[]) line: 4424    
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]  
Method.invoke(Object, Object...) line: 511  
ZygoteInit$MethodAndArgsCaller.run() line: 784  
ZygoteInit.main(String[]) line: 551 
NativeStart.main(String[]) line: not available [native method]  

Solution

  • I found the problem: for a compound component like mine, that contains a TextView, the repetitive call to .setText() causes a call to the parent's onLayout()

    ...even if the new text is equal to the current TextView text!

    so I solved:

    public void setTextViewText(String text) {
        if (!this.textViewValue.getText().equals(text)) {
            this.textViewValue.setText(text);
        }
    }