Loading core/java/android/widget/PopupWindow.java +74 −19 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.ViewParent; Loading Loading @@ -164,7 +165,20 @@ public class PopupWindow { com.android.internal.R.attr.state_above_anchor }; private final OnAttachStateChangeListener mOnAnchorRootDetachedListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) {} @Override public void onViewDetachedFromWindow(View v) { mIsAnchorRootAttached = false; } }; private WeakReference<View> mAnchor; private WeakReference<View> mAnchorRoot; private boolean mIsAnchorRootAttached; private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() { @Override Loading Loading @@ -1037,7 +1051,7 @@ public class PopupWindow { TransitionManager.endTransitions(mDecorView); unregisterForScrollChanged(); unregisterForViewTreeChanges(); mIsShowing = true; mIsDropdown = false; Loading Loading @@ -1120,7 +1134,7 @@ public class PopupWindow { TransitionManager.endTransitions(mDecorView); registerForScrollChanged(anchor, xoff, yoff, gravity); registerForViewTreeChanges(anchor, xoff, yoff, gravity); mIsShowing = true; mIsDropdown = true; Loading Loading @@ -1633,14 +1647,23 @@ public class PopupWindow { mIsShowing = false; mIsTransitioningToDismiss = true; // This method may be called as part of window detachment, in which // case the anchor view (and its root) will still return true from // isAttachedToWindow() during execution of this method; however, we // can expect the OnAttachStateChangeListener to have been called prior // to executing this method, so we can rely on that instead. final Transition exitTransition = mExitTransition; if (exitTransition != null && decorView.isLaidOut()) { if (!mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) { // The decor view is non-interactive during exit transitions. final LayoutParams p = (LayoutParams) decorView.getLayoutParams(); p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; mWindowManager.updateViewLayout(decorView, p); // Once we start dismissing the decor view, all state (including // the anchor root) needs to be moved to the decor view since we // may open another popup while it's busy exiting. final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null; final Rect epicenter = getTransitionEpicenter(); exitTransition.setEpicenterCallback(new EpicenterCallback() { @Override Loading @@ -1648,7 +1671,8 @@ public class PopupWindow { return epicenter; } }); decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() { decorView.startExitTransition(exitTransition, anchorRoot, new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { dismissImmediate(decorView, contentHolder, contentView); Loading @@ -1659,7 +1683,7 @@ public class PopupWindow { } // Clears the anchor view. unregisterForScrollChanged(); unregisterForViewTreeChanges(); if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); Loading Loading @@ -1925,7 +1949,7 @@ public class PopupWindow { final WeakReference<View> oldAnchor = mAnchor; final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff); if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) { registerForScrollChanged(anchor, xoff, yoff, mAnchoredGravity); registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity); } else if (needsUpdate) { // No need to register again if this is a DropDown, showAsDropDown already did. mAnchorXoff = xoff; Loading Loading @@ -1969,27 +1993,38 @@ public class PopupWindow { public void onDismiss(); } private void unregisterForScrollChanged() { final WeakReference<View> anchorRef = mAnchor; final View anchor = anchorRef == null ? null : anchorRef.get(); private void unregisterForViewTreeChanges() { final View anchor = mAnchor != null ? mAnchor.get() : null; if (anchor != null) { final ViewTreeObserver vto = anchor.getViewTreeObserver(); vto.removeOnScrollChangedListener(mOnScrollChangedListener); } mAnchor = null; final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null; if (anchorRoot != null) { anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener); } private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) { unregisterForScrollChanged(); mAnchor = null; mAnchorRoot = null; mIsAnchorRootAttached = false; } mAnchor = new WeakReference<>(anchor); private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) { unregisterForViewTreeChanges(); final ViewTreeObserver vto = anchor.getViewTreeObserver(); if (vto != null) { vto.addOnScrollChangedListener(mOnScrollChangedListener); } final View anchorRoot = anchor.getRootView(); anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener); mAnchor = new WeakReference<>(anchor); mAnchorRoot = new WeakReference<>(anchorRoot); mIsAnchorRootAttached = anchorRoot.isAttachedToWindow(); mAnchorXoff = xoff; mAnchorYoff = yoff; mAnchoredGravity = gravity; Loading Loading @@ -2109,16 +2144,23 @@ public class PopupWindow { * its {@code onTransitionEnd} method called even if the transition * never starts; however, it may be called with a {@code null} argument. */ public void startExitTransition(Transition transition, final TransitionListener listener) { public void startExitTransition(Transition transition, final View anchorRoot, final TransitionListener listener) { if (transition == null) { return; } // The anchor view's window may go away while we're executing our // transition, in which case we need to end the transition // immediately and execute the listener to remove the popup. anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener); // The exit listener MUST be called for cleanup, even if the // transition never starts or ends. Stash it for later. mPendingExitListener = new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener); listener.onTransitionEnd(transition); // The listener was called. Our job here is done. Loading Loading @@ -2153,6 +2195,19 @@ public class PopupWindow { mPendingExitListener.onTransitionEnd(null); } } private final OnAttachStateChangeListener mOnAnchorRootDetachedListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) {} @Override public void onViewDetachedFromWindow(View v) { v.removeOnAttachStateChangeListener(this); TransitionManager.endTransitions(PopupDecorView.this); } }; } private class PopupBackgroundView extends FrameLayout { Loading Loading
core/java/android/widget/PopupWindow.java +74 −19 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.ViewParent; Loading Loading @@ -164,7 +165,20 @@ public class PopupWindow { com.android.internal.R.attr.state_above_anchor }; private final OnAttachStateChangeListener mOnAnchorRootDetachedListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) {} @Override public void onViewDetachedFromWindow(View v) { mIsAnchorRootAttached = false; } }; private WeakReference<View> mAnchor; private WeakReference<View> mAnchorRoot; private boolean mIsAnchorRootAttached; private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() { @Override Loading Loading @@ -1037,7 +1051,7 @@ public class PopupWindow { TransitionManager.endTransitions(mDecorView); unregisterForScrollChanged(); unregisterForViewTreeChanges(); mIsShowing = true; mIsDropdown = false; Loading Loading @@ -1120,7 +1134,7 @@ public class PopupWindow { TransitionManager.endTransitions(mDecorView); registerForScrollChanged(anchor, xoff, yoff, gravity); registerForViewTreeChanges(anchor, xoff, yoff, gravity); mIsShowing = true; mIsDropdown = true; Loading Loading @@ -1633,14 +1647,23 @@ public class PopupWindow { mIsShowing = false; mIsTransitioningToDismiss = true; // This method may be called as part of window detachment, in which // case the anchor view (and its root) will still return true from // isAttachedToWindow() during execution of this method; however, we // can expect the OnAttachStateChangeListener to have been called prior // to executing this method, so we can rely on that instead. final Transition exitTransition = mExitTransition; if (exitTransition != null && decorView.isLaidOut()) { if (!mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) { // The decor view is non-interactive during exit transitions. final LayoutParams p = (LayoutParams) decorView.getLayoutParams(); p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; mWindowManager.updateViewLayout(decorView, p); // Once we start dismissing the decor view, all state (including // the anchor root) needs to be moved to the decor view since we // may open another popup while it's busy exiting. final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null; final Rect epicenter = getTransitionEpicenter(); exitTransition.setEpicenterCallback(new EpicenterCallback() { @Override Loading @@ -1648,7 +1671,8 @@ public class PopupWindow { return epicenter; } }); decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() { decorView.startExitTransition(exitTransition, anchorRoot, new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { dismissImmediate(decorView, contentHolder, contentView); Loading @@ -1659,7 +1683,7 @@ public class PopupWindow { } // Clears the anchor view. unregisterForScrollChanged(); unregisterForViewTreeChanges(); if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); Loading Loading @@ -1925,7 +1949,7 @@ public class PopupWindow { final WeakReference<View> oldAnchor = mAnchor; final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff); if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) { registerForScrollChanged(anchor, xoff, yoff, mAnchoredGravity); registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity); } else if (needsUpdate) { // No need to register again if this is a DropDown, showAsDropDown already did. mAnchorXoff = xoff; Loading Loading @@ -1969,27 +1993,38 @@ public class PopupWindow { public void onDismiss(); } private void unregisterForScrollChanged() { final WeakReference<View> anchorRef = mAnchor; final View anchor = anchorRef == null ? null : anchorRef.get(); private void unregisterForViewTreeChanges() { final View anchor = mAnchor != null ? mAnchor.get() : null; if (anchor != null) { final ViewTreeObserver vto = anchor.getViewTreeObserver(); vto.removeOnScrollChangedListener(mOnScrollChangedListener); } mAnchor = null; final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null; if (anchorRoot != null) { anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener); } private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) { unregisterForScrollChanged(); mAnchor = null; mAnchorRoot = null; mIsAnchorRootAttached = false; } mAnchor = new WeakReference<>(anchor); private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) { unregisterForViewTreeChanges(); final ViewTreeObserver vto = anchor.getViewTreeObserver(); if (vto != null) { vto.addOnScrollChangedListener(mOnScrollChangedListener); } final View anchorRoot = anchor.getRootView(); anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener); mAnchor = new WeakReference<>(anchor); mAnchorRoot = new WeakReference<>(anchorRoot); mIsAnchorRootAttached = anchorRoot.isAttachedToWindow(); mAnchorXoff = xoff; mAnchorYoff = yoff; mAnchoredGravity = gravity; Loading Loading @@ -2109,16 +2144,23 @@ public class PopupWindow { * its {@code onTransitionEnd} method called even if the transition * never starts; however, it may be called with a {@code null} argument. */ public void startExitTransition(Transition transition, final TransitionListener listener) { public void startExitTransition(Transition transition, final View anchorRoot, final TransitionListener listener) { if (transition == null) { return; } // The anchor view's window may go away while we're executing our // transition, in which case we need to end the transition // immediately and execute the listener to remove the popup. anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener); // The exit listener MUST be called for cleanup, even if the // transition never starts or ends. Stash it for later. mPendingExitListener = new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener); listener.onTransitionEnd(transition); // The listener was called. Our job here is done. Loading Loading @@ -2153,6 +2195,19 @@ public class PopupWindow { mPendingExitListener.onTransitionEnd(null); } } private final OnAttachStateChangeListener mOnAnchorRootDetachedListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) {} @Override public void onViewDetachedFromWindow(View v) { v.removeOnAttachStateChangeListener(this); TransitionManager.endTransitions(PopupDecorView.this); } }; } private class PopupBackgroundView extends FrameLayout { Loading