2020import android .graphics .Region ;
2121
2222import java .util .ArrayList ;
23+ import java .util .concurrent .CopyOnWriteArrayList ;
2324
2425/**
2526 * A view tree observer is used to register listeners that can be notified of global
3132 * for more information.
3233 */
3334public final class ViewTreeObserver {
34- private CopyOnWriteArray <OnGlobalFocusChangeListener > mOnGlobalFocusListeners ;
35- private CopyOnWriteArray <OnGlobalLayoutListener > mOnGlobalLayoutListeners ;
36- private CopyOnWriteArray <OnTouchModeChangeListener > mOnTouchModeChangeListeners ;
37- private CopyOnWriteArray <OnComputeInternalInsetsListener > mOnComputeInternalInsetsListeners ;
38- private CopyOnWriteArray <OnScrollChangedListener > mOnScrollChangedListeners ;
39- private CopyOnWriteArray <OnPreDrawListener > mOnPreDrawListeners ;
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 ;
4041 private ArrayList <OnDrawListener > mOnDrawListeners ;
4142
4243 private boolean mAlive = true ;
@@ -146,7 +147,7 @@ public final static class InternalInsetsInfo {
146147 * windows behind it should be placed.
147148 */
148149 public final Rect contentInsets = new Rect ();
149-
150+
150151 /**
151152 * Offsets from the frame of the window at which windows behind it
152153 * are visible.
@@ -165,13 +166,13 @@ public final static class InternalInsetsInfo {
165166 * can be touched.
166167 */
167168 public static final int TOUCHABLE_INSETS_FRAME = 0 ;
168-
169+
169170 /**
170171 * Option for {@link #setTouchableInsets(int)}: the area inside of
171172 * the content insets can be touched.
172173 */
173174 public static final int TOUCHABLE_INSETS_CONTENT = 1 ;
174-
175+
175176 /**
176177 * Option for {@link #setTouchableInsets(int)}: the area inside of
177178 * the visible insets can be touched.
@@ -194,7 +195,7 @@ public void setTouchableInsets(int val) {
194195 }
195196
196197 int mTouchableInsets ;
197-
198+
198199 void reset () {
199200 contentInsets .setEmpty ();
200201 visibleInsets .setEmpty ();
@@ -230,7 +231,7 @@ void set(InternalInsetsInfo other) {
230231 mTouchableInsets = other .mTouchableInsets ;
231232 }
232233 }
233-
234+
234235 /**
235236 * Interface definition for a callback to be invoked when layout has
236237 * completed and the client can compute its interior insets.
@@ -327,7 +328,7 @@ public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener)
327328 checkIsAlive ();
328329
329330 if (mOnGlobalFocusListeners == null ) {
330- mOnGlobalFocusListeners = new CopyOnWriteArray <OnGlobalFocusChangeListener >();
331+ mOnGlobalFocusListeners = new CopyOnWriteArrayList <OnGlobalFocusChangeListener >();
331332 }
332333
333334 mOnGlobalFocusListeners .add (listener );
@@ -362,7 +363,7 @@ public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
362363 checkIsAlive ();
363364
364365 if (mOnGlobalLayoutListeners == null ) {
365- mOnGlobalLayoutListeners = new CopyOnWriteArray <OnGlobalLayoutListener >();
366+ mOnGlobalLayoutListeners = new CopyOnWriteArrayList <OnGlobalLayoutListener >();
366367 }
367368
368369 mOnGlobalLayoutListeners .add (listener );
@@ -412,7 +413,7 @@ public void addOnPreDrawListener(OnPreDrawListener listener) {
412413 checkIsAlive ();
413414
414415 if (mOnPreDrawListeners == null ) {
415- mOnPreDrawListeners = new CopyOnWriteArray <OnPreDrawListener >();
416+ mOnPreDrawListeners = new ArrayList <OnPreDrawListener >();
416417 }
417418
418419 mOnPreDrawListeners .add (listener );
@@ -484,7 +485,7 @@ public void addOnScrollChangedListener(OnScrollChangedListener listener) {
484485 checkIsAlive ();
485486
486487 if (mOnScrollChangedListeners == null ) {
487- mOnScrollChangedListeners = new CopyOnWriteArray <OnScrollChangedListener >();
488+ mOnScrollChangedListeners = new CopyOnWriteArrayList <OnScrollChangedListener >();
488489 }
489490
490491 mOnScrollChangedListeners .add (listener );
@@ -518,7 +519,7 @@ public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
518519 checkIsAlive ();
519520
520521 if (mOnTouchModeChangeListeners == null ) {
521- mOnTouchModeChangeListeners = new CopyOnWriteArray <OnTouchModeChangeListener >();
522+ mOnTouchModeChangeListeners = new CopyOnWriteArrayList <OnTouchModeChangeListener >();
522523 }
523524
524525 mOnTouchModeChangeListeners .add (listener );
@@ -557,7 +558,7 @@ public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener l
557558
558559 if (mOnComputeInternalInsetsListeners == null ) {
559560 mOnComputeInternalInsetsListeners =
560- new CopyOnWriteArray <OnComputeInternalInsetsListener >();
561+ new CopyOnWriteArrayList <OnComputeInternalInsetsListener >();
561562 }
562563
563564 mOnComputeInternalInsetsListeners .add (listener );
@@ -621,16 +622,10 @@ final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
621622 // perform the dispatching. The iterator is a safe guard against listeners that
622623 // could mutate the list by calling the various add/remove methods. This prevents
623624 // the array from being modified while we iterate it.
624- final CopyOnWriteArray <OnGlobalFocusChangeListener > listeners = mOnGlobalFocusListeners ;
625+ final CopyOnWriteArrayList <OnGlobalFocusChangeListener > listeners = mOnGlobalFocusListeners ;
625626 if (listeners != null && listeners .size () > 0 ) {
626- CopyOnWriteArray .Access <OnGlobalFocusChangeListener > access = listeners .start ();
627- try {
628- int count = access .size ();
629- for (int i = 0 ; i < count ; i ++) {
630- access .get (i ).onGlobalFocusChanged (oldFocus , newFocus );
631- }
632- } finally {
633- listeners .end ();
627+ for (OnGlobalFocusChangeListener listener : listeners ) {
628+ listener .onGlobalFocusChanged (oldFocus , newFocus );
634629 }
635630 }
636631 }
@@ -645,16 +640,10 @@ public final void dispatchOnGlobalLayout() {
645640 // perform the dispatching. The iterator is a safe guard against listeners that
646641 // could mutate the list by calling the various add/remove methods. This prevents
647642 // the array from being modified while we iterate it.
648- final CopyOnWriteArray <OnGlobalLayoutListener > listeners = mOnGlobalLayoutListeners ;
643+ final CopyOnWriteArrayList <OnGlobalLayoutListener > listeners = mOnGlobalLayoutListeners ;
649644 if (listeners != null && listeners .size () > 0 ) {
650- CopyOnWriteArray .Access <OnGlobalLayoutListener > access = listeners .start ();
651- try {
652- int count = access .size ();
653- for (int i = 0 ; i < count ; i ++) {
654- access .get (i ).onGlobalLayout ();
655- }
656- } finally {
657- listeners .end ();
645+ for (OnGlobalLayoutListener listener : listeners ) {
646+ listener .onGlobalLayout ();
658647 }
659648 }
660649 }
@@ -669,17 +658,17 @@ public final void dispatchOnGlobalLayout() {
669658 */
670659 @ SuppressWarnings ("unchecked" )
671660 public final boolean dispatchOnPreDraw () {
661+ // NOTE: we *must* clone the listener list to perform the dispatching.
662+ // The clone is a safe guard against listeners that
663+ // could mutate the list by calling the various add/remove methods. This prevents
664+ // the array from being modified while we process it.
672665 boolean cancelDraw = false ;
673- final CopyOnWriteArray <OnPreDrawListener > listeners = mOnPreDrawListeners ;
674- if (listeners != null && listeners .size () > 0 ) {
675- CopyOnWriteArray .Access <OnPreDrawListener > access = listeners .start ();
676- try {
677- int count = access .size ();
678- for (int i = 0 ; i < count ; i ++) {
679- cancelDraw |= !(access .get (i ).onPreDraw ());
680- }
681- } finally {
682- listeners .end ();
666+ if (mOnPreDrawListeners != null && mOnPreDrawListeners .size () > 0 ) {
667+ final ArrayList <OnPreDrawListener > listeners =
668+ (ArrayList <OnPreDrawListener >) mOnPreDrawListeners .clone ();
669+ int numListeners = listeners .size ();
670+ for (int i = 0 ; i < numListeners ; ++i ) {
671+ cancelDraw |= !(listeners .get (i ).onPreDraw ());
683672 }
684673 }
685674 return cancelDraw ;
@@ -704,17 +693,11 @@ public final void dispatchOnDraw() {
704693 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
705694 */
706695 final void dispatchOnTouchModeChanged (boolean inTouchMode ) {
707- final CopyOnWriteArray <OnTouchModeChangeListener > listeners =
696+ final CopyOnWriteArrayList <OnTouchModeChangeListener > listeners =
708697 mOnTouchModeChangeListeners ;
709698 if (listeners != null && listeners .size () > 0 ) {
710- CopyOnWriteArray .Access <OnTouchModeChangeListener > access = listeners .start ();
711- try {
712- int count = access .size ();
713- for (int i = 0 ; i < count ; i ++) {
714- access .get (i ).onTouchModeChanged (inTouchMode );
715- }
716- } finally {
717- listeners .end ();
699+ for (OnTouchModeChangeListener listener : listeners ) {
700+ listener .onTouchModeChanged (inTouchMode );
718701 }
719702 }
720703 }
@@ -727,16 +710,10 @@ final void dispatchOnScrollChanged() {
727710 // perform the dispatching. The iterator is a safe guard against listeners that
728711 // could mutate the list by calling the various add/remove methods. This prevents
729712 // the array from being modified while we iterate it.
730- final CopyOnWriteArray <OnScrollChangedListener > listeners = mOnScrollChangedListeners ;
713+ final CopyOnWriteArrayList <OnScrollChangedListener > listeners = mOnScrollChangedListeners ;
731714 if (listeners != null && listeners .size () > 0 ) {
732- CopyOnWriteArray .Access <OnScrollChangedListener > access = listeners .start ();
733- try {
734- int count = access .size ();
735- for (int i = 0 ; i < count ; i ++) {
736- access .get (i ).onScrollChanged ();
737- }
738- } finally {
739- listeners .end ();
715+ for (OnScrollChangedListener listener : listeners ) {
716+ listener .onScrollChanged ();
740717 }
741718 }
742719 }
@@ -745,11 +722,11 @@ final void dispatchOnScrollChanged() {
745722 * Returns whether there are listeners for computing internal insets.
746723 */
747724 final boolean hasComputeInternalInsetsListeners () {
748- final CopyOnWriteArray <OnComputeInternalInsetsListener > listeners =
725+ final CopyOnWriteArrayList <OnComputeInternalInsetsListener > listeners =
749726 mOnComputeInternalInsetsListeners ;
750727 return (listeners != null && listeners .size () > 0 );
751728 }
752-
729+
753730 /**
754731 * Calls all listeners to compute the current insets.
755732 */
@@ -758,105 +735,12 @@ final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
758735 // perform the dispatching. The iterator is a safe guard against listeners that
759736 // could mutate the list by calling the various add/remove methods. This prevents
760737 // the array from being modified while we iterate it.
761- final CopyOnWriteArray <OnComputeInternalInsetsListener > listeners =
738+ final CopyOnWriteArrayList <OnComputeInternalInsetsListener > listeners =
762739 mOnComputeInternalInsetsListeners ;
763740 if (listeners != null && listeners .size () > 0 ) {
764- CopyOnWriteArray .Access <OnComputeInternalInsetsListener > access = listeners .start ();
765- try {
766- int count = access .size ();
767- for (int i = 0 ; i < count ; i ++) {
768- access .get (i ).onComputeInternalInsets (inoutInfo );
769- }
770- } finally {
771- listeners .end ();
772- }
773- }
774- }
775-
776- /**
777- * Copy on write array. This array is not thread safe, and only one loop can
778- * iterate over this array at any given time. This class avoids allocations
779- * until a concurrent modification happens.
780- *
781- * Usage:
782- *
783- * CopyOnWriteArray.Access<MyData> access = array.start();
784- * try {
785- * for (int i = 0; i < access.size(); i++) {
786- * MyData d = access.get(i);
787- * }
788- * } finally {
789- * access.end();
790- * }
791- */
792- static class CopyOnWriteArray <T > {
793- private ArrayList <T > mData = new ArrayList <T >();
794- private ArrayList <T > mDataCopy ;
795-
796- private final Access <T > mAccess = new Access <T >();
797-
798- private boolean mStart ;
799-
800- static class Access <T > {
801- private ArrayList <T > mData ;
802- private int mSize ;
803-
804- T get (int index ) {
805- return mData .get (index );
741+ for (OnComputeInternalInsetsListener listener : listeners ) {
742+ listener .onComputeInternalInsets (inoutInfo );
806743 }
807-
808- int size () {
809- return mSize ;
810- }
811- }
812-
813- CopyOnWriteArray () {
814- }
815-
816- private ArrayList <T > getArray () {
817- if (mStart ) {
818- if (mDataCopy == null ) mDataCopy = new ArrayList <T >(mData );
819- return mDataCopy ;
820- }
821- return mData ;
822- }
823-
824- Access <T > start () {
825- if (mStart ) throw new IllegalStateException ("Iteration already started" );
826- mStart = true ;
827- mDataCopy = null ;
828- mAccess .mData = mData ;
829- mAccess .mSize = mData .size ();
830- return mAccess ;
831- }
832-
833- void end () {
834- if (!mStart ) throw new IllegalStateException ("Iteration not started" );
835- mStart = false ;
836- if (mDataCopy != null ) {
837- mData = mDataCopy ;
838- }
839- mDataCopy = null ;
840- }
841-
842- int size () {
843- return getArray ().size ();
844- }
845-
846- void add (T item ) {
847- getArray ().add (item );
848- }
849-
850- void addAll (CopyOnWriteArray <T > array ) {
851- getArray ().addAll (array .mData );
852- }
853-
854- void remove (T item ) {
855- getArray ().remove (item );
856- }
857-
858- void clear () {
859- getArray ().clear ();
860744 }
861745 }
862746}
0 commit comments