From f796309c9492475cc1911d22fda5319d6d4c4853 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Mon, 16 Sep 2013 11:26:36 -0700 Subject: Refactor Drag Shadow Bug: 10759985 Bug: 10686781 Change-Id: I8f6446ffe1e882c39c38e61c21a60561d91d1e2e --- .../android/dialer/list/PhoneFavoriteListView.java | 134 ++++++++++++++++++--- .../android/dialer/list/PhoneFavoriteTileView.java | 5 +- .../dialer/list/PhoneFavoritesTileAdapter.java | 40 +++--- 3 files changed, 145 insertions(+), 34 deletions(-) diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java index 4636e1794..4ce5df15f 100644 --- a/src/com/android/dialer/list/PhoneFavoriteListView.java +++ b/src/com/android/dialer/list/PhoneFavoriteListView.java @@ -19,6 +19,9 @@ package com.android.dialer.list; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; @@ -63,6 +66,20 @@ public class PhoneFavoriteListView extends ListView implements SwipeHelperCallba private boolean mIsDragScrollerRunning = false; private int mTouchDownForDragStartX; private int mTouchDownForDragStartY; + private Bitmap mDragShadowBitmap; + + // X and Y offsets inside the item from where the user grabbed to the + // child's left coordinate. This is used to aid in the drawing of the drag shadow. + private int mTouchOffsetToChildLeft; + private int mTouchOffsetToChildTop; + + private int mDragShadowLeft; + private int mDragShadowTop; + private int mDragShadowWidth; + private int mDragShadowHeight; + + private final int DRAG_SHADOW_ALPHA = 180; + private final Paint mPaint = new Paint(); /** * {@link #mTopScrollBound} and {@link mBottomScrollBound} will be @@ -97,6 +114,7 @@ public class PhoneFavoriteListView extends ListView implements SwipeHelperCallba mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, mDensityScale, mTouchSlop); setItemsCanFocus(true); + mPaint.setAlpha(DRAG_SHADOW_ALPHA); } @Override @@ -206,7 +224,9 @@ public class PhoneFavoriteListView extends ListView implements SwipeHelperCallba final int eY = (int) event.getY(); switch (action) { case DragEvent.ACTION_DRAG_STARTED: - handleDragStarted(mTouchDownForDragStartX, mTouchDownForDragStartY); + if (!handleDragStarted(mTouchDownForDragStartX, mTouchDownForDragStartY)) { + return false; + }; break; case DragEvent.ACTION_DRAG_LOCATION: mLastDragY = eY; @@ -232,7 +252,7 @@ public class PhoneFavoriteListView extends ListView implements SwipeHelperCallba mScrollHandler.removeCallbacks(mDragScroller); mIsDragScrollerRunning = false; if (action != DragEvent.ACTION_DRAG_EXITED) { - handleDragFinished(); + handleDragFinished(eX, eY); } break; default: @@ -242,6 +262,15 @@ public class PhoneFavoriteListView extends ListView implements SwipeHelperCallba return true; } + @Override + public void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + // Draw the drag shadow at its last known location if the drag shadow exists. + if (mDragShadowBitmap != null) { + canvas.drawBitmap(mDragShadowBitmap, mDragShadowLeft, mDragShadowTop, mPaint); + } + } + /** * Find the view under the pointer. */ @@ -263,34 +292,109 @@ public class PhoneFavoriteListView extends ListView implements SwipeHelperCallba } } - private void handleDragStarted(int x, int y) { + /** + * @return True if the drag is started. + */ + private boolean handleDragStarted(int x, int y) { final View child = getViewAtPosition(x, y); - if (child instanceof ContactTileRow) { - final ContactTileRow tile = (ContactTileRow) child; - final int itemIndex = tile.getItemIndex(x, y); - if (itemIndex != -1 && mOnDragDropListener != null) { - mOnDragDropListener.onDragStarted(itemIndex); + if (!(child instanceof ContactTileRow)) { + // Bail early. + return false; + } + + final ContactTileRow tile = (ContactTileRow) child; + final int itemIndex = tile.getItemIndex(x, y); + if (itemIndex != -1 && mOnDragDropListener != null) { + final PhoneFavoriteTileView tileView = + (PhoneFavoriteTileView) tile.getViewAtPosition(x, y); + mDragShadowBitmap = createDraggedChildBitmap(tileView); + if (mDragShadowBitmap == null) { + return false; + } + + if (tileView instanceof PhoneFavoriteRegularRowView) { + mDragShadowLeft = tile.getLeft(); + mDragShadowTop = tile.getTop(); + } else { + // Square tile is relative to the contact tile, + // and contact tile is relative to this list view. + mDragShadowLeft = tileView.getLeft() + tileView.getParentRow().getLeft(); + mDragShadowTop = tileView.getTop() + tileView.getParentRow().getTop(); } + mDragShadowWidth = tileView.getWidth(); + mDragShadowHeight = tileView.getHeight(); + + // x and y passed in are the coordinates of where the user has touched down, calculate + // the offset to the top left coordinate of the dragged child. This will be used for + // drawing the drag shadow. + mTouchOffsetToChildLeft = x - mDragShadowLeft; + mTouchOffsetToChildTop = y - mDragShadowTop; + + // invalidate to trigger a redraw of the drag shadow. + invalidate(); + + mOnDragDropListener.onDragStarted(itemIndex); } + + return true; } private void handleDragHovered(int x, int y) { final View child = getViewAtPosition(x, y); - if (child instanceof ContactTileRow) { - final ContactTileRow tile = (ContactTileRow) child; - final int itemIndex = tile.getItemIndex(x, y); - if (itemIndex != -1 && mOnDragDropListener != null) { - mOnDragDropListener.onDragHovered(itemIndex); - } + if (!(child instanceof ContactTileRow)) { + // Bail early. + return; + } + + // Update the drag shadow location. + mDragShadowLeft = x - mTouchOffsetToChildLeft; + mDragShadowTop = y - mTouchOffsetToChildTop; + + // invalidate to trigger a redraw of the drag shadow. + invalidate(); + + final ContactTileRow tile = (ContactTileRow) child; + final int itemIndex = tile.getItemIndex(x, y); + if (itemIndex != -1 && mOnDragDropListener != null) { + mOnDragDropListener.onDragHovered(itemIndex); } } - private void handleDragFinished() { + private void handleDragFinished(int x, int y) { + // Update the drag shadow location. + mDragShadowLeft = x - mTouchOffsetToChildLeft; + mDragShadowTop = y - mTouchOffsetToChildTop; + + if (mDragShadowBitmap != null) { + mDragShadowBitmap.recycle(); + mDragShadowBitmap = null; + } + if (mOnDragDropListener != null) { mOnDragDropListener.onDragFinished(); } } + private Bitmap createDraggedChildBitmap(View view) { + view.setDrawingCacheEnabled(true); + final Bitmap cache = view.getDrawingCache(); + + Bitmap bitmap = null; + if (cache != null) { + try { + bitmap = cache.copy(Bitmap.Config.ARGB_8888, false); + } catch (final OutOfMemoryError e) { + Log.w(LOG_TAG, "Failed to copy bitmap from Drawing cache", e); + bitmap = null; + } + } + + view.destroyDrawingCache(); + view.setDrawingCacheEnabled(false); + + return bitmap; + } + public interface OnDragDropListener { public void onDragStarted(int itemIndex); public void onDragHovered(int itemIndex); diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java index 374ff874c..5a3f4e54d 100644 --- a/src/com/android/dialer/list/PhoneFavoriteTileView.java +++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java @@ -92,16 +92,17 @@ public abstract class PhoneFavoriteTileView extends ContactTileView { public boolean onLongClick(View v) { setPressed(false); final PhoneFavoriteTileView view = (PhoneFavoriteTileView) v; + // NOTE The drag shadow is handled in the ListView. if (view instanceof PhoneFavoriteRegularRowView) { final ContactTileRow parent = (ContactTileRow) view.getParentRow(); // If the view is regular row, start drag the row view. // Drag is not available for the item exceeds the PIN_LIMIT. if (parent.getRegularRowItemIndex() < PhoneFavoritesTileAdapter.PIN_LIMIT) { - parent.startDrag(null, new View.DragShadowBuilder(parent), null, 0); + parent.startDrag(null, new View.DragShadowBuilder(), null, 0); } } else { // If the view is a tile view, start drag the tile. - view.startDrag(null, new View.DragShadowBuilder(view), null, 0); + view.startDrag(null, new View.DragShadowBuilder(), null, 0); } return true; } diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java index 8d0fb8647..779b15dc4 100644 --- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java +++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java @@ -634,6 +634,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements PinnedPositions.STAR_WHEN_PINNING, "true").build(); // update the database here with the new pinned positions mContext.getContentResolver().update(pinUri, cv, null, null); + notifyDataSetChanged(); } mDraggedEntry = null; } @@ -970,31 +971,36 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements return mPosition; } - @Override - public View getChildAtPosition(MotionEvent ev) { + /** + * Find the view under the pointer. + */ + public View getViewAtPosition(int x, int y) { // find the view under the pointer, accounting for GONE views final int count = getChildCount(); - final int touchX = (int) ev.getX(); - View slidingChild; + View view; for (int childIdx = 0; childIdx < count; childIdx++) { - slidingChild = getChildAt(childIdx); - if (slidingChild.getVisibility() == GONE) { - continue; - } - if (touchX >= slidingChild.getLeft() && touchX <= slidingChild.getRight()) { - if (SwipeHelper.isSwipeable(slidingChild)) { - // If this view is swipable, then return it. If not, because the removal - // dialog is currently showing, then return a null view, which will simply - // be ignored by the swipe helper. - return slidingChild; - } else { - return null; - } + view = getChildAt(childIdx); + if (x >= view.getLeft() && x <= view.getRight()) { + return view; } } return null; } + @Override + public View getChildAtPosition(MotionEvent ev) { + final View view = getViewAtPosition((int) ev.getX(), (int) ev.getY()); + if (view != null && + SwipeHelper.isSwipeable(view) && + view.getVisibility() != GONE) { + // If this view is swipable, then return it. If not, because the removal + // dialog is currently showing, then return a null view, which will simply + // be ignored by the swipe helper. + return view; + } + return null; + } + @Override public View getChildContentView(View v) { return v.findViewById(R.id.contact_favorite_card); -- cgit v1.2.3