Do not move PopupWindow when its anchor is detached

The current PopupWindow implementation might call
the anchor view's getLocationInWindow even if the anchor
is currently detached from the window (and the location is
a meaningless (0,0)). This results in the popup jumping to
the top of the screen.

This patch is adding tracking of the anchor's attachment state.
The position is never updated while the anchor is detached.
When the anchor is re-attached, the popup position is updated.

Bug: 34853580
Test: manual (see bug)
Change-Id: Icca1b9b558a70ee3edbe6236e076d2d08a1f8f11
This commit is contained in:
Vladislav Kaznacheev
2017-02-09 11:43:28 -08:00
parent d0e1022237
commit afaa932ece

View File

@@ -208,6 +208,21 @@ public class PopupWindow {
com.android.internal.R.attr.state_above_anchor
};
private final OnAttachStateChangeListener mOnAnchorDetachedListener =
new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
// Anchor might have been reattached in a different position.
alignToAnchor();
}
@Override
public void onViewDetachedFromWindow(View v) {
// Leave the popup in its current position.
// The anchor might become attached again.
}
};
private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
new OnAttachStateChangeListener() {
@Override
@@ -223,20 +238,7 @@ public class PopupWindow {
private WeakReference<View> mAnchorRoot;
private boolean mIsAnchorRootAttached;
private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
@Override
public void onScrollChanged() {
final View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null && mDecorView != null) {
final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
mDecorView.getLayoutParams();
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
p.width, p.height, mAnchoredGravity, false));
update(p.x, p.y, -1, -1, true);
}
}
};
private final OnScrollChangedListener mOnScrollChangedListener = this::alignToAnchor;
private int mAnchorXoff;
private int mAnchorYoff;
@@ -2214,6 +2216,7 @@ public class PopupWindow {
if (anchor != null) {
final ViewTreeObserver vto = anchor.getViewTreeObserver();
vto.removeOnScrollChangedListener(mOnScrollChangedListener);
anchor.removeOnAttachStateChangeListener(mOnAnchorDetachedListener);
}
final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
@@ -2233,6 +2236,7 @@ public class PopupWindow {
if (vto != null) {
vto.addOnScrollChangedListener(mOnScrollChangedListener);
}
anchor.addOnAttachStateChangeListener(mOnAnchorDetachedListener);
final View anchorRoot = anchor.getRootView();
anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
@@ -2247,6 +2251,18 @@ public class PopupWindow {
mAnchoredGravity = gravity;
}
private void alignToAnchor() {
final View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null && anchor.isAttachedToWindow() && mDecorView != null) {
final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
mDecorView.getLayoutParams();
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
p.width, p.height, mAnchoredGravity, false));
update(p.x, p.y, -1, -1, true);
}
}
private class PopupDecorView extends FrameLayout {
private TransitionListenerAdapter mPendingExitListener;