diff options
author | Adam Cohen <adamcohen@google.com> | 2010-08-19 16:49:52 -0700 |
---|---|---|
committer | Adam Cohen <adamcohen@google.com> | 2010-08-25 13:16:14 -0700 |
commit | 9b073948cfb84c0dd04f8a94ee1f7f263f027c83 (patch) | |
tree | 40a111e61be846c7eee74b9843f8a5f8b183a6bc /core | |
parent | 3c1fbf7ad2766a2942c326abd6692286949aee2f (diff) | |
download | frameworks_base-9b073948cfb84c0dd04f8a94ee1f7f263f027c83.zip frameworks_base-9b073948cfb84c0dd04f8a94ee1f7f263f027c83.tar.gz frameworks_base-9b073948cfb84c0dd04f8a94ee1f7f263f027c83.tar.bz2 |
Cleanup and bug fixes in StackView
-> pushed functionality from AdapterViewAnimator to StackView
-> only modifying clipping flags as far up the view
hierarchy as needed
-> still need framework level solution for drawing outside bounds
ViewGroup touch dispatch temp fix
-> Touch event was being modified and not reset in some
code paths.
Change-Id: I8135b62e2d73f239b9df407b0fa93bc434796989
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/view/ViewGroup.java | 14 | ||||
-rw-r--r-- | core/java/android/widget/AdapterViewAnimator.java | 112 | ||||
-rw-r--r-- | core/java/android/widget/StackView.java | 297 |
3 files changed, 238 insertions, 185 deletions
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 3678684..9770313 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -970,7 +970,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mMotionTarget = null; } - return target.dispatchTouchEvent(ev); + if (target.dispatchTouchEvent(ev)) { + return true; + } else { + ev.setLocation(xf, yf); + } + return false; } /** @@ -1006,7 +1011,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // There are many subtle interactions in touch event dispatch; change at your own risk. child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; ev.offsetLocation(localX - x, localY - y); - return child.dispatchTouchEvent(ev); + if (child.dispatchTouchEvent(ev)) { + return true; + } else { + ev.offsetLocation(x - localX, y - localY); + return false; + } } return false; } diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index b6c8e47..e78c8b7 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; import android.os.Looper; import android.os.Parcel; @@ -323,15 +324,13 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> return null; } - private LayoutParams createOrReuseLayoutParams(View v) { + LayoutParams createOrReuseLayoutParams(View v) { final ViewGroup.LayoutParams currentLp = v.getLayoutParams(); - if (currentLp instanceof LayoutParams) { + if (currentLp instanceof ViewGroup.LayoutParams) { LayoutParams lp = (LayoutParams) currentLp; - lp.setHorizontalOffset(0); - lp.setVerticalOffset(0); return lp; } - return new LayoutParams(v); + return new ViewGroup.LayoutParams(0, 0); } void showOnly(int childIndex, boolean animate, boolean onLayout) { @@ -472,10 +471,9 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> int childRight = mPaddingLeft + child.getMeasuredWidth(); int childBottom = mPaddingTop + child.getMeasuredHeight(); - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset, - childRight + lp.horizontalOffset, childBottom + lp.verticalOffset); + child.layout(mPaddingLeft, mPaddingTop, + childRight, childBottom); } mDataChanged = false; } @@ -738,102 +736,4 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> setAdapter(mRemoteViewsAdapter); } } - - private final Rect dirtyRect = new Rect(); - @Override - public void removeViewInLayout(View view) { - // TODO: need to investigate this block a bit more - // and perhaps fix some other invalidations issues. - View parent = null; - view.setVisibility(INVISIBLE); - if (view.getLayoutParams() instanceof LayoutParams) { - LayoutParams lp = (LayoutParams) view.getLayoutParams(); - parent = lp.getParentAndDirtyRegion(dirtyRect); - } - - super.removeViewInLayout(view); - - if (parent != null) - parent.invalidate(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom); - } - - static class LayoutParams extends ViewGroup.LayoutParams { - int horizontalOffset; - int verticalOffset; - View mView; - - LayoutParams(View view) { - super(0, 0); - horizontalOffset = 0; - verticalOffset = 0; - mView = view; - } - - LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - horizontalOffset = 0; - verticalOffset = 0; - } - - private Rect parentRect = new Rect(); - void invalidateGlobalRegion(View v, Rect r) { - View p = v; - boolean firstPass = true; - parentRect.set(0, 0, 0, 0); - while (p.getParent() != null && p.getParent() instanceof View - && !parentRect.contains(r)) { - if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY()); - firstPass = false; - p = (View) p.getParent(); - parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(), - p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY()); - } - p.invalidate(r.left, r.top, r.right, r.bottom); - } - - public View getParentAndDirtyRegion(Rect globalRect) { - globalRect.set(mView.getLeft(), mView.getTop(), mView.getRight(), mView.getBottom()); - View p = mView; - boolean firstPass = true; - parentRect.set(0, 0, 0, 0); - while (p.getParent() != null && p.getParent() instanceof View - && !parentRect.contains(globalRect)) { - if (!firstPass) { - globalRect.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY()); - } - - firstPass = false; - p = (View) p.getParent(); - parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(), - p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY()); - } - return p; - } - - private Rect invalidateRect = new Rect(); - // This is public so that PropertyAnimator can access it - public void setVerticalOffset(int newVerticalOffset) { - int offsetDelta = newVerticalOffset - verticalOffset; - verticalOffset = newVerticalOffset; - if (mView != null) { - mView.requestLayout(); - int top = Math.min(mView.getTop() + offsetDelta, mView.getTop()); - int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom()); - invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom); - invalidateGlobalRegion(mView, invalidateRect); - } - } - - public void setHorizontalOffset(int newHorizontalOffset) { - int offsetDelta = newHorizontalOffset - horizontalOffset; - horizontalOffset = newHorizontalOffset; - if (mView != null) { - mView.requestLayout(); - int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft()); - int right = Math.max(mView.getRight() + offsetDelta, mView.getRight()); - invalidateRect.set(left, mView.getTop(), right, mView.getBottom()); - invalidateGlobalRegion(mView, invalidateRect); - } - } - } } diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index c3e8838..b460adb 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -27,6 +27,7 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; @@ -66,8 +67,6 @@ public class StackView extends AdapterViewAnimator { private static final float SLIDE_UP_RATIO = 0.7f; private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>(); - private final WeakHashMap<View, Integer> - mChildrenToApplyTransformsTo = new WeakHashMap<View, Integer>(); /** * Sentinel value for no current active pointer. @@ -90,9 +89,12 @@ public class StackView extends AdapterViewAnimator { private int mMaximumVelocity; private VelocityTracker mVelocityTracker; + private static HolographicHelper sHolographicHelper; private ImageView mHighlight; private StackSlider mStackSlider; private boolean mFirstLayoutHappened = false; + private ViewGroup mAncestorContainingAllChildren = null; + private int mAncestorHeight = 0; public StackView(Context context) { super(context); @@ -117,9 +119,11 @@ public class StackView extends AdapterViewAnimator { addViewInLayout(mHighlight, -1, new LayoutParams(mHighlight)); mStackSlider = new StackSlider(); - if (!sPaintsInitialized) { - initializePaints(); + if (sHolographicHelper == null) { + sHolographicHelper = new HolographicHelper(); } + setClipChildren(false); + setClipToPadding(false); } /** @@ -205,6 +209,7 @@ public class StackView extends AdapterViewAnimator { if (!mRotations.containsKey(child)) { float rotation = (float) (Math.random()*26 - 13); mRotations.put(child, rotation); + child.setRotation(rotation); } // Child has been removed @@ -212,47 +217,36 @@ public class StackView extends AdapterViewAnimator { if (mRotations.containsKey(child)) { mRotations.remove(child); } - if (mChildrenToApplyTransformsTo.containsKey(child)) { - mChildrenToApplyTransformsTo.remove(child); - } } - - // if this view is already in the layout, we need to - // wait until layout has finished in order to set the - // pivot point of the rotation (requiring getMeasuredWidth/Height()) - mChildrenToApplyTransformsTo.put(child, relativeIndex); } @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - if (!mChildrenToApplyTransformsTo.isEmpty()) { - for (View child: mChildrenToApplyTransformsTo.keySet()) { - if (mRotations.containsKey(child)) { - child.setPivotX(child.getMeasuredWidth()/2); - child.setPivotY(child.getMeasuredHeight()/2); - child.setRotation(mRotations.get(child)); - } + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + } + + // TODO: right now, this code walks up the hierarchy as far as needed and disables clipping + // so that the stack's children can draw outside of the stack's bounds. This is fine within + // the context of widgets in the launcher, but is destructive in general, as the clipping + // values are not being reset. For this to be a full framework level widget, we will need + // framework level support for drawing outside of a parent's bounds. + private void disableParentalClipping() { + if (mAncestorContainingAllChildren != null) { + Log.v(TAG, "Disabling parental clipping."); + ViewGroup vg = this; + while (vg.getParent() != null && vg.getParent() instanceof ViewGroup) { + if (vg == mAncestorContainingAllChildren) break; + vg = (ViewGroup) vg.getParent(); + vg.setClipChildren(false); + vg.setClipToPadding(false); } - mChildrenToApplyTransformsTo.clear(); } + } + private void onLayout() { if (!mFirstLayoutHappened) { mViewHeight = Math.round(SLIDE_UP_RATIO*getMeasuredHeight()); mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight); - - // TODO: Right now this walks all the way up the view hierarchy and disables - // ClipChildren and ClipToPadding. We're probably going to want to reset - // these flags as well. - setClipChildren(false); - setClipToPadding(false); - ViewGroup view = this; - while (view.getParent() != null && view.getParent() instanceof ViewGroup) { - view = (ViewGroup) view.getParent(); - view.setClipChildren(false); - view.setClipToPadding(false); - } mFirstLayoutHappened = true; } } @@ -261,7 +255,6 @@ public class StackView extends AdapterViewAnimator { public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch(action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: { if (mActivePointerId == INVALID_POINTER) { mInitialX = ev.getX(); @@ -320,7 +313,7 @@ public class StackView extends AdapterViewAnimator { View v = getViewAtRelativeIndex(activeIndex); if (v == null) return; - mHighlight.setImageBitmap(createOutline(v)); + mHighlight.setImageBitmap(sHolographicHelper.createOutline(v)); mHighlight.bringToFront(); v.bringToFront(); mStackSlider.setView(v); @@ -640,7 +633,6 @@ public class StackView extends AdapterViewAnimator { float getXProgress() { return mXProgress; } - } @Override @@ -649,55 +641,206 @@ public class StackView extends AdapterViewAnimator { setDisplayedChild(mWhichChild); } - private static final Paint sHolographicPaint = new Paint(); - private static final Paint sErasePaint = new Paint(); - private static boolean sPaintsInitialized = false; - private static final float STROKE_WIDTH = 3.0f; - - static void initializePaints() { - sHolographicPaint.setColor(0xff6699ff); - sHolographicPaint.setFilterBitmap(true); - sErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); - sErasePaint.setFilterBitmap(true); - sPaintsInitialized = true; + LayoutParams createOrReuseLayoutParams(View v) { + final ViewGroup.LayoutParams currentLp = v.getLayoutParams(); + if (currentLp instanceof LayoutParams) { + LayoutParams lp = (LayoutParams) currentLp; + lp.setHorizontalOffset(0); + lp.setVerticalOffset(0); + return lp; + } + return new LayoutParams(v); } - static Bitmap createOutline(View v) { - if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) { - return null; + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + boolean dataChanged = mDataChanged; + if (dataChanged) { + handleDataChanged(); + + // if the data changes, mWhichChild might be out of the bounds of the adapter + // in this case, we reset mWhichChild to the beginning + if (mWhichChild >= mAdapter.getCount()) + mWhichChild = 0; + + showOnly(mWhichChild, true, true); + } + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + + int childRight = mPaddingLeft + child.getMeasuredWidth(); + int childBottom = mPaddingTop + child.getMeasuredHeight(); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset, + childRight + lp.horizontalOffset, childBottom + lp.verticalOffset); + + //TODO: temp until fix in View + child.setPivotX(child.getMeasuredWidth()/2); + child.setPivotY(child.getMeasuredHeight()/2); + } + + mDataChanged = false; + onLayout(); + } + + class LayoutParams extends ViewGroup.LayoutParams { + int horizontalOffset; + int verticalOffset; + View mView; + + LayoutParams(View view) { + super(0, 0); + horizontalOffset = 0; + verticalOffset = 0; + mView = view; + } + + LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + horizontalOffset = 0; + verticalOffset = 0; } - Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), - Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); + private Rect parentRect = new Rect(); + void invalidateGlobalRegion(View v, Rect r) { + View p = v; + if (!(v.getParent() != null && v.getParent() instanceof View)) return; + + View gp = (View) v.getParent(); + boolean firstPass = true; + parentRect.set(0, 0, 0, 0); + int depth = 0; + while (gp.getParent() != null && gp.getParent() instanceof View + && !parentRect.contains(r)) { + if (!firstPass) { + r.offset(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY()); + depth++; + } + firstPass = false; + p = (View) p.getParent(); + gp = (View) p.getParent(); + parentRect.set(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY(), + p.getRight() - gp.getScrollX(), p.getBottom() - gp.getScrollY()); + } + + if (depth > mAncestorHeight) { + mAncestorContainingAllChildren = (ViewGroup) p; + mAncestorHeight = depth; + disableParentalClipping(); + } + + p.invalidate(r.left, r.top, r.right, r.bottom); + } - float rotationX = v.getRotationX(); - v.setRotationX(0); - canvas.concat(v.getMatrix()); - v.draw(canvas); - v.setRotationX(rotationX); + private Rect invalidateRect = new Rect(); + private RectF invalidateRectf = new RectF(); + // This is public so that PropertyAnimator can access it + public void setVerticalOffset(int newVerticalOffset) { + int offsetDelta = newVerticalOffset - verticalOffset; + verticalOffset = newVerticalOffset; - Bitmap outlineBitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), - Bitmap.Config.ARGB_8888); - Canvas outlineCanvas = new Canvas(outlineBitmap); - drawOutline(outlineCanvas, bitmap); - bitmap.recycle(); - return outlineBitmap; + if (mView != null) { + mView.requestLayout(); + int top = Math.min(mView.getTop() + offsetDelta, mView.getTop()); + int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom()); + + invalidateRectf.set(mView.getLeft(), top, mView.getRight(), bottom); + + float xoffset = -invalidateRectf.left; + float yoffset = -invalidateRectf.top; + invalidateRectf.offset(xoffset, yoffset); + mView.getMatrix().mapRect(invalidateRectf); + invalidateRectf.offset(-xoffset, -yoffset); + invalidateRect.set((int) Math.floor(invalidateRectf.left), + (int) Math.floor(invalidateRectf.top), + (int) Math.ceil(invalidateRectf.right), + (int) Math.ceil(invalidateRectf.bottom)); + + invalidateGlobalRegion(mView, invalidateRect); + } + } + + public void setHorizontalOffset(int newHorizontalOffset) { + int offsetDelta = newHorizontalOffset - horizontalOffset; + horizontalOffset = newHorizontalOffset; + + if (mView != null) { + mView.requestLayout(); + int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft()); + int right = Math.max(mView.getRight() + offsetDelta, mView.getRight()); + invalidateRectf.set(left, mView.getTop(), right, mView.getBottom()); + + float xoffset = -invalidateRectf.left; + float yoffset = -invalidateRectf.top; + invalidateRectf.offset(xoffset, yoffset); + mView.getMatrix().mapRect(invalidateRectf); + invalidateRectf.offset(-xoffset, -yoffset); + + invalidateRect.set((int) Math.floor(invalidateRectf.left), + (int) Math.floor(invalidateRectf.top), + (int) Math.ceil(invalidateRectf.right), + (int) Math.ceil(invalidateRectf.bottom)); + + invalidateGlobalRegion(mView, invalidateRect); + } + } } - static void drawOutline(Canvas dest, Bitmap src) { - dest.drawColor(0, PorterDuff.Mode.CLEAR); + private static class HolographicHelper { + private final Paint mHolographicPaint = new Paint(); + private final Paint mErasePaint = new Paint(); + private final float STROKE_WIDTH = 3.0f; - Bitmap mask = src.extractAlpha(); - Matrix id = new Matrix(); + HolographicHelper() { + initializePaints(); + } + + void initializePaints() { + mHolographicPaint.setColor(0xff6699ff); + mHolographicPaint.setFilterBitmap(true); + mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + mErasePaint.setFilterBitmap(true); + } + + Bitmap createOutline(View v) { + if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) { + return null; + } + + Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); - Matrix m = new Matrix(); - float xScale = STROKE_WIDTH*2/(src.getWidth()); - float yScale = STROKE_WIDTH*2/(src.getHeight()); - m.preScale(1+xScale, 1+yScale, src.getWidth()/2, src.getHeight()/2); - dest.drawBitmap(mask, m, sHolographicPaint); + float rotationX = v.getRotationX(); + v.setRotationX(0); + canvas.concat(v.getMatrix()); + v.draw(canvas); - dest.drawBitmap(src, id, sErasePaint); - mask.recycle(); + v.setRotationX(rotationX); + + drawOutline(canvas, bitmap); + return bitmap; + } + + final Matrix id = new Matrix(); + final Matrix scaleMatrix = new Matrix(); + void drawOutline(Canvas dest, Bitmap src) { + Bitmap mask = src.extractAlpha(); + + dest.drawColor(0, PorterDuff.Mode.CLEAR); + + float xScale = STROKE_WIDTH*2/(dest.getWidth()); + float yScale = STROKE_WIDTH*2/(dest.getHeight()); + + scaleMatrix.reset(); + scaleMatrix.preScale(1+xScale, 1+yScale, dest.getWidth()/2, dest.getHeight()/2); + dest.setMatrix(id); + dest.drawBitmap(mask, scaleMatrix, mHolographicPaint); + dest.drawBitmap(mask, id, mErasePaint); + mask.recycle(); + } } } |