diff options
| author | Adam Powell <adamp@google.com> | 2010-09-17 16:22:49 -0700 |
|---|---|---|
| committer | Adam Powell <adamp@google.com> | 2010-09-17 17:59:06 -0700 |
| commit | f37df070ea84c353ff8bed4b2591932126d7e2ca (patch) | |
| tree | a52df9e3e4f6c92ac9dc3881eb6bea91f4a5123a /core/java/android | |
| parent | f1ec1ddd5072fa4f75c15be0ee1d82c17646500a (diff) | |
| parent | b39470b57d2e4e0bf5abb43729ef9e43eb0ad5f2 (diff) | |
resolved conflicts for merge of b39470b5 to master
Change-Id: If441c8684c576b6cbc485a37088d6869ad3fb23f
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/view/View.java | 61 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 103 | ||||
| -rw-r--r-- | core/java/android/view/ViewParent.java | 7 | ||||
| -rw-r--r-- | core/java/android/view/ViewRoot.java | 18 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 100 |
5 files changed, 276 insertions, 13 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ac65e0935bcc..97d58d2af804 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1613,15 +1613,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000; /** + * Indicates that this view has a visible/touchable overlay. + * @hide + */ + static final int HAS_OVERLAY = 0x10000000; + + /** * Indicates that pivotX or pivotY were explicitly set and we should not assume the center * for transform operations * * @hide */ - private static final int PIVOT_EXPLICITLY_SET = 0x10000000; + private static final int PIVOT_EXPLICITLY_SET = 0x20000000; /** {@hide} */ - static final int ACTIVATED = 0x20000000; + static final int ACTIVATED = 0x40000000; /** * The parent this view is attached to. @@ -3034,6 +3040,57 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility resetPressedState(); } + /** + * Enable or disable drawing overlays after a full drawing pass. This enables a view to + * draw on a topmost overlay layer after normal drawing completes and get right of first + * refusal for touch events in the window. + * + * <em>Warning:</em> Views that use this feature should take care to disable/enable overlay + * appropriately when they are attached/detached from their window. All overlays should be + * disabled when detached. + * + * @param enabled true if overlay drawing should be enabled for this view, false otherwise + * + * @see #onDrawOverlay(Canvas) + * + * @hide + */ + protected void setOverlayEnabled(boolean enabled) { + final boolean oldValue = (mPrivateFlags & HAS_OVERLAY) == HAS_OVERLAY; + mPrivateFlags = (mPrivateFlags & ~HAS_OVERLAY) | (enabled ? HAS_OVERLAY : 0); + if (enabled != oldValue) { + final ViewParent parent = getParent(); + if (parent != null) { + try { + parent.childOverlayStateChanged(this); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, "Could not propagate hasOverlay state", e); + } + } + } + } + + /** + * @return true if this View has an overlay enabled. + * + * @see #setOverlayEnabled(boolean) + * @see #onDrawOverlay(Canvas) + * + * @hide + */ + public boolean isOverlayEnabled() { + return (mPrivateFlags & HAS_OVERLAY) == HAS_OVERLAY; + } + + /** + * Override this method to draw on an overlay layer above all other views in the window + * after the standard drawing pass is complete. This allows a view to draw outside its + * normal boundaries. + * @hide + */ + public void onDrawOverlay(Canvas canvas) { + } + private void resetPressedState() { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index a18a9775fd73..abe667211885 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -245,9 +245,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000; /** + * When set, at least one child of this ViewGroup will return true from hasOverlay. + */ + private static final int FLAG_CHILD_HAS_OVERLAY = 0x100000; + + /** * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate. */ - private static final int FLAG_SPLIT_MOTION_EVENTS = 0x100000; + private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000; /** * Indicates which types of drawing caches are to be kept in memory. @@ -904,6 +909,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // who can handle it, start with the front-most child. final View[] children = mChildren; final int count = mChildrenCount; + + // Check for children with overlays first. They don't rely on hit rects to determine + // if they can accept a new touch event. + if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY) { + for (int i = count - 1; i >= 0; i--) { + final View child = children[i]; + // Don't let children respond to events as an overlay during an animation. + if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE + && child.getAnimation() == null + && child.isOverlayEnabled()) { + // offset the event to the view's coordinate system + final float xc = scrolledXFloat - child.mLeft; + final float yc = scrolledYFloat - child.mTop; + ev.setLocation(xc, yc); + child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; + if (child.dispatchTouchEvent(ev)) { + // Event handled, we have a target now. + mMotionTarget = child; + return true; + } + // The event didn't get handled, try the next view. + // Don't reset the event's location, it's not + // necessary here. + } + } + } + + // Now check views normally. for (int i = count - 1; i >= 0; i--) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE @@ -2741,6 +2774,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (clearChildFocus != null) { clearChildFocus(clearChildFocus); } + + mGroupFlags &= ~FLAG_CHILD_HAS_OVERLAY; } /** @@ -2989,7 +3024,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int left = mLeft; final int top = mTop; - if (dirty.intersect(0, 0, mRight - left, mBottom - top) || + if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY || + dirty.intersect(0, 0, mRight - left, mBottom - top) || (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) { mPrivateFlags &= ~DRAWING_CACHE_VALID; @@ -3977,6 +4013,69 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * Called when a child's overlay state changes between enabled/disabled. + * @param child Child view whose state has changed or null + * @hide + */ + public void childOverlayStateChanged(View child) { + boolean childHasOverlay = false; + if (child != null) { + childHasOverlay = child.isOverlayEnabled(); + } else { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + if (childHasOverlay |= getChildAt(i).isOverlayEnabled()) { + break; + } + } + } + + final boolean hasChildWithOverlay = childHasOverlay || + (mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY; + + final boolean oldValue = isOverlayEnabled(); + mGroupFlags = (mGroupFlags & ~FLAG_CHILD_HAS_OVERLAY) | + (hasChildWithOverlay ? FLAG_CHILD_HAS_OVERLAY : 0); + if (isOverlayEnabled() != oldValue) { + final ViewParent parent = getParent(); + if (parent != null) { + try { + parent.childOverlayStateChanged(this); + } catch (AbstractMethodError e) { + Log.e("ViewGroup", "Could not propagate hasOverlay state", e); + } + } + } + } + + /** + * @hide + */ + public boolean isOverlayEnabled() { + return super.isOverlayEnabled() || + ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY); + } + + /** + * @hide + */ + @Override + public void onDrawOverlay(Canvas canvas) { + if ((mGroupFlags & FLAG_CHILD_HAS_OVERLAY) == FLAG_CHILD_HAS_OVERLAY) { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + if (child.isOverlayEnabled()) { + canvas.translate(child.mLeft + child.mScrollX, child.mTop + child.mScrollY); + child.onDrawOverlay(canvas); + canvas.translate(-(child.mLeft + child.mScrollX), + -(child.mTop + child.mScrollY)); + } + } + } + } + + /** * LayoutParams are used by views to tell their parents how they want to be * laid out. See * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index d7d4c3f02d0d..d2907da0b8f5 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -222,4 +222,11 @@ public interface ViewParent { */ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate); + + /** + * Called when a child view's overlay state changes between enabled/disabled. + * @param child Child view whose state changed or null. + * @hide + */ + public void childOverlayStateChanged(View child); } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 7b20b8be8817..cb9f84da39b9 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -207,6 +207,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn private final int mDensity; + private boolean mHasOverlay; + public static IWindowSession getWindowSession(Looper mainLooper) { synchronized (mStaticInit) { if (!mInitialized) { @@ -1357,6 +1359,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn canvas.setScreenDensity(scalingRequired ? DisplayMetrics.DENSITY_DEVICE : 0); mView.draw(canvas); + if (mHasOverlay) { + mView.onDrawOverlay(canvas); + } } finally { mAttachInfo.mIgnoreDirtyState = false; } @@ -2739,6 +2744,19 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn return scrollToRectOrFocus(rectangle, immediate); } + /** + * @hide + */ + public void childOverlayStateChanged(View child) { + final boolean oldState = mHasOverlay; + mHasOverlay = child.isOverlayEnabled(); + // Invalidate the whole thing when we change overlay states just in case + // something left chunks of data drawn someplace it shouldn't have. + if (mHasOverlay != oldState) { + child.invalidate(); + } + } + class TakenSurfaceHolder extends BaseSurfaceHolder { @Override public boolean onAllowLockCanvas() { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2d550e479520..93e87e6ab86c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -290,6 +290,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } InputMethodState mInputMethodState; + private int mTextSelectHandleLeftRes; + private int mTextSelectHandleRightRes; + private int mTextSelectHandleRes; + /* * Kick-start the font cache for the zygote process (to pay the cost of * initializing freetype for our default font only once). @@ -710,6 +714,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Log.w(LOG_TAG, "Failure reading input extras", e); } break; + + case com.android.internal.R.styleable.TextView_textSelectHandleLeft: + mTextSelectHandleLeftRes = a.getResourceId(attr, 0); + break; + + case com.android.internal.R.styleable.TextView_textSelectHandleRight: + mTextSelectHandleRightRes = a.getResourceId(attr, 0); + break; + + case com.android.internal.R.styleable.TextView_textSelectHandle: + mTextSelectHandleRes = a.getResourceId(attr, 0); + break; } } a.recycle(); @@ -3763,6 +3779,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener showError(); mShowErrorAfterAttach = false; } + + updateOverlay(); } @Override @@ -3780,6 +3798,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mError != null) { hideError(); } + + setOverlayEnabled(false); } @Override @@ -4147,7 +4167,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ canvas.restore(); + } + /** + * @hide + */ + @Override + public void onDrawOverlay(Canvas canvas) { if (mInsertionPointCursorController != null) { mInsertionPointCursorController.draw(canvas); } @@ -6742,10 +6768,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { + // Check to see if we're testing for our anchor overlay. + boolean handled = false; + final float x = event.getX(); + final float y = event.getY(); + if (x < 0 || x >= mRight - mLeft || y < 0 || y >= mBottom - mTop) { + if (mInsertionPointCursorController != null) { + handled |= mInsertionPointCursorController.onTouchEvent(event); + } + if (mSelectionModifierCursorController != null) { + handled |= mSelectionModifierCursorController.onTouchEvent(event); + } + + if (!handled) { + return false; + } + } + // Reset this state; it will be re-set if super.onTouchEvent // causes focus to move to the view. mTouchFocusSelected = false; mScrolled = false; + + if (handled) { + return true; + } } final boolean superResult = super.onTouchEvent(event); @@ -7658,6 +7705,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + private void updateOverlay() { + boolean enableOverlay = false; + if (mSelectionModifierCursorController != null) { + enableOverlay |= mSelectionModifierCursorController.isShowing(); + } + if (mInsertionPointCursorController != null) { + enableOverlay |= mInsertionPointCursorController.isShowing(); + } + setOverlayEnabled(enableOverlay); + } + /** * A CursorController instance can be used to control a cursor in the text. * @@ -7681,6 +7739,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void hide(); /** + * @return true if the CursorController is currently visible + */ + public boolean isShowing(); + + /** * Update the controller's position. */ public void updatePosition(int x, int y); @@ -7702,7 +7765,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * a chance to become active and/or visible. * @param event The touch event */ - public void onTouchEvent(MotionEvent event); + public boolean onTouchEvent(MotionEvent event); /** * Draws a visual representation of the controller on the canvas. @@ -7736,10 +7799,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final Rect bounds = sCursorControllerTempRect; bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0) + mScrollX; - bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2 + mScrollY; + bounds.top = (bottom ? lineBottom : lineTop) + mScrollY; mTopExtension = bottom ? 0 : drawableHeight / 2; - mBottomExtension = drawableHeight; + mBottomExtension = 0; //drawableHeight / 4; // Extend touch region up when editing the last line of text (or a single line) so that // it is easier to grab. @@ -7797,7 +7860,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener InsertionPointCursorController() { Resources res = mContext.getResources(); - mHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle)); + mHandle = new Handle(res.getDrawable(mTextSelectHandleRes)); } public void show() { @@ -7805,6 +7868,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Has to be done after updateDrawablePosition, so that previous position invalidate // in only done if necessary. mIsVisible = true; + updateOverlay(); } public void hide() { @@ -7818,6 +7882,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + public boolean isShowing() { + return mIsVisible; + } + public void draw(Canvas canvas) { if (mIsVisible) { int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart); @@ -7833,6 +7901,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { mHandle.mDrawable.setAlpha(0); mIsVisible = false; + updateOverlay(); } } mHandle.mDrawable.draw(canvas); @@ -7861,6 +7930,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Should never happen, safety check. Log.w(LOG_TAG, "Update cursor controller position called with no cursor"); mIsVisible = false; + updateOverlay(); return; } @@ -7870,7 +7940,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mHandle.mDrawable.setAlpha(255); } - public void onTouchEvent(MotionEvent event) { + public boolean onTouchEvent(MotionEvent event) { if (isFocused() && isTextEditable() && mIsVisible) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN : { @@ -7898,8 +7968,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mOffsetY += viewportToContentVerticalOffset(); mOnDownTimerStart = event.getEventTime(); + return true; } - break; + return false; } case MotionEvent.ACTION_UP : { @@ -7917,6 +7988,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } + return false; } public float getOffsetX() { @@ -7944,8 +8016,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener SelectionModifierCursorController() { Resources res = mContext.getResources(); - mStartHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle)); - mEndHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle)); + mStartHandle = new Handle(res.getDrawable(mTextSelectHandleLeftRes)); + mEndHandle = new Handle(res.getDrawable(mTextSelectHandleRightRes)); } public void show() { @@ -7953,6 +8025,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Has to be done after updateDrawablePositions, so that previous position invalidate // in only done if necessary. mIsVisible = true; + updateOverlay(); mFadeOutTimerStart = -1; hideInsertionPointCursorController(); } @@ -7965,8 +8038,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + public boolean isShowing() { + return mIsVisible; + } + public void cancelFadeOutAnimation() { mIsVisible = false; + updateOverlay(); mStartHandle.postInvalidate(); mEndHandle.postInvalidate(); } @@ -7985,6 +8063,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mStartHandle.mDrawable.setAlpha(0); mEndHandle.mDrawable.setAlpha(0); mIsVisible = false; + updateOverlay(); } } mStartHandle.mDrawable.draw(canvas); @@ -8042,6 +8121,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Should never happen, safety check. Log.w(LOG_TAG, "Update selection controller position called with no cursor"); mIsVisible = false; + updateOverlay(); return; } @@ -8054,7 +8134,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEndHandle.mDrawable.setAlpha(255); } - public void onTouchEvent(MotionEvent event) { + public boolean onTouchEvent(MotionEvent event) { if (isTextEditable()) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: @@ -8088,6 +8168,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mOffsetY += viewportToContentVerticalOffset(); ((ArrowKeyMovementMethod)mMovement).setCursorController(this); + return true; } } } @@ -8104,6 +8185,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; } } + return false; } /** |
