Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view;
     18 
     19 import android.graphics.Rect;
     20 import android.graphics.Region;
     21 
     22 import java.util.ArrayList;
     23 import java.util.concurrent.CopyOnWriteArrayList;
     24 
     25 /**
     26  * A view tree observer is used to register listeners that can be notified of global
     27  * changes in the view tree. Such global events include, but are not limited to,
     28  * layout of the whole tree, beginning of the drawing pass, touch mode change....
     29  *
     30  * A ViewTreeObserver should never be instantiated by applications as it is provided
     31  * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()}
     32  * for more information.
     33  */
     34 public final class ViewTreeObserver {
     35     private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
     36     private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
     37     private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
     38     private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
     39     private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
     40     private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
     41 
     42     private boolean mAlive = true;
     43 
     44     /**
     45      * Interface definition for a callback to be invoked when the focus state within
     46      * the view tree changes.
     47      */
     48     public interface OnGlobalFocusChangeListener {
     49         /**
     50          * Callback method to be invoked when the focus changes in the view tree. When
     51          * the view tree transitions from touch mode to non-touch mode, oldFocus is null.
     52          * When the view tree transitions from non-touch mode to touch mode, newFocus is
     53          * null. When focus changes in non-touch mode (without transition from or to
     54          * touch mode) either oldFocus or newFocus can be null.
     55          *
     56          * @param oldFocus The previously focused view, if any.
     57          * @param newFocus The newly focused View, if any.
     58          */
     59         public void onGlobalFocusChanged(View oldFocus, View newFocus);
     60     }
     61 
     62     /**
     63      * Interface definition for a callback to be invoked when the global layout state
     64      * or the visibility of views within the view tree changes.
     65      */
     66     public interface OnGlobalLayoutListener {
     67         /**
     68          * Callback method to be invoked when the global layout state or the visibility of views
     69          * within the view tree changes
     70          */
     71         public void onGlobalLayout();
     72     }
     73 
     74     /**
     75      * Interface definition for a callback to be invoked when the view tree is about to be drawn.
     76      */
     77     public interface OnPreDrawListener {
     78         /**
     79          * Callback method to be invoked when the view tree is about to be drawn. At this point, all
     80          * views in the tree have been measured and given a frame. Clients can use this to adjust
     81          * their scroll bounds or even to request a new layout before drawing occurs.
     82          *
     83          * @return Return true to proceed with the current drawing pass, or false to cancel.
     84          *
     85          * @see android.view.View#onMeasure
     86          * @see android.view.View#onLayout
     87          * @see android.view.View#onDraw
     88          */
     89         public boolean onPreDraw();
     90     }
     91 
     92     /**
     93      * Interface definition for a callback to be invoked when the touch mode changes.
     94      */
     95     public interface OnTouchModeChangeListener {
     96         /**
     97          * Callback method to be invoked when the touch mode changes.
     98          *
     99          * @param isInTouchMode True if the view hierarchy is now in touch mode, false  otherwise.
    100          */
    101         public void onTouchModeChanged(boolean isInTouchMode);
    102     }
    103 
    104     /**
    105      * Interface definition for a callback to be invoked when
    106      * something in the view tree has been scrolled.
    107      */
    108     public interface OnScrollChangedListener {
    109         /**
    110          * Callback method to be invoked when something in the view tree
    111          * has been scrolled.
    112          */
    113         public void onScrollChanged();
    114     }
    115 
    116     /**
    117      * Parameters used with OnComputeInternalInsetsListener.
    118      *
    119      * We are not yet ready to commit to this API and support it, so
    120      * @hide
    121      */
    122     public final static class InternalInsetsInfo {
    123         /**
    124          * Offsets from the frame of the window at which the content of
    125          * windows behind it should be placed.
    126          */
    127         public final Rect contentInsets = new Rect();
    128 
    129         /**
    130          * Offsets from the frame of the window at which windows behind it
    131          * are visible.
    132          */
    133         public final Rect visibleInsets = new Rect();
    134 
    135         /**
    136          * Touchable region defined relative to the origin of the frame of the window.
    137          * Only used when {@link #setTouchableInsets(int)} is called with
    138          * the option {@link #TOUCHABLE_INSETS_REGION}.
    139          */
    140         public final Region touchableRegion = new Region();
    141 
    142         /**
    143          * Option for {@link #setTouchableInsets(int)}: the entire window frame
    144          * can be touched.
    145          */
    146         public static final int TOUCHABLE_INSETS_FRAME = 0;
    147 
    148         /**
    149          * Option for {@link #setTouchableInsets(int)}: the area inside of
    150          * the content insets can be touched.
    151          */
    152         public static final int TOUCHABLE_INSETS_CONTENT = 1;
    153 
    154         /**
    155          * Option for {@link #setTouchableInsets(int)}: the area inside of
    156          * the visible insets can be touched.
    157          */
    158         public static final int TOUCHABLE_INSETS_VISIBLE = 2;
    159 
    160         /**
    161          * Option for {@link #setTouchableInsets(int)}: the area inside of
    162          * the provided touchable region in {@link #touchableRegion} can be touched.
    163          */
    164         public static final int TOUCHABLE_INSETS_REGION = 3;
    165 
    166         /**
    167          * Set which parts of the window can be touched: either
    168          * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
    169          * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
    170          */
    171         public void setTouchableInsets(int val) {
    172             mTouchableInsets = val;
    173         }
    174 
    175         public int getTouchableInsets() {
    176             return mTouchableInsets;
    177         }
    178 
    179         int mTouchableInsets;
    180 
    181         void reset() {
    182             contentInsets.setEmpty();
    183             visibleInsets.setEmpty();
    184             touchableRegion.setEmpty();
    185             mTouchableInsets = TOUCHABLE_INSETS_FRAME;
    186         }
    187 
    188         @Override public boolean equals(Object o) {
    189             try {
    190                 if (o == null) {
    191                     return false;
    192                 }
    193                 InternalInsetsInfo other = (InternalInsetsInfo)o;
    194                 if (mTouchableInsets != other.mTouchableInsets) {
    195                     return false;
    196                 }
    197                 if (!contentInsets.equals(other.contentInsets)) {
    198                     return false;
    199                 }
    200                 if (!visibleInsets.equals(other.visibleInsets)) {
    201                     return false;
    202                 }
    203                 return touchableRegion.equals(other.touchableRegion);
    204             } catch (ClassCastException e) {
    205                 return false;
    206             }
    207         }
    208 
    209         void set(InternalInsetsInfo other) {
    210             contentInsets.set(other.contentInsets);
    211             visibleInsets.set(other.visibleInsets);
    212             touchableRegion.set(other.touchableRegion);
    213             mTouchableInsets = other.mTouchableInsets;
    214         }
    215     }
    216 
    217     /**
    218      * Interface definition for a callback to be invoked when layout has
    219      * completed and the client can compute its interior insets.
    220      *
    221      * We are not yet ready to commit to this API and support it, so
    222      * @hide
    223      */
    224     public interface OnComputeInternalInsetsListener {
    225         /**
    226          * Callback method to be invoked when layout has completed and the
    227          * client can compute its interior insets.
    228          *
    229          * @param inoutInfo Should be filled in by the implementation with
    230          * the information about the insets of the window.  This is called
    231          * with whatever values the previous OnComputeInternalInsetsListener
    232          * returned, if there are multiple such listeners in the window.
    233          */
    234         public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
    235     }
    236 
    237     /**
    238      * Creates a new ViewTreeObserver. This constructor should not be called
    239      */
    240     ViewTreeObserver() {
    241     }
    242 
    243     /**
    244      * Merges all the listeners registered on the specified observer with the listeners
    245      * registered on this object. After this method is invoked, the specified observer
    246      * will return false in {@link #isAlive()} and should not be used anymore.
    247      *
    248      * @param observer The ViewTreeObserver whose listeners must be added to this observer
    249      */
    250     void merge(ViewTreeObserver observer) {
    251         if (observer.mOnGlobalFocusListeners != null) {
    252             if (mOnGlobalFocusListeners != null) {
    253                 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
    254             } else {
    255                 mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
    256             }
    257         }
    258 
    259         if (observer.mOnGlobalLayoutListeners != null) {
    260             if (mOnGlobalLayoutListeners != null) {
    261                 mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
    262             } else {
    263                 mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
    264             }
    265         }
    266 
    267         if (observer.mOnPreDrawListeners != null) {
    268             if (mOnPreDrawListeners != null) {
    269                 mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
    270             } else {
    271                 mOnPreDrawListeners = observer.mOnPreDrawListeners;
    272             }
    273         }
    274 
    275         if (observer.mOnTouchModeChangeListeners != null) {
    276             if (mOnTouchModeChangeListeners != null) {
    277                 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
    278             } else {
    279                 mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
    280             }
    281         }
    282 
    283         if (observer.mOnComputeInternalInsetsListeners != null) {
    284             if (mOnComputeInternalInsetsListeners != null) {
    285                 mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
    286             } else {
    287                 mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
    288             }
    289         }
    290 
    291         observer.kill();
    292     }
    293 
    294     /**
    295      * Register a callback to be invoked when the focus state within the view tree changes.
    296      *
    297      * @param listener The callback to add
    298      *
    299      * @throws IllegalStateException If {@link #isAlive()} returns false
    300      */
    301     public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) {
    302         checkIsAlive();
    303 
    304         if (mOnGlobalFocusListeners == null) {
    305             mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
    306         }
    307 
    308         mOnGlobalFocusListeners.add(listener);
    309     }
    310 
    311     /**
    312      * Remove a previously installed focus change callback.
    313      *
    314      * @param victim The callback to remove
    315      *
    316      * @throws IllegalStateException If {@link #isAlive()} returns false
    317      *
    318      * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener)
    319      */
    320     public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) {
    321         checkIsAlive();
    322         if (mOnGlobalFocusListeners == null) {
    323             return;
    324         }
    325         mOnGlobalFocusListeners.remove(victim);
    326     }
    327 
    328     /**
    329      * Register a callback to be invoked when the global layout state or the visibility of views
    330      * within the view tree changes
    331      *
    332      * @param listener The callback to add
    333      *
    334      * @throws IllegalStateException If {@link #isAlive()} returns false
    335      */
    336     public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
    337         checkIsAlive();
    338 
    339         if (mOnGlobalLayoutListeners == null) {
    340             mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>();
    341         }
    342 
    343         mOnGlobalLayoutListeners.add(listener);
    344     }
    345 
    346     /**
    347      * Remove a previously installed global layout callback
    348      *
    349      * @param victim The callback to remove
    350      *
    351      * @throws IllegalStateException If {@link #isAlive()} returns false
    352      *
    353      * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
    354      */
    355     public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
    356         checkIsAlive();
    357         if (mOnGlobalLayoutListeners == null) {
    358             return;
    359         }
    360         mOnGlobalLayoutListeners.remove(victim);
    361     }
    362 
    363     /**
    364      * Register a callback to be invoked when the view tree is about to be drawn
    365      *
    366      * @param listener The callback to add
    367      *
    368      * @throws IllegalStateException If {@link #isAlive()} returns false
    369      */
    370     public void addOnPreDrawListener(OnPreDrawListener listener) {
    371         checkIsAlive();
    372 
    373         if (mOnPreDrawListeners == null) {
    374             mOnPreDrawListeners = new ArrayList<OnPreDrawListener>();
    375         }
    376 
    377         mOnPreDrawListeners.add(listener);
    378     }
    379 
    380     /**
    381      * Remove a previously installed pre-draw callback
    382      *
    383      * @param victim The callback to remove
    384      *
    385      * @throws IllegalStateException If {@link #isAlive()} returns false
    386      *
    387      * @see #addOnPreDrawListener(OnPreDrawListener)
    388      */
    389     public void removeOnPreDrawListener(OnPreDrawListener victim) {
    390         checkIsAlive();
    391         if (mOnPreDrawListeners == null) {
    392             return;
    393         }
    394         mOnPreDrawListeners.remove(victim);
    395     }
    396 
    397     /**
    398      * Register a callback to be invoked when a view has been scrolled.
    399      *
    400      * @param listener The callback to add
    401      *
    402      * @throws IllegalStateException If {@link #isAlive()} returns false
    403      */
    404     public void addOnScrollChangedListener(OnScrollChangedListener listener) {
    405         checkIsAlive();
    406 
    407         if (mOnScrollChangedListeners == null) {
    408             mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>();
    409         }
    410 
    411         mOnScrollChangedListeners.add(listener);
    412     }
    413 
    414     /**
    415      * Remove a previously installed scroll-changed callback
    416      *
    417      * @param victim The callback to remove
    418      *
    419      * @throws IllegalStateException If {@link #isAlive()} returns false
    420      *
    421      * @see #addOnScrollChangedListener(OnScrollChangedListener)
    422      */
    423     public void removeOnScrollChangedListener(OnScrollChangedListener victim) {
    424         checkIsAlive();
    425         if (mOnScrollChangedListeners == null) {
    426             return;
    427         }
    428         mOnScrollChangedListeners.remove(victim);
    429     }
    430 
    431     /**
    432      * Register a callback to be invoked when the invoked when the touch mode changes.
    433      *
    434      * @param listener The callback to add
    435      *
    436      * @throws IllegalStateException If {@link #isAlive()} returns false
    437      */
    438     public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
    439         checkIsAlive();
    440 
    441         if (mOnTouchModeChangeListeners == null) {
    442             mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
    443         }
    444 
    445         mOnTouchModeChangeListeners.add(listener);
    446     }
    447 
    448     /**
    449      * Remove a previously installed touch mode change callback
    450      *
    451      * @param victim The callback to remove
    452      *
    453      * @throws IllegalStateException If {@link #isAlive()} returns false
    454      *
    455      * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener)
    456      */
    457     public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) {
    458         checkIsAlive();
    459         if (mOnTouchModeChangeListeners == null) {
    460             return;
    461         }
    462         mOnTouchModeChangeListeners.remove(victim);
    463     }
    464 
    465     /**
    466      * Register a callback to be invoked when the invoked when it is time to
    467      * compute the window's internal insets.
    468      *
    469      * @param listener The callback to add
    470      *
    471      * @throws IllegalStateException If {@link #isAlive()} returns false
    472      *
    473      * We are not yet ready to commit to this API and support it, so
    474      * @hide
    475      */
    476     public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) {
    477         checkIsAlive();
    478 
    479         if (mOnComputeInternalInsetsListeners == null) {
    480             mOnComputeInternalInsetsListeners =
    481                     new CopyOnWriteArrayList<OnComputeInternalInsetsListener>();
    482         }
    483 
    484         mOnComputeInternalInsetsListeners.add(listener);
    485     }
    486 
    487     /**
    488      * Remove a previously installed internal insets computation callback
    489      *
    490      * @param victim The callback to remove
    491      *
    492      * @throws IllegalStateException If {@link #isAlive()} returns false
    493      *
    494      * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener)
    495      *
    496      * We are not yet ready to commit to this API and support it, so
    497      * @hide
    498      */
    499     public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
    500         checkIsAlive();
    501         if (mOnComputeInternalInsetsListeners == null) {
    502             return;
    503         }
    504         mOnComputeInternalInsetsListeners.remove(victim);
    505     }
    506 
    507     private void checkIsAlive() {
    508         if (!mAlive) {
    509             throw new IllegalStateException("This ViewTreeObserver is not alive, call "
    510                     + "getViewTreeObserver() again");
    511         }
    512     }
    513 
    514     /**
    515      * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
    516      * any call to a method (except this one) will throw an exception.
    517      *
    518      * If an application keeps a long-lived reference to this ViewTreeObserver, it should
    519      * always check for the result of this method before calling any other method.
    520      *
    521      * @return True if this object is alive and be used, false otherwise.
    522      */
    523     public boolean isAlive() {
    524         return mAlive;
    525     }
    526 
    527     /**
    528      * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
    529      * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
    530      *
    531      * @hide
    532      */
    533     private void kill() {
    534         mAlive = false;
    535     }
    536 
    537     /**
    538      * Notifies registered listeners that focus has changed.
    539      */
    540     final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
    541         // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
    542         // perform the dispatching. The iterator is a safe guard against listeners that
    543         // could mutate the list by calling the various add/remove methods. This prevents
    544         // the array from being modified while we iterate it.
    545         final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
    546         if (listeners != null && listeners.size() > 0) {
    547             for (OnGlobalFocusChangeListener listener : listeners) {
    548                 listener.onGlobalFocusChanged(oldFocus, newFocus);
    549             }
    550         }
    551     }
    552 
    553     /**
    554      * Notifies registered listeners that a global layout happened. This can be called
    555      * manually if you are forcing a layout on a View or a hierarchy of Views that are
    556      * not attached to a Window or in the GONE state.
    557      */
    558     public final void dispatchOnGlobalLayout() {
    559         // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
    560         // perform the dispatching. The iterator is a safe guard against listeners that
    561         // could mutate the list by calling the various add/remove methods. This prevents
    562         // the array from being modified while we iterate it.
    563         final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
    564         if (listeners != null && listeners.size() > 0) {
    565             for (OnGlobalLayoutListener listener : listeners) {
    566                 listener.onGlobalLayout();
    567             }
    568         }
    569     }
    570 
    571     /**
    572      * Notifies registered listeners that the drawing pass is about to start. If a
    573      * listener returns true, then the drawing pass is canceled and rescheduled. This can
    574      * be called manually if you are forcing the drawing on a View or a hierarchy of Views
    575      * that are not attached to a Window or in the GONE state.
    576      *
    577      * @return True if the current draw should be canceled and resceduled, false otherwise.
    578      */
    579     public final boolean dispatchOnPreDraw() {
    580         // NOTE: we *must* clone the listener list to perform the dispatching.
    581         // The clone is a safe guard against listeners that
    582         // could mutate the list by calling the various add/remove methods. This prevents
    583         // the array from being modified while we process it.
    584         boolean cancelDraw = false;
    585         if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) {
    586             final ArrayList<OnPreDrawListener> listeners =
    587                     (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone();
    588             int numListeners = listeners.size();
    589             for (int i = 0; i < numListeners; ++i) {
    590                 cancelDraw |= !(listeners.get(i).onPreDraw());
    591             }
    592         }
    593         return cancelDraw;
    594     }
    595 
    596     /**
    597      * Notifies registered listeners that the touch mode has changed.
    598      *
    599      * @param inTouchMode True if the touch mode is now enabled, false otherwise.
    600      */
    601     final void dispatchOnTouchModeChanged(boolean inTouchMode) {
    602         final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
    603                 mOnTouchModeChangeListeners;
    604         if (listeners != null && listeners.size() > 0) {
    605             for (OnTouchModeChangeListener listener : listeners) {
    606                 listener.onTouchModeChanged(inTouchMode);
    607             }
    608         }
    609     }
    610 
    611     /**
    612      * Notifies registered listeners that something has scrolled.
    613      */
    614     final void dispatchOnScrollChanged() {
    615         // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
    616         // perform the dispatching. The iterator is a safe guard against listeners that
    617         // could mutate the list by calling the various add/remove methods. This prevents
    618         // the array from being modified while we iterate it.
    619         final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
    620         if (listeners != null && listeners.size() > 0) {
    621             for (OnScrollChangedListener listener : listeners) {
    622                 listener.onScrollChanged();
    623             }
    624         }
    625     }
    626 
    627     /**
    628      * Returns whether there are listeners for computing internal insets.
    629      */
    630     final boolean hasComputeInternalInsetsListeners() {
    631         final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
    632                 mOnComputeInternalInsetsListeners;
    633         return (listeners != null && listeners.size() > 0);
    634     }
    635 
    636     /**
    637      * Calls all listeners to compute the current insets.
    638      */
    639     final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
    640         // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
    641         // perform the dispatching. The iterator is a safe guard against listeners that
    642         // could mutate the list by calling the various add/remove methods. This prevents
    643         // the array from being modified while we iterate it.
    644         final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
    645                 mOnComputeInternalInsetsListeners;
    646         if (listeners != null && listeners.size() > 0) {
    647             for (OnComputeInternalInsetsListener listener : listeners) {
    648                 listener.onComputeInternalInsets(inoutInfo);
    649             }
    650         }
    651     }
    652 }
    653