diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 4b858d0dd1544..9002b1ddaf8ad 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -378,8 +378,7 @@ public class PopupWindow { *

Change the popup's content. The content is represented by an instance * of {@link android.view.View}.

* - *

This method has no effect if called when the popup is showing. To - * apply it while a popup is showing, call

+ *

This method has no effect if called when the popup is showing.

* * @param contentView the new content for the popup * @@ -1040,7 +1039,7 @@ public class PopupWindow { * * @return true if the popup is translated upwards to fit on screen */ - private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, + boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff, int yoff) { anchor.getLocationInWindow(mDrawingLocation); @@ -1374,6 +1373,7 @@ public class PopupWindow { * height can be set to -1 to update location only. Calling this function * also updates the window with the current popup state as * described for {@link #update()}.

+ * *

If the view later scrolls to move anchor to a different * location, the popup will be moved correspondingly.

* @@ -1395,9 +1395,13 @@ public class PopupWindow { } WeakReference oldAnchor = mAnchor; - if (oldAnchor == null || oldAnchor.get() != anchor || - (updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff))) { + final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff); + if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) { registerForScrollChanged(anchor, xoff, yoff); + } else if (needsUpdate) { + // No need to register again if this is a DropDown, showAsDropDown already did. + mAnchorXoff = xoff; + mAnchorYoff = yoff; } WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index c4d95b26c9ea4..6791a1d22e46d 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3873,9 +3873,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void registerForPreDraw() { final ViewTreeObserver observer = getViewTreeObserver(); - if (observer == null) { - return; - } if (mPreDrawState == PREDRAW_NOT_REGISTERED) { observer.addOnPreDrawListener(this); @@ -3961,15 +3958,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } final ViewTreeObserver observer = getViewTreeObserver(); - if (observer != null) { - // No need to create the controller. - // The get method will add the listener on controller creation. - if (mInsertionPointCursorController != null) { - observer.addOnTouchModeChangeListener(mInsertionPointCursorController); - } - if (mSelectionModifierCursorController != null) { - observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); - } + // No need to create the controller. + // The get method will add the listener on controller creation. + if (mInsertionPointCursorController != null) { + observer.addOnTouchModeChangeListener(mInsertionPointCursorController); + } + if (mSelectionModifierCursorController != null) { + observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); } } @@ -3978,18 +3973,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super.onDetachedFromWindow(); final ViewTreeObserver observer = getViewTreeObserver(); - if (observer != null) { - if (mPreDrawState != PREDRAW_NOT_REGISTERED) { - observer.removeOnPreDrawListener(this); - mPreDrawState = PREDRAW_NOT_REGISTERED; - } - // No need to create the controller, as getXXController would. - if (mInsertionPointCursorController != null) { - observer.removeOnTouchModeChangeListener(mInsertionPointCursorController); - } - if (mSelectionModifierCursorController != null) { - observer.removeOnTouchModeChangeListener(mSelectionModifierCursorController); - } + if (mPreDrawState != PREDRAW_NOT_REGISTERED) { + observer.removeOnPreDrawListener(this); + mPreDrawState = PREDRAW_NOT_REGISTERED; + } + // No need to create the controller, as getXXController would. + if (mInsertionPointCursorController != null) { + observer.removeOnTouchModeChangeListener(mInsertionPointCursorController); + } + if (mSelectionModifierCursorController != null) { + observer.removeOnTouchModeChangeListener(mSelectionModifierCursorController); } if (mError != null) { @@ -4290,10 +4283,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mPreDrawState == PREDRAW_DONE) { final ViewTreeObserver observer = getViewTreeObserver(); - if (observer != null) { - observer.removeOnPreDrawListener(this); - mPreDrawState = PREDRAW_NOT_REGISTERED; - } + observer.removeOnPreDrawListener(this); + mPreDrawState = PREDRAW_NOT_REGISTERED; } int color = mCurTextColor; @@ -5541,8 +5532,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { - boring = BoringLayout.isBoring(mTransformed, mTextPaint, - mBoring); + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring); if (boring != null) { mBoring = boring; } @@ -8655,9 +8645,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private class HandleView extends View { + private class HandleView extends View implements ViewTreeObserver.OnScrollChangedListener { private Drawable mDrawable; - private final PopupWindow mContainer; + private final ScrollingPopupWindow mContainer; private int mPositionX; private int mPositionY; private final CursorController mController; @@ -8726,7 +8716,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public HandleView(CursorController controller, int pos) { super(TextView.this.mContext); mController = controller; - mContainer = new PopupWindow(TextView.this.mContext, null, + mContainer = new ScrollingPopupWindow(TextView.this.mContext, null, com.android.internal.R.attr.textSelectHandleWindowStyle); mContainer.setSplitTouchEnabled(true); mContainer.setClippingEnabled(false); @@ -8794,24 +8784,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } mContainer.setContentView(this); - final int[] coords = mTempCoords; - TextView.this.getLocationInWindow(coords); - mContainerPositionX = coords[0] + mPositionX; - mContainerPositionY = coords[1] + mPositionY; - mContainer.showAtLocation(TextView.this, 0, mContainerPositionX, mContainerPositionY); + mContainerPositionX = mPositionX; + mContainerPositionY = mPositionY - TextView.this.getHeight(); + mContainer.showAsDropDown(TextView.this, mContainerPositionX, mContainerPositionY); // Hide paste view when handle is moved on screen. - if (mPastePopupWindow != null) { - mPastePopupWindow.hide(); - } + hidePastePopupWindow(); } public void hide() { mIsDragging = false; mContainer.dismiss(); - if (mPastePopupWindow != null) { - mPastePopupWindow.hide(); - } + hidePastePopupWindow(); } public boolean isShowing() { @@ -8834,19 +8818,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int compoundPaddingRight = getCompoundPaddingRight(); final TextView hostView = TextView.this; - final int left = 0; - final int right = hostView.getWidth(); - final int top = 0; - final int bottom = hostView.getHeight(); if (mTempRect == null) { mTempRect = new Rect(); } final Rect clip = mTempRect; - clip.left = left + compoundPaddingLeft; - clip.top = top + extendedPaddingTop; - clip.right = right - compoundPaddingRight; - clip.bottom = bottom - extendedPaddingBottom; + clip.left = compoundPaddingLeft; + clip.top = extendedPaddingTop; + clip.right = hostView.getWidth() - compoundPaddingRight; + clip.bottom = hostView.getHeight() - extendedPaddingBottom; final ViewParent parent = hostView.getParent(); if (parent == null || !parent.getChildVisibleRect(hostView, clip, null)) { @@ -8858,7 +8838,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int posX = coords[0] + mPositionX + (int) mHotspotX; final int posY = coords[1] + mPositionY + (int) mHotspotY; - return posX >= clip.left && posX <= clip.right && + // Offset by 1 to take into account 0.5 and int rounding around getPrimaryHorizontal. + return posX >= clip.left - 1 && posX <= clip.right + 1 && posY >= clip.top && posY <= clip.bottom; } @@ -8868,23 +8849,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (isPositionVisible()) { int[] coords = null; if (mContainer.isShowing()) { - coords = mTempCoords; - TextView.this.getLocationInWindow(coords); - final int containerPositionX = coords[0] + mPositionX; - final int containerPositionY = coords[1] + mPositionY; + final int containerPositionX = mPositionX; + final int containerPositionY = mPositionY - TextView.this.getHeight(); if (containerPositionX != mContainerPositionX || containerPositionY != mContainerPositionY) { mContainerPositionX = containerPositionX; mContainerPositionY = containerPositionY; - mContainer.update(mContainerPositionX, mContainerPositionY, + mContainer.update(TextView.this, mContainerPositionX, mContainerPositionY, mRight - mLeft, mBottom - mTop); // Hide paste popup window as soon as a scroll occurs. - if (mPastePopupWindow != null) { - mPastePopupWindow.hide(); - } + hidePastePopupWindow(); } } else { show(); @@ -8902,9 +8879,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mLastParentY = coords[1]; } // Hide paste popup window as soon as the handle is dragged. - if (mPastePopupWindow != null) { - mPastePopupWindow.hide(); - } + hidePastePopupWindow(); } } else { hide(); @@ -9008,10 +8983,60 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mPastePopupWindow.show(); } } + + void hidePastePopupWindow() { + if (mPastePopupWindow != null) { + mPastePopupWindow.hide(); + } + } + + /** + * A popup window, attached to a view, and that listens to scroll events in its anchors' + * view hierarchy, so that it is automatically moved on such events. + */ + private class ScrollingPopupWindow extends PopupWindow { + + private int[] mDrawingLocations = new int[2]; + + public ScrollingPopupWindow(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, + int xoff, int yoff) { + anchor.getLocationInWindow(mDrawingLocations); + p.x = mDrawingLocations[0] + xoff; + p.y = mDrawingLocations[1] + anchor.getHeight() + yoff; + + // Hide paste popup as soon as the view is scrolled. + hidePastePopupWindow(); + + if (!isPositionVisible()) { + dismiss(); + onHandleBecomeInvisible(); + } + + return false; + } + } + + public void onScrollChanged() { + if (isPositionVisible()) { + show(); + ViewTreeObserver vto = TextView.this.getViewTreeObserver(); + vto.removeOnScrollChangedListener(this); + } + } + + public void onHandleBecomeInvisible() { + ViewTreeObserver vto = TextView.this.getViewTreeObserver(); + vto.addOnScrollChangedListener(this); + } } private class InsertionPointCursorController implements CursorController { - private static final int DELAY_BEFORE_FADE_OUT = 4100; + private static final int DELAY_BEFORE_FADE_OUT = 4000; private static final int DELAY_BEFORE_PASTE = 2000; private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; @@ -9027,7 +9052,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void show(int delayBeforePaste) { updatePosition(); hideDelayed(); - getHandle().show(); removePastePopupCallback(); final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime; if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) { @@ -9547,9 +9571,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mInsertionPointCursorController = new InsertionPointCursorController(); final ViewTreeObserver observer = getViewTreeObserver(); - if (observer != null) { - observer.addOnTouchModeChangeListener(mInsertionPointCursorController); - } + observer.addOnTouchModeChangeListener(mInsertionPointCursorController); } return mInsertionPointCursorController; @@ -9564,9 +9586,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mSelectionModifierCursorController = new SelectionModifierCursorController(); final ViewTreeObserver observer = getViewTreeObserver(); - if (observer != null) { - observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); - } + observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); } return mSelectionModifierCursorController;