OSDN Git Service

Only call Drawable.setVisible(false) for visible outgoing drawables
authorAdam Powell <adamp@google.com>
Tue, 22 Mar 2016 17:40:29 +0000 (10:40 -0700)
committerAdam Powell <adamp@google.com>
Tue, 22 Mar 2016 18:14:16 +0000 (11:14 -0700)
In framework views where we're handling the new visibility aggregated
call we only update the drawable visibility when we're attached to a
window. For old outgoing drawables being replaced, gate this on
whether the drawable is already marked visible instead.

This catches a case where views being inflated might set drawables in
in a superclass constructor and have them replaced in a later
constructor. Gating the call into a drawable that might invoke its
callback (the view being constructed) avoids potential problems where
overridden methods are called unexpectedly on a view subclass that has
not finished running its constructor.

This is a better check than isAttachedToWindow, as isAttachedToWindow
will return false if the view has been temporarily detached from its
parent by a view-recycling container. In those cases, the view would
not correctly update the outgoing drawable.

Bug 27461617

Change-Id: I733a2dd3e3df0a8d80d5dc542ca7b30064159d5d

core/java/android/view/View.java
core/java/android/widget/ImageView.java

index 3586484..6d35a58 100644 (file)
@@ -18002,7 +18002,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
          * to clear the previous drawable. setVisible first while we still have the callback set.
          */
         if (mBackground != null) {
-            if (isAttachedToWindow()) {
+            // It's possible for this method to be invoked from the View constructor before
+            // subclass constructors have run. Drawables can and should trigger invalidations
+            // and other activity with their callback on visibility changes, which shouldn't
+            // happen before subclass constructors finish. However, we won't have set the
+            // drawable as visible until the view becomes attached. This guard below keeps
+            // multiple calls to this method from constructors from causing issues.
+            if (mBackground.isVisible()) {
                 mBackground.setVisible(false, false);
             }
             mBackground.setCallback(null);
@@ -18237,7 +18243,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         }
 
         if (mForegroundInfo.mDrawable != null) {
-            if (isAttachedToWindow()) {
+            // It's possible for this method to be invoked from the View constructor before
+            // subclass constructors have run. Drawables can and should trigger invalidations
+            // and other activity with their callback on visibility changes, which shouldn't
+            // happen before subclass constructors finish. However, we won't have set the
+            // drawable as visible until the view becomes attached. This guard below keeps
+            // multiple calls to this method from constructors from causing issues.
+            if (mForegroundInfo.mDrawable.isVisible()) {
                 mForegroundInfo.mDrawable.setVisible(false, false);
             }
             mForegroundInfo.mDrawable.setCallback(null);
index 222a040..0206577 100644 (file)
@@ -911,11 +911,17 @@ public class ImageView extends View {
         }
 
         if (mDrawable != null) {
-            mDrawable.setCallback(null);
-            unscheduleDrawable(mDrawable);
-            if (isAttachedToWindow()) {
+            // It's possible for this method to be invoked from the constructor before
+            // subclass constructors have run. Drawables can and should trigger invalidations
+            // and other activity with their callback on visibility changes, which shouldn't
+            // happen before subclass constructors finish. However, we won't have set the
+            // drawable as visible until the view becomes attached. This guard below keeps
+            // multiple calls to this method from constructors from causing issues.
+            if (mDrawable.isVisible()) {
                 mDrawable.setVisible(false, false);
             }
+            mDrawable.setCallback(null);
+            unscheduleDrawable(mDrawable);
         }
 
         mDrawable = d;