• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
onGlobalFocusChanged(View oldFocus, View newFocus)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          */
onGlobalLayout()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          */
onPreDraw()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          */
onTouchModeChanged(boolean isInTouchMode)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          */
onScrollChanged()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          */
setTouchableInsets(int val)171         public void setTouchableInsets(int val) {
172             mTouchableInsets = val;
173         }
174 
getTouchableInsets()175         public int getTouchableInsets() {
176             return mTouchableInsets;
177         }
178 
179         int mTouchableInsets;
180 
reset()181         void reset() {
182             contentInsets.setEmpty();
183             visibleInsets.setEmpty();
184             touchableRegion.setEmpty();
185             mTouchableInsets = TOUCHABLE_INSETS_FRAME;
186         }
187 
equals(Object o)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 
set(InternalInsetsInfo other)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          */
onComputeInternalInsets(InternalInsetsInfo inoutInfo)234         public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
235     }
236 
237     /**
238      * Creates a new ViewTreeObserver. This constructor should not be called
239      */
ViewTreeObserver()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      */
merge(ViewTreeObserver observer)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      */
addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener)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      */
removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim)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      */
addOnGlobalLayoutListener(OnGlobalLayoutListener listener)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      */
removeGlobalOnLayoutListener(OnGlobalLayoutListener victim)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      */
addOnPreDrawListener(OnPreDrawListener listener)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      */
removeOnPreDrawListener(OnPreDrawListener victim)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      */
addOnScrollChangedListener(OnScrollChangedListener listener)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      */
removeOnScrollChangedListener(OnScrollChangedListener victim)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      */
addOnTouchModeChangeListener(OnTouchModeChangeListener listener)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      */
removeOnTouchModeChangeListener(OnTouchModeChangeListener victim)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      */
addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener)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      */
removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim)499     public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
500         checkIsAlive();
501         if (mOnComputeInternalInsetsListeners == null) {
502             return;
503         }
504         mOnComputeInternalInsetsListeners.remove(victim);
505     }
506 
checkIsAlive()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      */
isAlive()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      */
kill()533     private void kill() {
534         mAlive = false;
535     }
536 
537     /**
538      * Notifies registered listeners that focus has changed.
539      */
dispatchOnGlobalFocusChange(View oldFocus, View newFocus)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      */
dispatchOnGlobalLayout()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      */
dispatchOnPreDraw()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      */
dispatchOnTouchModeChanged(boolean inTouchMode)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      */
dispatchOnScrollChanged()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      */
hasComputeInternalInsetsListeners()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      */
dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo)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