diff options
| -rw-r--r-- | api/current.txt | 9 | ||||
| -rw-r--r-- | core/java/android/view/Overlay.java | 80 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 71 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 30 | ||||
| -rw-r--r-- | core/java/android/view/ViewOverlay.java | 203 |
5 files changed, 387 insertions, 6 deletions
diff --git a/api/current.txt b/api/current.txt index 16681702b928..6302ddf0a405 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24951,6 +24951,14 @@ package android.view { field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff } + public abstract interface Overlay { + method public abstract void add(android.graphics.drawable.Drawable); + method public abstract void add(android.view.View); + method public abstract void clear(); + method public abstract void remove(android.graphics.drawable.Drawable); + method public abstract void remove(android.view.View); + } + public class ScaleGestureDetector { ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener); method public float getCurrentSpan(); @@ -25255,6 +25263,7 @@ package android.view { method public int getNextFocusUpId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method public int getOverScrollMode(); + method public android.view.Overlay getOverlay(); method public int getPaddingBottom(); method public int getPaddingEnd(); method public int getPaddingLeft(); diff --git a/core/java/android/view/Overlay.java b/core/java/android/view/Overlay.java new file mode 100644 index 000000000000..f15d4d2b57dd --- /dev/null +++ b/core/java/android/view/Overlay.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view; + +import android.graphics.drawable.Drawable; + +/** + * An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after + * all other content in that view (including children, if the view is a ViewGroup). Interaction + * with the overlay layer is done in terms of adding/removing views and drawables. Invalidation and + * redrawing of the overlay layer (and its host view) is handled differently for views versus + * drawables in the overlay. Views invalidate themselves as usual, causing appropriate redrawing + * to occur automatically. Drawables, on the other hand, do not manage invalidation, so changes to + * drawable objects should be accompanied by appropriate calls to invalidate() on the host view. + * + * @see android.view.View#getOverlay() + */ +public interface Overlay { + + /** + * Adds a Drawable to the overlay. The bounds of the drawable should be relative to + * the host view. Any drawable added to the overlay should be removed when it is no longer + * needed or no longer visible. There is no automatic invalidation of the host view; changes to + * the drawable should be accompanied by appropriate invalidation calls to the host view + * to cause the proper area of the view, and the overlay, to be redrawn. + * + * @param drawable The Drawable to be added to the overlay. This drawable will be + * drawn when the view redraws its overlay. + * @see #remove(android.graphics.drawable.Drawable) + * @see #add(View) + */ + void add(Drawable drawable); + + /** + * Removes the specified Drawable from the overlay. + * + * @param drawable The Drawable to be removed from the overlay. + * @see #add(android.graphics.drawable.Drawable) + */ + void remove(Drawable drawable); + + /** + * Adds a View to the overlay. The bounds of the added view should be relative to + * the host view. Any view added to the overlay should be removed when it is no longer + * needed or no longer visible. The view must not be parented elsewhere when it is added + * to the overlay. + * + * @param view The View to be added to the overlay. The added view will be + * drawn when the overlay is drawn. + * @see #remove(View) + * @see #add(android.graphics.drawable.Drawable) + */ + void add(View view); + + /** + * Removes the specified View from the overlay. + * + * @param view The View to be removed from the overlay. + * @see #add(View) + */ + void remove(View view); + + /** + * Removes all views and drawables from the overlay. + */ + void clear(); +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2e60f517cd8a..f9b206474774 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3223,6 +3223,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityDelegate mAccessibilityDelegate; /** + * The view's overlay layer. Developers get a reference to the overlay via getOverlay() + * and add/remove objects to/from the overlay directly through the Overlay methods. + */ + ViewOverlay mOverlay; + + /** * Consistency verifier for debugging purposes. * @hide */ @@ -9589,7 +9595,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mDisplayList.setTop(mTop); } - onSizeChanged(width, mBottom - mTop, width, oldHeight); + sizeChange(width, mBottom - mTop, width, oldHeight); if (!matrixIsIdentity) { if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { @@ -9662,7 +9668,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mDisplayList.setBottom(mBottom); } - onSizeChanged(width, mBottom - mTop, width, oldHeight); + sizeChange(width, mBottom - mTop, width, oldHeight); if (!matrixIsIdentity) { if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { @@ -9729,7 +9735,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mDisplayList.setLeft(left); } - onSizeChanged(mRight - mLeft, height, oldWidth, height); + sizeChange(mRight - mLeft, height, oldWidth, height); if (!matrixIsIdentity) { if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { @@ -9793,7 +9799,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mDisplayList.setRight(mRight); } - onSizeChanged(mRight - mLeft, height, oldWidth, height); + sizeChange(mRight - mLeft, height, oldWidth, height); if (!matrixIsIdentity) { if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { @@ -12088,6 +12094,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, void dispatchAttachedToWindow(AttachInfo info, int visibility) { //System.out.println("Attached! " + this); mAttachInfo = info; + if (mOverlay != null) { + mOverlay.mAttachInfo = info; + } mWindowAttachCount++; // We will need to evaluate the drawable state at least once. mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY; @@ -12156,6 +12165,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } mAttachInfo = null; + if (mOverlay != null) { + mOverlay.mAttachInfo = null; + } } /** @@ -12811,6 +12823,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); + if (mOverlay != null && !mOverlay.isEmpty()) { + mOverlay.draw(canvas); + } } else { draw(canvas); } @@ -13124,6 +13139,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); + if (mOverlay != null && !mOverlay.isEmpty()) { + mOverlay.draw(canvas); + } } else { draw(canvas); } @@ -13879,6 +13897,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); + if (mOverlay != null && !mOverlay.isEmpty()) { + mOverlay.dispatchDraw(canvas); + } + // we're done... return; } @@ -14018,6 +14040,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); + + if (mOverlay != null && !mOverlay.isEmpty()) { + mOverlay.dispatchDraw(canvas); + } + } + + /** + * Called by the addToOverlay() methods to create, attach, and size the overlay as necessary + */ + private void setupOverlay() { + if (mOverlay == null) { + mOverlay = new ViewOverlay(mContext, this); + mOverlay.mAttachInfo = mAttachInfo; + mOverlay.setRight(mRight); + mOverlay.setBottom(mBottom); + } + } + + /** + * Returns the overlay for this view, creating it if it does not yet exist. Adding drawables + * and/or views to the overlay will cause them to be displayed whenever the view itself is + * redrawn. Objects in the overlay should be actively managed: remove them when they should + * not be displayed anymore and invalidate this view appropriately when overlay drawables + * change. The overlay will always have the same size as its host view. + * + * @return The Overlay object for this view. + * @see Overlay + */ + public Overlay getOverlay() { + setupOverlay(); + return mOverlay; } /** @@ -14273,7 +14326,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTransformationInfo.mMatrixDirty = true; } } - onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); + sizeChange(newWidth, newHeight, oldWidth, oldHeight); } if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) { @@ -14297,6 +14350,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return changed; } + private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) { + onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); + if (mOverlay != null) { + mOverlay.setRight(mRight); + mOverlay.setBottom(mBottom); + } + } + /** * Finalize inflating a view from XML. This is called as the last phase * of inflation, after all child views have been added. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 442dfdbee73e..d63f7bc8de89 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1869,7 +1869,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager removePointersFromTouchTargets(idBitsToAssign); final int childrenCount = mChildrenCount; - if (childrenCount != 0) { + if (childrenCount != 0 || mOverlay != null) { // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; @@ -1906,6 +1906,27 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager break; } } + if (mOverlay != null && newTouchTarget == null) { + // Check to see whether the overlay can handle the event + final View child = mOverlay; + if (canViewReceivePointerEvents(child) && + isTransformedTouchPointInView(x, y, child, null)) { + newTouchTarget = getTouchTarget(child); + if (newTouchTarget != null) { + newTouchTarget.pointerIdBits |= idBitsToAssign; + } else { + resetCancelNextUpFlag(child); + if (dispatchTransformedTouchEvent(ev, false, child, + idBitsToAssign)) { + mLastTouchDownTime = ev.getDownTime(); + mLastTouchDownX = ev.getX(); + mLastTouchDownY = ev.getY(); + newTouchTarget = addTouchTarget(child, idBitsToAssign); + alreadyDispatchedToNewTouchTarget = true; + } + } + } + } } if (newTouchTarget == null && mFirstTouchTarget != null) { @@ -3022,6 +3043,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.mRecreateDisplayList = false; } } + if (mOverlay != null) { + mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED) + == PFLAG_INVALIDATED; + mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED; + mOverlay.getDisplayList(); + mOverlay.mRecreateDisplayList = false; + } } /** diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java new file mode 100644 index 000000000000..8c2ab9da77a2 --- /dev/null +++ b/core/java/android/view/ViewOverlay.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +import java.util.ArrayList; + +/** + * ViewOverlay is a container that View uses to host all objects (views and drawables) that + * are added to its "overlay", gotten through {@link View#getOverlay()}. Views and drawables are + * added to the overlay via the add/remove methods in this class. These views and drawables are + * then drawn whenever the view itself is drawn, after which it will draw its overlay (if it + * exists). + * + * Besides managing and drawing the list of drawables, this class serves two purposes: + * (1) it noops layout calls because children are absolutely positioned and + * (2) it forwards all invalidation calls to its host view. The invalidation redirect is + * necessary because the overlay is not a child of the host view and invalidation cannot + * therefore follow the normal path up through the parent hierarchy. + * + * @hide + */ +class ViewOverlay extends ViewGroup implements Overlay { + + /** + * The View for which this is an overlay. Invalidations of the overlay are redirected to + * this host view. + */ + View mHostView; + + /** + * The set of drawables to draw when the overlay is rendered. + */ + ArrayList<Drawable> mDrawables = null; + + ViewOverlay(Context context, View host) { + super(context); + mHostView = host; + mParent = mHostView.getParent(); + } + + @Override + public void add(Drawable drawable) { + if (mDrawables == null) { + mDrawables = new ArrayList<Drawable>(); + } + if (!mDrawables.contains(drawable)) { + // Make each drawable unique in the overlay; can't add it more than once + mDrawables.add(drawable); + invalidate(drawable.getBounds()); + } + } + + @Override + public void remove(Drawable drawable) { + if (mDrawables != null) { + mDrawables.remove(drawable); + invalidate(drawable.getBounds()); + } + } + + @Override + public void add(View child) { + super.addView(child); + } + + @Override + public void remove(View view) { + super.removeView(view); + } + + @Override + public void clear() { + removeAllViews(); + mDrawables.clear(); + } + + boolean isEmpty() { + if (getChildCount() == 0 && (mDrawables == null || mDrawables.size() == 0)) { + return true; + } + return false; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size(); + for (int i = 0; i < numDrawables; ++i) { + mDrawables.get(i).draw(canvas); + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + // Noop: children are positioned absolutely + } + + /* + The following invalidation overrides exist for the purpose of redirecting invalidation to + the host view. The overlay is not parented to the host view (since a View cannot be a parent), + so the invalidation cannot proceed through the normal parent hierarchy. + There is a built-in assumption that the overlay exactly covers the host view, therefore + the invalidation rectangles received do not need to be adjusted when forwarded to + the host view. + */ + + @Override + public void invalidate(Rect dirty) { + super.invalidate(dirty); + if (mHostView != null) { + dirty.offset(getLeft(), getTop()); + mHostView.invalidate(dirty); + } + } + + @Override + public void invalidate(int l, int t, int r, int b) { + super.invalidate(l, t, r, b); + if (mHostView != null) { + mHostView.invalidate(l, t, r, b); + } + } + + @Override + public void invalidate() { + super.invalidate(); + if (mHostView != null) { + mHostView.invalidate(); + } + } + + @Override + void invalidate(boolean invalidateCache) { + super.invalidate(invalidateCache); + if (mHostView != null) { + mHostView.invalidate(invalidateCache); + } + } + + @Override + void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) { + super.invalidateViewProperty(invalidateParent, forceRedraw); + if (mHostView != null) { + mHostView.invalidateViewProperty(invalidateParent, forceRedraw); + } + } + + @Override + protected void invalidateParentCaches() { + super.invalidateParentCaches(); + if (mHostView != null) { + mHostView.invalidateParentCaches(); + } + } + + @Override + protected void invalidateParentIfNeeded() { + super.invalidateParentIfNeeded(); + if (mHostView != null) { + mHostView.invalidateParentIfNeeded(); + } + } + + public void invalidateChildFast(View child, final Rect dirty) { + if (mHostView != null) { + // Note: This is not a "fast" invalidation. Would be nice to instead invalidate using DL + // properties and a dirty rect instead of causing a real invalidation of the host view + int left = child.mLeft; + int top = child.mTop; + if (!child.getMatrix().isIdentity()) { + child.transformRect(dirty); + } + dirty.offset(left, top); + mHostView.invalidate(dirty); + } + } + + @Override + public ViewParent invalidateChildInParent(int[] location, Rect dirty) { + if (mHostView != null) { + mHostView.invalidate(dirty); + } + return null; + } +} |