diff --git a/CHANGELOG.md b/CHANGELOG.md index 8517b7a..7ee0ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ Changelog ========= +0.6.0 +----- + +* Remove modes are now `fling_remove` and `click_remove`. + +0.5.0 +----- + +* Multiple-choice and single-choice selections handled. + 0.4.0 ----- diff --git a/README.md b/README.md index e06c2e3..f0880ca 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ DragSortListView News ---- +**Feb. 9, 2013**: Version 0.6.0. Consolidated remove modes to +`click_remove` and `fling_remove`. No more fling remove while +dragging; fling anywhere on item to remove it. +[Leszek Mzyk](https://github.com/imbryk) is a bona fide code-slanger. + **Jan. 10, 2013**: Version 0.5.0 is released. Supports ListView multi-choice and single-choice modes thanks to the hard work of [Mattias Flodin](https://github.com/mattiasflodin)! Awesome-sauce. @@ -152,16 +157,16 @@ of 1 means items snap from position to position without animation. * `remove_mode`: (enum, "flingRight") Sets the gesture for removing the dragged item. + "clickRemove": Click on item child View with id `click_remove_id`. - + "flingRight": Fling to the right; get outta here! - + "flingLeft": Fling to the left; sayonara sucker! - + "slideRight": Floating View fades as you slide your finger - to the right; lifting while faded removes item. - + "slideLeft": Floating View fades as you slide your finger - to the right; lifting while faded removes item. + + "flingRemove": Fling horizontal anywhere on item. * `click_remove_id`: (id, 0) Android resource id that points to a child View of a list item. When `remove_mode="clickRemove"` and `remove_enabled="true"`, a click on this child View removes the containing item. This attr is used by DragSortController. +* `fling_handle_id`: (id, 0) Android resource id that points to a + child View of a list item. When `remove_mode="flingRemove"` and + `remove_enabled="true"`, a fling that originates on this child + View removes the containing item. This attr is used by + DragSortController. ### Listeners @@ -355,7 +360,7 @@ dependency to your pom.xml: com.mobeta.android.dslv drag-sort-listview - 0.3.0-SNAPSHOT + 0.6.0-SNAPSHOT apklib ``` diff --git a/demo/pom.xml b/demo/pom.xml index 66bb052..173a549 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -20,14 +20,14 @@ Copyright 2012 Andreas Schildbach com.mobeta.android.dslv parent - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT ../pom.xml com.mobeta.android.demodslv drag-sort-listview-demo apk - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT diff --git a/demo/res/layout/hetero_main.xml b/demo/res/layout/hetero_main.xml index f111f70..397d009 100644 --- a/demo/res/layout/hetero_main.xml +++ b/demo/res/layout/hetero_main.xml @@ -22,4 +22,4 @@ dslv:sort_enabled="true" dslv:remove_enabled="true" dslv:drag_start_mode="onDown" - dslv:remove_mode="flingLeft" /> + dslv:remove_mode="flingRemove" /> diff --git a/demo/res/values/strings.xml b/demo/res/values/strings.xml index 04fab69..533fc7c 100644 --- a/demo/res/values/strings.xml +++ b/demo/res/values/strings.xml @@ -57,10 +57,7 @@ Add footer Click remove - Fling right - Fling left - Slide right - Slide left + Fling remove On down diff --git a/demo/src/com/mobeta/android/demodslv/DSLVFragment.java b/demo/src/com/mobeta/android/demodslv/DSLVFragment.java index b9143bc..5bcdd8f 100644 --- a/demo/src/com/mobeta/android/demodslv/DSLVFragment.java +++ b/demo/src/com/mobeta/android/demodslv/DSLVFragment.java @@ -53,9 +53,10 @@ protected int getLayout() { * Return list item layout resource passed to the ArrayAdapter. */ protected int getItemLayout() { - if (removeMode == DragSortController.FLING_LEFT_REMOVE || removeMode == DragSortController.SLIDE_LEFT_REMOVE) { + /*if (removeMode == DragSortController.FLING_LEFT_REMOVE || removeMode == DragSortController.SLIDE_LEFT_REMOVE) { return R.layout.list_item_handle_right; - } else if (removeMode == DragSortController.CLICK_REMOVE) { + } else */ + if (removeMode == DragSortController.CLICK_REMOVE) { return R.layout.list_item_click_remove; } else { return R.layout.list_item_handle_left; @@ -67,7 +68,7 @@ protected int getItemLayout() { public int dragStartMode = DragSortController.ON_DOWN; public boolean removeEnabled = false; - public int removeMode = DragSortController.FLING_RIGHT_REMOVE; + public int removeMode = DragSortController.FLING_REMOVE; public boolean sortEnabled = true; public boolean dragEnabled = true; diff --git a/demo/src/com/mobeta/android/demodslv/RemoveModeDialog.java b/demo/src/com/mobeta/android/demodslv/RemoveModeDialog.java index 6c50733..07bead9 100644 --- a/demo/src/com/mobeta/android/demodslv/RemoveModeDialog.java +++ b/demo/src/com/mobeta/android/demodslv/RemoveModeDialog.java @@ -21,7 +21,7 @@ public class RemoveModeDialog extends DialogFragment { public RemoveModeDialog() { super(); - mRemoveMode = DragSortController.FLING_RIGHT_REMOVE; + mRemoveMode = DragSortController.FLING_REMOVE; } public RemoveModeDialog(int inRemoveMode) { diff --git a/demo/src/com/mobeta/android/demodslv/TestBedDSLV.java b/demo/src/com/mobeta/android/demodslv/TestBedDSLV.java index 0688f65..23e5817 100644 --- a/demo/src/com/mobeta/android/demodslv/TestBedDSLV.java +++ b/demo/src/com/mobeta/android/demodslv/TestBedDSLV.java @@ -27,9 +27,9 @@ public class TestBedDSLV extends FragmentActivity implements private int mNumHeaders = 0; private int mNumFooters = 0; - private int mDragStartMode = DragSortController.ON_DOWN; - private boolean mRemoveEnabled = false; - private int mRemoveMode = DragSortController.FLING_RIGHT_REMOVE; + private int mDragStartMode = DragSortController.ON_DRAG; + private boolean mRemoveEnabled = true; + private int mRemoveMode = DragSortController.FLING_REMOVE; private boolean mSortEnabled = true; private boolean mDragEnabled = true; diff --git a/library/pom.xml b/library/pom.xml index 3bf9723..6f10b04 100644 --- a/library/pom.xml +++ b/library/pom.xml @@ -20,12 +20,12 @@ Copyright 2012 Andreas Schildbach com.mobeta.android.dslv drag-sort-listview apklib - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT com.mobeta.android.dslv parent - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT ../pom.xml diff --git a/library/res/values/dslv_attrs.xml b/library/res/values/dslv_attrs.xml index 8b3b6f8..8c779c9 100644 --- a/library/res/values/dslv_attrs.xml +++ b/library/res/values/dslv_attrs.xml @@ -7,10 +7,7 @@ - - - - + @@ -26,6 +23,7 @@ + diff --git a/library/src/com/mobeta/android/dslv/DragSortController.java b/library/src/com/mobeta/android/dslv/DragSortController.java index 34843bb..e631065 100644 --- a/library/src/com/mobeta/android/dslv/DragSortController.java +++ b/library/src/com/mobeta/android/dslv/DragSortController.java @@ -1,11 +1,11 @@ package com.mobeta.android.dslv; +import android.graphics.Point; import android.view.GestureDetector; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; -import android.graphics.Point; import android.widget.AdapterView; /** @@ -36,10 +36,7 @@ public class DragSortController extends SimpleFloatViewManager implements View.O * Remove mode enum. */ public static final int CLICK_REMOVE = 0; - public static final int FLING_RIGHT_REMOVE = 1; - public static final int FLING_LEFT_REMOVE = 2; - public static final int SLIDE_RIGHT_REMOVE = 3; - public static final int SLIDE_LEFT_REMOVE = 4; + public static final int FLING_REMOVE = 1; /** * The current remove mode. @@ -47,6 +44,7 @@ public class DragSortController extends SimpleFloatViewManager implements View.O private int mRemoveMode; private boolean mRemoveEnabled = false; + private boolean mIsRemoving = false; private GestureDetector mDetector; @@ -57,6 +55,7 @@ public class DragSortController extends SimpleFloatViewManager implements View.O public static final int MISS = -1; private int mHitPos = MISS; + private int mFlingHitPos = MISS; private int mClickRemoveHitPos = MISS; @@ -72,14 +71,15 @@ public class DragSortController extends SimpleFloatViewManager implements View.O private float mFlingSpeed = 500f; - private float mOrigFloatAlpha = 1.0f; - private int mDragHandleId; private int mClickRemoveId; - private DragSortListView mDslv; + private int mFlingHandleId; + private boolean mCanDrag; + private DragSortListView mDslv; + private int mPositionX; /** * Calls {@link #DragSortController(DragSortListView, int)} with a @@ -90,13 +90,17 @@ public class DragSortController extends SimpleFloatViewManager implements View.O * @param dslv The DSLV instance */ public DragSortController(DragSortListView dslv) { - this(dslv, 0, ON_DOWN, FLING_RIGHT_REMOVE); + this(dslv, 0, ON_DOWN, FLING_REMOVE); } public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode) { this(dslv, dragHandleId, dragInitMode, removeMode, 0); } + public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode, int clickRemoveId) { + this(dslv, dragHandleId, dragInitMode, removeMode, clickRemoveId, 0); + } + /** * By default, sorting is enabled, and removal is disabled. * @@ -104,7 +108,8 @@ public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitM * @param dragHandleId The resource id of the View that represents * the drag handle in a list item. */ - public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode, int clickRemoveId) { + public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, + int removeMode, int clickRemoveId, int flingHandleId) { super(dslv); mDslv = dslv; mDetector = new GestureDetector(dslv.getContext(), this); @@ -113,9 +118,9 @@ public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitM mTouchSlop = ViewConfiguration.get(dslv.getContext()).getScaledTouchSlop(); mDragHandleId = dragHandleId; mClickRemoveId = clickRemoveId; + mFlingHandleId = flingHandleId; setRemoveMode(removeMode); setDragInitMode(dragInitMode); - mOrigFloatAlpha = dslv.getFloatAlpha(); } @@ -182,6 +187,16 @@ public void setDragHandleId(int id) { mDragHandleId = id; } + /** + * Set the resource id for the View that represents the fling + * handle in a list item. + * + * @param id An android resource id. + */ + public void setFlingHandleId(int id) { + mFlingHandleId = id; + } + /** * Set the resource id for the View that represents click * removal button. @@ -192,7 +207,6 @@ public void setClickRemoveId(int id) { mClickRemoveId = id; } - /** * Sets flags to restrict certain motions of the floating View * based on DragSortController settings (such as remove mode). @@ -204,22 +218,19 @@ public void setClickRemoveId(int id) { * * @return True if drag started, false otherwise. */ - public boolean startDrag(int position, int deltaX, int deltaY) { + public boolean startDrag(int position, int deltaX, int deltaY) { int dragFlags = 0; - if (mSortEnabled) { + if (mSortEnabled && !mIsRemoving) { dragFlags |= DragSortListView.DRAG_POS_Y | DragSortListView.DRAG_NEG_Y; - //dragFlags |= DRAG_POS_Y; //for fun } - if (mRemoveEnabled) { - if (mRemoveMode == FLING_RIGHT_REMOVE) { - dragFlags |= DragSortListView.DRAG_POS_X; - } else if (mRemoveMode == FLING_LEFT_REMOVE) { - dragFlags |= DragSortListView.DRAG_NEG_X; - } + if (mRemoveEnabled && mIsRemoving) { + dragFlags |= DragSortListView.DRAG_POS_X; + dragFlags |= DragSortListView.DRAG_NEG_X; } - mDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), dragFlags, deltaX, deltaY); + mDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), dragFlags, deltaX, + deltaY); return mDragging; } @@ -230,30 +241,29 @@ public boolean onTouch(View v, MotionEvent ev) { } mDetector.onTouchEvent(ev); - if (mRemoveEnabled && mDragging && (mRemoveMode == FLING_RIGHT_REMOVE || mRemoveMode == FLING_LEFT_REMOVE)) { + if (mRemoveEnabled && mDragging && mRemoveMode == FLING_REMOVE) { mFlingRemoveDetector.onTouchEvent(ev); } int action = ev.getAction() & MotionEvent.ACTION_MASK; switch (action) { - case MotionEvent.ACTION_DOWN: - mCurrX = (int) ev.getX(); - mCurrY = (int) ev.getY(); - break; - case MotionEvent.ACTION_UP: - if (mRemoveEnabled) { - final int x = (int) ev.getX(); - int thirdW = mDslv.getWidth() / 3; - int twoThirdW = mDslv.getWidth() - thirdW; - if ((mRemoveMode == SLIDE_RIGHT_REMOVE && x > twoThirdW) || - (mRemoveMode == SLIDE_LEFT_REMOVE && x < thirdW)) { - mDslv.stopDrag(true); + case MotionEvent.ACTION_DOWN: + mCurrX = (int) ev.getX(); + mCurrY = (int) ev.getY(); + break; + case MotionEvent.ACTION_UP: + if (mRemoveEnabled && mIsRemoving) { + int x = mPositionX >= 0 ? mPositionX : -mPositionX; + int removePoint = mDslv.getWidth() / 2; + if (x > removePoint) { + mDslv.stopDragWithVelocity(true, 0); + } } - } - case MotionEvent.ACTION_CANCEL: - mDragging = false; - break; + case MotionEvent.ACTION_CANCEL: + mIsRemoving = false; + mDragging = false; + break; } return false; @@ -265,37 +275,8 @@ public boolean onTouch(View v, MotionEvent ev) { @Override public void onDragFloatView(View floatView, Point position, Point touch) { - if (mRemoveEnabled) { - int x = touch.x; - int y = touch.y; - - if (mRemoveMode == SLIDE_RIGHT_REMOVE) { - int width = mDslv.getWidth(); - int thirdWidth = width / 3; - - float alpha; - if (x < thirdWidth) { - alpha = 1.0f; - } else if (x < width - thirdWidth) { - alpha = ((float) (width - thirdWidth - x)) / ((float) thirdWidth); - } else { - alpha = 0.0f; - } - mDslv.setFloatAlpha(mOrigFloatAlpha * alpha); - } else if (mRemoveMode == SLIDE_LEFT_REMOVE) { - int width = mDslv.getWidth(); - int thirdWidth = width / 3; - - float alpha; - if (x < thirdWidth) { - alpha = 0.0f; - } else if (x < width - thirdWidth) { - alpha = ((float) (x - thirdWidth)) / ((float) thirdWidth); - } else { - alpha = 1.0f; - } - mDslv.setFloatAlpha(mOrigFloatAlpha * alpha); - } + if (mRemoveEnabled && mIsRemoving) { + mPositionX = position.x; } } @@ -316,6 +297,10 @@ public int startDragPosition(MotionEvent ev) { return dragHandleHitPosition(ev); } + public int startFlingPosition(MotionEvent ev) { + return mRemoveMode == FLING_REMOVE ? flingHandleHitPosition(ev) : MISS; + } + /** * Checks for the touch of an item's drag handle (specified by * {@link #setDragHandleId(int)}), and returns that item's position @@ -330,30 +315,33 @@ public int dragHandleHitPosition(MotionEvent ev) { return viewIdHitPosition(ev, mDragHandleId); } + public int flingHandleHitPosition(MotionEvent ev) { + return viewIdHitPosition(ev, mFlingHandleId); + } + public int viewIdHitPosition(MotionEvent ev, int id) { final int x = (int) ev.getX(); final int y = (int) ev.getY(); - int touchPos = mDslv.pointToPosition(x, y); //includes headers/footers - + int touchPos = mDslv.pointToPosition(x, y); // includes headers/footers + final int numHeaders = mDslv.getHeaderViewsCount(); final int numFooters = mDslv.getFooterViewsCount(); final int count = mDslv.getCount(); - - //Log.d("mobeta", "touch down on position " + itemnum); + + // Log.d("mobeta", "touch down on position " + itemnum); // We're only interested if the touch was on an // item that's not a header or footer. - if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders && touchPos < (count - numFooters)) { + if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders + && touchPos < (count - numFooters)) { final View item = mDslv.getChildAt(touchPos - mDslv.getFirstVisiblePosition()); final int rawX = (int) ev.getRawX(); final int rawY = (int) ev.getRawY(); - - //View dragBox = (View) item.getTag(); - View dragBox = (View) item.findViewById(id); - boolean dragHit = false; + + View dragBox = id == 0 ? item : (View) item.findViewById(id); if (dragBox != null) { dragBox.getLocationOnScreen(mTempLoc); - + if (rawX > mTempLoc[0] && rawY > mTempLoc[1] && rawX < mTempLoc[0] + dragBox.getWidth() && rawY < mTempLoc[1] + dragBox.getHeight()) { @@ -380,30 +368,42 @@ public boolean onDown(MotionEvent ev) { startDrag(mHitPos, (int) ev.getX() - mItemX, (int) ev.getY() - mItemY); } + mIsRemoving = false; + mCanDrag = true; + mPositionX = 0; + mFlingHitPos = startFlingPosition(ev); + return true; } - + @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - //Log.d("mobeta", "lift listener scrolled dX="+distanceX+" dY="+distanceY); - - if (mHitPos != MISS && mDragInitMode == ON_DRAG && !mDragging) { - final int x1 = (int) e1.getX(); - final int y1 = (int) e1.getY(); - final int x2 = (int) e2.getX(); - final int y2 = (int) e2.getY(); - - boolean start = false; - if (mRemoveEnabled && mSortEnabled) { - start = true; - } else if (mRemoveEnabled) { - start = Math.abs(x2 - x1) > mTouchSlop; - } else if (mSortEnabled) { - start = Math.abs(y2 - y1) > mTouchSlop; - } - if (start) { - startDrag(mHitPos, x2 - mItemX, y2 - mItemY); + final int x1 = (int) e1.getX(); + final int y1 = (int) e1.getY(); + final int x2 = (int) e2.getX(); + final int y2 = (int) e2.getY(); + final int deltaX = x2 - mItemX; + final int deltaY = y2 - mItemY; + + if (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) { + if (mHitPos != MISS) { + if (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) { + startDrag(mHitPos, deltaX, deltaY); + } + else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) + { + mIsRemoving = true; + startDrag(mFlingHitPos, deltaX, deltaY); + } + } else if (mFlingHitPos != MISS) { + if (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) { + mIsRemoving = true; + startDrag(mFlingHitPos, deltaX, deltaY); + } else if (Math.abs(y2 - y1) > mTouchSlop) { + mCanDrag = false; // if started to scroll the list then + // don't allow sorting nor fling-removing + } } } // return whatever @@ -412,7 +412,7 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d @Override public void onLongPress(MotionEvent e) { - //Log.d("mobeta", "lift listener long pressed"); + // Log.d("mobeta", "lift listener long pressed"); if (mHitPos != MISS && mDragInitMode == ON_LONG_PRESS) { mDslv.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); startDrag(mHitPos, mCurrX - mItemX, mCurrY - mItemY); @@ -445,25 +445,25 @@ public void onShowPress(MotionEvent ev) { private GestureDetector.OnGestureListener mFlingRemoveListener = new GestureDetector.SimpleOnGestureListener() { @Override - public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - //Log.d("mobeta", "on fling remove called"); - if (mRemoveEnabled) { - switch (mRemoveMode) { - case FLING_RIGHT_REMOVE: - if (velocityX > mFlingSpeed) { - mDslv.stopDrag(true); + public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + // Log.d("mobeta", "on fling remove called"); + if (mRemoveEnabled && mIsRemoving) { + int w = mDslv.getWidth(); + int minPos = w / 5; + if (velocityX > mFlingSpeed) { + if (mPositionX > -minPos) { + mDslv.stopDragWithVelocity(true, velocityX); } - break; - case FLING_LEFT_REMOVE: - if (velocityX < -mFlingSpeed) { - mDslv.stopDrag(true); + } else if (velocityX < -mFlingSpeed) { + if (mPositionX < minPos) { + mDslv.stopDragWithVelocity(true, velocityX); } - break; } + mIsRemoving = false; } return false; } }; } - diff --git a/library/src/com/mobeta/android/dslv/DragSortListView.java b/library/src/com/mobeta/android/dslv/DragSortListView.java index 3e9bf4b..7254b9c 100644 --- a/library/src/com/mobeta/android/dslv/DragSortListView.java +++ b/library/src/com/mobeta/android/dslv/DragSortListView.java @@ -24,32 +24,30 @@ import android.content.Context; import android.content.res.TypedArray; import android.database.DataSetObserver; -import android.graphics.Bitmap; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Point; +import android.graphics.drawable.Drawable; import android.os.Environment; import android.os.SystemClock; -import android.text.method.MovementMethod; import android.util.AttributeSet; import android.util.Log; import android.util.SparseBooleanArray; import android.util.SparseIntArray; -import android.view.*; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.widget.*; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.HeaderViewListAdapter; +import android.widget.ListAdapter; +import android.widget.ListView; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.text.ChoiceFormat; import java.util.ArrayList; -import java.util.HashMap; - /** * ListView subclass that mediates drag and drop resorting of items. @@ -104,7 +102,7 @@ public class DragSortListView extends ListView { * View. If dropped, the dragged item will land in this position. */ private int mFloatPos; - + /** * The first expanded ListView position that helps represent * the drop slot tracking the floating View. @@ -175,7 +173,7 @@ public class DragSortListView extends ListView { /** * Enable/Disable item dragging - * + * * @attr name dslv:drag_enabled */ private boolean mDragEnabled = true; @@ -188,9 +186,9 @@ public class DragSortListView extends ListView { private final static int DROPPING = 2; private final static int STOPPED = 3; private final static int DRAGGING = 4; - + private int mDragState = IDLE; - + /** * Height in pixels to which the originally dragged item * is collapsed during a drag-sort. Currently, this value @@ -257,13 +255,12 @@ public class DragSortListView extends ListView { */ private float mDragDownScrollHeight; - /** * Maximum drag-scroll speed in pixels per ms. Only used with * default linear drag-scroll profile. */ private float mMaxScrollSpeed = 0.5f; - + /** * Defines the scroll speed during a drag-scroll. User can * provide their own; this default is a simple linear profile @@ -286,7 +283,7 @@ public float getSpeed(float w, long t) { * Current touch y. */ private int mY; - + /** * Last touch x. */ @@ -387,7 +384,7 @@ public float getSpeed(float w, long t) { * is directly below). */ private float mSlideFrac = 0.0f; - + /** * Wraps the user-provided ListAdapter. This is used to wrap each * item View given by the user inside another View (currenly @@ -438,12 +435,15 @@ public float getSpeed(float w, long t) { private DropAnimator mDropAnimator; + private boolean mUseRemoveVelocity; + private float mRemoveVelocityX = 0; + public DragSortListView(Context context, AttributeSet attrs) { super(context, attrs); int defaultDuration = 150; - int removeAnimDuration = defaultDuration; //ms - int dropAnimDuration = defaultDuration; //ms + int removeAnimDuration = defaultDuration; // ms + int dropAnimDuration = defaultDuration; // ms if (attrs != null) { TypedArray a = getContext().obtainStyledAttributes(attrs, @@ -467,8 +467,8 @@ public DragSortListView(Context context, AttributeSet attrs) { mSlideRegionFrac = Math.max(0.0f, Math.min(1.0f, 1.0f - a.getFloat( - R.styleable.DragSortListView_slide_shuffle_speed, - 0.75f))); + R.styleable.DragSortListView_slide_shuffle_speed, + 0.75f))); mAnimate = mSlideRegionFrac > 0.0f; @@ -500,7 +500,7 @@ public DragSortListView(Context context, AttributeSet attrs) { false); int removeMode = a.getInt( R.styleable.DragSortListView_remove_mode, - DragSortController.FLING_RIGHT_REMOVE); + DragSortController.FLING_REMOVE); boolean sortEnabled = a.getBoolean( R.styleable.DragSortListView_sort_enabled, true); @@ -510,16 +510,19 @@ public DragSortListView(Context context, AttributeSet attrs) { int dragHandleId = a.getResourceId( R.styleable.DragSortListView_drag_handle_id, 0); + int flingHandleId = a.getResourceId( + R.styleable.DragSortListView_fling_handle_id, + 0); int clickRemoveId = a.getResourceId( R.styleable.DragSortListView_click_remove_id, 0); int bgColor = a.getColor( R.styleable.DragSortListView_float_background_color, Color.BLACK); - + DragSortController controller = new DragSortController( this, dragHandleId, dragInitMode, removeMode, - clickRemoveId); + clickRemoveId, flingHandleId); controller.setRemoveEnabled(removeEnabled); controller.setSortEnabled(sortEnabled); controller.setBackgroundColor(bgColor); @@ -537,31 +540,32 @@ public DragSortListView(Context context, AttributeSet attrs) { if (removeAnimDuration > 0) { mRemoveAnimator = new RemoveAnimator(smoothness, removeAnimDuration); } - //mLiftAnimator = new LiftAnimator(smoothness, 100); + // mLiftAnimator = new LiftAnimator(smoothness, 100); if (dropAnimDuration > 0) { mDropAnimator = new DropAnimator(smoothness, dropAnimDuration); } - mCancelEvent = MotionEvent.obtain(0,0,MotionEvent.ACTION_CANCEL,0f,0f,0f,0f,0,0f,0f,0,0); + mCancelEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0f, 0f, 0, 0f, + 0f, 0, 0); // construct the dataset observer mObserver = new DataSetObserver() { - private void cancel() { - if (mDragState == DRAGGING) { - cancelDrag(); - } - } + private void cancel() { + if (mDragState == DRAGGING) { + cancelDrag(); + } + } - @Override - public void onChanged() { - cancel(); - } + @Override + public void onChanged() { + cancel(); + } - @Override - public void onInvalidated() { - cancel(); - } - }; + @Override + public void onInvalidated() { + cancel(); + } + }; } /** @@ -576,7 +580,7 @@ public void setFloatAlpha(float alpha) { public float getFloatAlpha() { return mCurrFloatAlpha; } - + /** * Set maximum drag scroll speed in positions/second. Only applies * if using default ScrollSpeedProfile. @@ -586,7 +590,7 @@ public float getFloatAlpha() { public void setMaxScrollSpeed(float max) { mMaxScrollSpeed = max; } - + /** * For each DragSortListView Listener interface implemented by * adapter, this method calls the appropriate @@ -617,7 +621,7 @@ public void setAdapter(ListAdapter adapter) { super.setAdapter(mAdapterWrapper); } - + /** * As opposed to {@link ListView#getAdapter()}, which returns * a heavily wrapped ListAdapter (DragSortListView wraps the @@ -633,15 +637,14 @@ public ListAdapter getInputAdapter() { } } - private class AdapterWrapper extends HeaderViewListAdapter { private ListAdapter mAdapter; - + public AdapterWrapper(ListAdapter adapter) { super(null, null, adapter); mAdapter = adapter; } - + public ListAdapter getAdapter() { return mAdapter; } @@ -651,14 +654,16 @@ public View getView(int position, View convertView, ViewGroup parent) { DragSortItemView v; View child; - //Log.d("mobeta", "getView: position="+position+" convertView="+convertView); + // Log.d("mobeta", + // "getView: position="+position+" convertView="+convertView); if (convertView != null) { v = (DragSortItemView) convertView; View oldChild = v.getChildAt(0); child = mAdapter.getView(position, oldChild, v); if (child != oldChild) { - // shouldn't get here if user is reusing convertViews properly + // shouldn't get here if user is reusing convertViews + // properly v.removeViewAt(0); v.addView(child); } @@ -680,13 +685,14 @@ public View getView(int position, View convertView, ViewGroup parent) { } private void drawDivider(int expPosition, Canvas canvas) { - + final Drawable divider = getDivider(); final int dividerHeight = getDividerHeight(); - //Log.d("mobeta", "div="+divider+" divH="+dividerHeight); - + // Log.d("mobeta", "div="+divider+" divH="+dividerHeight); + if (divider != null && dividerHeight != 0) { - final ViewGroup expItem = (ViewGroup) getChildAt(expPosition - getFirstVisiblePosition()); + final ViewGroup expItem = (ViewGroup) getChildAt(expPosition + - getFirstVisiblePosition()); if (expItem != null) { final int l = getPaddingLeft(); final int r = getWidth() - getPaddingRight(); @@ -702,11 +708,11 @@ private void drawDivider(int expPosition, Canvas canvas) { b = expItem.getBottom() - childHeight; t = b - dividerHeight; } - //Log.d("mobeta", "l="+l+" t="+t+" r="+r+" b="+b); + // Log.d("mobeta", "l="+l+" t="+t+" r="+r+" b="+b); // Have to clip to support ColorDrawable on <= Gingerbread canvas.save(); - canvas.clipRect(l, t, r, b); + canvas.clipRect(l, t, r, b); divider.setBounds(l, t, r, b); divider.draw(canvas); canvas.restore(); @@ -732,14 +738,28 @@ protected void dispatchDraw(Canvas canvas) { // draw the float view over everything final int w = mFloatView.getWidth(); final int h = mFloatView.getHeight(); - final int alpha = (int) (255f * mCurrFloatAlpha); + + int x = mFloatLoc.x; + + int width = getWidth(); + if (x < 0) + x = -x; + float alphaMod; + if (x < width) { + alphaMod = ((float) (width - x)) / ((float) width); + alphaMod *= alphaMod; + } else { + alphaMod = 0; + } + + final int alpha = (int) (255f * mCurrFloatAlpha * alphaMod); canvas.save(); - //Log.d("mobeta", "clip rect bounds: " + canvas.getClipBounds()); + // Log.d("mobeta", "clip rect bounds: " + canvas.getClipBounds()); canvas.translate(mFloatLoc.x, mFloatLoc.y); canvas.clipRect(0, 0, w, h); - //Log.d("mobeta", "clip rect bounds: " + canvas.getClipBounds()); + // Log.d("mobeta", "clip rect bounds: " + canvas.getClipBounds()); canvas.saveLayerAlpha(0, 0, w, h, alpha, Canvas.ALL_SAVE_FLAG); mFloatView.draw(canvas); canvas.restore(); @@ -761,7 +781,8 @@ private int getItemHeight(int position) { } private void printPosData() { - Log.d("mobeta", "mSrcPos="+mSrcPos+" mFirstExpPos="+mFirstExpPos+" mSecondExpPos="+mSecondExpPos); + Log.d("mobeta", "mSrcPos=" + mSrcPos + " mFirstExpPos=" + mFirstExpPos + " mSecondExpPos=" + + mSecondExpPos); } private class HeightCache { @@ -905,13 +926,13 @@ private boolean updatePositions() { int divHeight = getDividerHeight(); - //Log.d("mobeta", "float mid="+mFloatViewMid); + // Log.d("mobeta", "float mid="+mFloatViewMid); int itemPos = startPos; int itemTop = startTop; if (mFloatViewMid < edge) { // scanning up for float position - //Log.d("mobeta", " edge="+edge); + // Log.d("mobeta", " edge="+edge); while (itemPos >= 0) { itemPos--; itemHeight = getItemHeight(itemPos); @@ -923,8 +944,8 @@ private boolean updatePositions() { itemTop -= itemHeight + divHeight; edge = getShuffleEdge(itemPos, itemTop); - //Log.d("mobeta", " edge="+edge); - + // Log.d("mobeta", " edge="+edge); + if (mFloatViewMid >= edge) { break; } @@ -933,7 +954,7 @@ private boolean updatePositions() { } } else { // scanning down for float position - //Log.d("mobeta", " edge="+edge); + // Log.d("mobeta", " edge="+edge); final int count = getCount(); while (itemPos < count) { if (itemPos == count - 1) { @@ -944,7 +965,7 @@ private boolean updatePositions() { itemTop += divHeight + itemHeight; itemHeight = getItemHeight(itemPos + 1); edge = getShuffleEdge(itemPos + 1, itemTop); - //Log.d("mobeta", " edge="+edge); + // Log.d("mobeta", " edge="+edge); // test for hit if (mFloatViewMid < edge) { @@ -964,7 +985,7 @@ private boolean updatePositions() { int oldFirstExpPos = mFirstExpPos; int oldSecondExpPos = mSecondExpPos; float oldSlideFrac = mSlideFrac; - + if (mAnimate) { int edgeToEdge = Math.abs(edge - lastEdge); @@ -976,7 +997,7 @@ private boolean updatePositions() { edgeTop = edge; edgeBottom = lastEdge; } - //Log.d("mobeta", "edgeTop="+edgeTop+" edgeBot="+edgeBottom); + // Log.d("mobeta", "edgeTop="+edgeTop+" edgeBot="+edgeBottom); int slideRgnHeight = (int) (0.5f * mSlideRegionFrac * edgeToEdge); float slideRgnHeightF = (float) slideRgnHeight; @@ -988,15 +1009,18 @@ private boolean updatePositions() { mFirstExpPos = itemPos - 1; mSecondExpPos = itemPos; mSlideFrac = 0.5f * ((float) (slideEdgeTop - mFloatViewMid)) / slideRgnHeightF; - //Log.d("mobeta", "firstExp="+mFirstExpPos+" secExp="+mSecondExpPos+" slideFrac="+mSlideFrac); + // Log.d("mobeta", + // "firstExp="+mFirstExpPos+" secExp="+mSecondExpPos+" slideFrac="+mSlideFrac); } else if (mFloatViewMid < slideEdgeBottom) { mFirstExpPos = itemPos; mSecondExpPos = itemPos; } else { mFirstExpPos = itemPos; mSecondExpPos = itemPos + 1; - mSlideFrac = 0.5f * (1.0f + ((float) (edgeBottom - mFloatViewMid)) / slideRgnHeightF); - //Log.d("mobeta", "firstExp="+mFirstExpPos+" secExp="+mSecondExpPos+" slideFrac="+mSlideFrac); + mSlideFrac = 0.5f * (1.0f + ((float) (edgeBottom - mFloatViewMid)) + / slideRgnHeightF); + // Log.d("mobeta", + // "firstExp="+mFirstExpPos+" secExp="+mSecondExpPos+" slideFrac="+mSlideFrac); } } else { @@ -1015,7 +1039,8 @@ private boolean updatePositions() { mSecondExpPos = itemPos; } - if (mFirstExpPos != oldFirstExpPos || mSecondExpPos != oldSecondExpPos || mSlideFrac != oldSlideFrac) { + if (mFirstExpPos != oldFirstExpPos || mSecondExpPos != oldSecondExpPos + || mSlideFrac != oldSlideFrac) { updated = true; } @@ -1030,7 +1055,7 @@ private boolean updatePositions() { return updated; } - + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -1041,7 +1066,7 @@ protected void onDraw(Canvas canvas) { } private class SmoothAnimator implements Runnable { - private long mStartTime; + protected long mStartTime; private float mDurationF; @@ -1049,7 +1074,7 @@ private class SmoothAnimator implements Runnable { private float mA, mB, mC, mD; private boolean mCanceled; - + public SmoothAnimator(float smoothness, int duration) { mAlpha = smoothness; mDurationF = (float) duration; @@ -1080,15 +1105,15 @@ public void cancel() { } public void onStart() { - //stub + // stub } public void onUpdate(float frac, float smoothFrac) { - //stub + // stub } public void onStop() { - //stub + // stub } @Override @@ -1120,7 +1145,7 @@ private class LiftAnimator extends SmoothAnimator { public LiftAnimator(float smoothness, int duration) { super(smoothness, duration); } - + @Override public void onStart() { mInitDragDeltaY = mDragDeltaY; @@ -1132,7 +1157,8 @@ public void onUpdate(float frac, float smoothFrac) { if (mDragState != DRAGGING) { cancel(); } else { - mDragDeltaY = (int) (smoothFrac * mFinalDragDeltaY + (1f - smoothFrac) * mInitDragDeltaY); + mDragDeltaY = (int) (smoothFrac * mFinalDragDeltaY + (1f - smoothFrac) + * mInitDragDeltaY); mFloatLoc.y = mY - mDragDeltaY; doDragFloatView(true); } @@ -1152,7 +1178,7 @@ private class DropAnimator extends SmoothAnimator { public DropAnimator(float smoothness, int duration) { super(smoothness, duration); } - + @Override public void onStart() { mDropPos = mFloatPos; @@ -1188,9 +1214,11 @@ private int getTargetY() { @Override public void onUpdate(float frac, float smoothFrac) { final int targetY = getTargetY(); + final int targetX = getPaddingLeft(); final float deltaY = mFloatLoc.y - targetY; + final float deltaX = mFloatLoc.x - targetX; final float f = 1f - smoothFrac; - if (f < Math.abs(deltaY / mInitDeltaY)) { + if (f < Math.abs(deltaY / mInitDeltaY) || f < Math.abs(deltaX / mInitDeltaX)) { mFloatLoc.y = targetY + (int) (mInitDeltaY * f); mFloatLoc.x = getPaddingLeft() + (int) (mInitDeltaX * f); doDragFloatView(true); @@ -1209,6 +1237,7 @@ public void onStop() { */ private class RemoveAnimator extends SmoothAnimator { + private float mFloatLocX; private float mFirstStartBlank; private float mSecondStartBlank; @@ -1222,7 +1251,7 @@ private class RemoveAnimator extends SmoothAnimator { public RemoveAnimator(float smoothness, int duration) { super(smoothness, duration); } - + @Override public void onStart() { mFirstChildHeight = -1; @@ -1231,7 +1260,22 @@ public void onStart() { mSecondPos = mSecondExpPos; srcPos = mSrcPos; mDragState = REMOVING; - destroyFloatView(); + + mFloatLocX = mFloatLoc.x; + if (mUseRemoveVelocity) { + float minVelocity = 2f * getWidth(); + if (mRemoveVelocityX == 0) { + mRemoveVelocityX = (mFloatLocX < 0 ? -1 : 1) * minVelocity; + } else { + minVelocity *= 2; + if (mRemoveVelocityX < 0 && mRemoveVelocityX > -minVelocity) + mRemoveVelocityX = -minVelocity; + else if (mRemoveVelocityX > 0 && mRemoveVelocityX < minVelocity) + mRemoveVelocityX = minVelocity; + } + } else { + destroyFloatView(); + } } @Override @@ -1242,6 +1286,23 @@ public void onUpdate(float frac, float smoothFrac) { View item = getChildAt(mFirstPos - firstVis); ViewGroup.LayoutParams lp; int blank; + + if (mUseRemoveVelocity) { + float dt = (float) (SystemClock.uptimeMillis() - mStartTime) / 1000; + if (dt == 0) + return; + float dx = mRemoveVelocityX * dt; + int w = getWidth(); + mRemoveVelocityX += (mRemoveVelocityX > 0 ? 1 : -1) * dt * w; + mFloatLocX += dx; + mFloatLoc.x = (int) mFloatLocX; + if (mFloatLocX < w && mFloatLocX > -w) { + mStartTime = SystemClock.uptimeMillis(); + doDragFloatView(true); + return; + } + } + if (item != null) { if (mFirstChildHeight == -1) { mFirstChildHeight = getChildHeight(mFirstPos, item, false); @@ -1273,14 +1334,22 @@ public void onStop() { } } + public void removeItem(int which) { + + mUseRemoveVelocity = false; + removeItem(which, 0); + } + /** * Removes an item from the list and animates the removal. * * @param which Position to remove (NOTE: headers/footers ignored! * this is a position in your input ListAdapter). + * @param velocityX */ - public void removeItem(int which) { + public void removeItem(int which, float velocityX) { if (mDragState == IDLE || mDragState == DRAGGING) { + if (mDragState == IDLE) { // called from outside drag-sort mSrcPos = getHeaderViewsCount() + which; @@ -1293,14 +1362,17 @@ public void removeItem(int which) { } } + mDragState = REMOVING; + mRemoveVelocityX = velocityX; + if (mInTouchEvent) { switch (mCancelMethod) { - case ON_TOUCH_EVENT: - super.onTouchEvent(mCancelEvent); - break; - case ON_INTERCEPT_TOUCH_EVENT: - super.onInterceptTouchEvent(mCancelEvent); - break; + case ON_TOUCH_EVENT: + super.onTouchEvent(mCancelEvent); + break; + case ON_INTERCEPT_TOUCH_EVENT: + super.onInterceptTouchEvent(mCancelEvent); + break; } } @@ -1312,7 +1384,6 @@ public void removeItem(int which) { } } - /** * Move an item, bypassing the drag-sort process. Simply calls * through to {@link DropListener#drop(int, int)}. @@ -1392,7 +1463,7 @@ private void doRemoveItem(int which) { // must set to avoid cancelDrag being called from the // DataSetObserver mDragState = REMOVING; - + // end it if (mRemoveListener != null) { mRemoveListener.remove(which); @@ -1413,7 +1484,7 @@ private void doRemoveItem(int which) { private void adjustOnReorder() { final int firstPos = getFirstVisiblePosition(); - //Log.d("mobeta", "first="+firstPos+" src="+mSrcPos); + // Log.d("mobeta", "first="+firstPos+" src="+mSrcPos); if (mSrcPos < firstPos) { // collapsed src item is off screen; // adjust the scroll after item heights have been fixed @@ -1422,7 +1493,7 @@ private void adjustOnReorder() { if (v != null) { top = v.getTop(); } - //Log.d("mobeta", "top="+top+" fvh="+mFloatViewHeight); + // Log.d("mobeta", "top="+top+" fvh="+mFloatViewHeight); setSelectionFromTop(firstPos - 1, top - getPaddingTop()); } } @@ -1439,11 +1510,22 @@ private void adjustOnReorder() { * no floating View. */ public boolean stopDrag(boolean remove) { + mUseRemoveVelocity = false; + return stopDrag(remove, 0); + } + + public boolean stopDragWithVelocity(boolean remove, float velocityX) { + + mUseRemoveVelocity = true; + return stopDrag(remove, velocityX); + } + + public boolean stopDrag(boolean remove, float velocityX) { if (mFloatView != null) { mDragScroller.stopScrolling(true); - + if (remove) { - removeItem(mSrcPos - getHeaderViewsCount()); + removeItem(mSrcPos - getHeaderViewsCount(), velocityX); } else { if (mDropAnimator != null) { mDropAnimator.start(); @@ -1483,15 +1565,15 @@ public boolean onTouchEvent(MotionEvent ev) { saveTouchCoords(ev); } - //if (mFloatView != null) { + // if (mFloatView != null) { if (mDragState == DRAGGING) { onDragTouchEvent(ev); - more = true; //give us more! + more = true; // give us more! } else { // what if float view is null b/c we dropped in middle // of drag touch event? - //if (mDragState != STOPPED) { + // if (mDragState != STOPPED) { if (mDragState == IDLE) { if (super.onTouchEvent(ev)) { more = true; @@ -1501,14 +1583,14 @@ public boolean onTouchEvent(MotionEvent ev) { int action = ev.getAction() & MotionEvent.ACTION_MASK; switch (action) { - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - doActionUpOrCancel(); - break; - default: - if (more) { - mCancelMethod = ON_TOUCH_EVENT; - } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + doActionUpOrCancel(); + break; + default: + if (more) { + mCancelMethod = ON_TOUCH_EVENT; + } } } @@ -1541,7 +1623,6 @@ private void saveTouchCoords(MotionEvent ev) { mOffsetY = (int) ev.getRawY() - mY; } - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (!mDragEnabled) { @@ -1563,7 +1644,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { } boolean intercept = false; - + // the following deals with calls to super.onInterceptTouchEvent if (mFloatView != null) { // super's touch event canceled in startDrag @@ -1574,16 +1655,16 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { } switch (action) { - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - doActionUpOrCancel(); - break; - default: - if (intercept) { - mCancelMethod = ON_TOUCH_EVENT; - } else { - mCancelMethod = ON_INTERCEPT_TOUCH_EVENT; - } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + doActionUpOrCancel(); + break; + default: + if (intercept) { + mCancelMethod = ON_TOUCH_EVENT; + } else { + mCancelMethod = ON_INTERCEPT_TOUCH_EVENT; + } } } @@ -1593,8 +1674,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { return intercept; } - - + /** * Set the width of each drag scroll region by specifying * a fraction of the ListView height. @@ -1606,8 +1686,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { public void setDragScrollStart(float heightFraction) { setDragScrollStarts(heightFraction, heightFraction); } - - + /** * Set the width of each drag scroll region by specifying * a fraction of the ListView height. @@ -1651,7 +1730,8 @@ private void continueDrag(int x, int y) { int currentScrollDir = mDragScroller.getScrollDir(); if (minY > mLastY && minY > mDownScrollStartY && currentScrollDir != DragScroller.DOWN) { - // dragged down, it is below the down scroll start and it is not scrolling up + // dragged down, it is below the down scroll start and it is not + // scrolling up if (currentScrollDir != DragScroller.STOP) { // moved directly from up scroll to down scroll @@ -1661,38 +1741,41 @@ private void continueDrag(int x, int y) { // start scrolling down mDragScroller.startScrolling(DragScroller.DOWN); } else if (maxY < mLastY && maxY < mUpScrollStartY && currentScrollDir != DragScroller.UP) { - // dragged up, it is above the up scroll start and it is not scrolling up + // dragged up, it is above the up scroll start and it is not + // scrolling up if (currentScrollDir != DragScroller.STOP) { // moved directly from down scroll to up scroll mDragScroller.stopScrolling(true); } - + // start scrolling up mDragScroller.startScrolling(DragScroller.UP); } - else if (maxY >= mUpScrollStartY && minY <= mDownScrollStartY && mDragScroller.isScrolling()) { - // not in the upper nor in the lower drag-scroll regions but it is still scrolling + else if (maxY >= mUpScrollStartY && minY <= mDownScrollStartY + && mDragScroller.isScrolling()) { + // not in the upper nor in the lower drag-scroll regions but it is + // still scrolling mDragScroller.stopScrolling(true); } } - + private void updateScrollStarts() { final int padTop = getPaddingTop(); final int listHeight = getHeight() - padTop - getPaddingBottom(); float heightF = (float) listHeight; - + mUpScrollStartYF = padTop + mDragUpScrollStartFrac * heightF; mDownScrollStartYF = padTop + (1.0f - mDragDownScrollStartFrac) * heightF; mUpScrollStartY = (int) mUpScrollStartYF; mDownScrollStartY = (int) mDownScrollStartYF; - + mDragUpScrollHeight = mUpScrollStartYF - padTop; mDragDownScrollHeight = padTop + listHeight - mDownScrollStartYF; } - + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); @@ -1751,7 +1834,6 @@ private void adjustItem(int position, View v, boolean invalidChildHeight) { } } - // Finally adjust item visibility int oldVis = v.getVisibility(); @@ -1782,13 +1864,13 @@ private int getChildHeight(int position) { // first check cache for child height at this position int childHeight = mChildHeightCache.get(position); if (childHeight != -1) { - //Log.d("mobeta", "found child height in cache!"); + // Log.d("mobeta", "found child height in cache!"); return childHeight; } final ListAdapter adapter = getAdapter(); int type = adapter.getItemViewType(position); - + // There might be a better place for checking for the following final int typeCount = adapter.getViewTypeCount(); if (typeCount != mSampleViewTypes.length) { @@ -1946,7 +2028,8 @@ private void measureItem(View item) { lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); item.setLayoutParams(lp); } - int wspec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, getListPaddingLeft() + getListPaddingRight(), lp.width); + int wspec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, getListPaddingLeft() + + getListPaddingRight(), lp.width); int hspec; if (lp.height > 0) { hspec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); @@ -1967,16 +2050,16 @@ private void measureFloatView() { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - //Log.d("mobeta", "onMeasure called"); + // Log.d("mobeta", "onMeasure called"); if (mFloatView != null) { if (mFloatView.isLayoutRequested()) { measureFloatView(); } - mFloatViewOnMeasured = true; //set to false after layout + mFloatViewOnMeasured = true; // set to false after layout } mWidthMeasureSpec = widthMeasureSpec; } - + @Override protected void layoutChildren() { super.layoutChildren(); @@ -1993,33 +2076,32 @@ protected void layoutChildren() { } } - protected boolean onDragTouchEvent(MotionEvent ev) { // we are in a drag int action = ev.getAction() & MotionEvent.ACTION_MASK; switch (ev.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_CANCEL: - if (mDragState == DRAGGING) { - cancelDrag(); - } - doActionUpOrCancel(); - break; - case MotionEvent.ACTION_UP: - //Log.d("mobeta", "calling stopDrag from onDragTouchEvent"); - if (mDragState == DRAGGING) { - stopDrag(false); - } - doActionUpOrCancel(); - break; - case MotionEvent.ACTION_MOVE: - continueDrag((int) ev.getX(), (int) ev.getY()); - break; + case MotionEvent.ACTION_CANCEL: + if (mDragState == DRAGGING) { + cancelDrag(); + } + doActionUpOrCancel(); + break; + case MotionEvent.ACTION_UP: + // Log.d("mobeta", "calling stopDrag from onDragTouchEvent"); + if (mDragState == DRAGGING) { + stopDrag(false); + } + doActionUpOrCancel(); + break; + case MotionEvent.ACTION_MOVE: + continueDrag((int) ev.getX(), (int) ev.getY()); + break; } return true; } - + private boolean mFloatViewInvalidated = false; private void invalidateFloatView() { @@ -2087,7 +2169,8 @@ public boolean startDrag(int position, int dragFlags, int deltaX, int deltaY) { * a drag in progress. */ public boolean startDrag(int position, View floatView, int dragFlags, int deltaX, int deltaY) { - if (mDragState != IDLE || !mInTouchEvent || mFloatView != null || floatView == null || !mDragEnabled) { + if (mDragState != IDLE || !mInTouchEvent || mFloatView != null || floatView == null + || !mDragEnabled) { return false; } @@ -2101,19 +2184,19 @@ public boolean startDrag(int position, View floatView, int dragFlags, int deltaX mSrcPos = pos; mFloatPos = pos; - //mDragState = dragType; + // mDragState = dragType; mDragState = DRAGGING; mDragFlags = 0; mDragFlags |= dragFlags; mFloatView = floatView; - measureFloatView(); //sets mFloatViewHeight + measureFloatView(); // sets mFloatViewHeight mDragDeltaX = deltaX; mDragDeltaY = deltaY; mDragStartY = mY; - //updateFloatView(mX - mDragDeltaX, mY - mDragDeltaY); + // updateFloatView(mX - mDragDeltaX, mY - mDragDeltaY); mFloatLoc.x = mX - mDragDeltaX; mFloatLoc.y = mY - mDragDeltaY; @@ -2131,12 +2214,12 @@ public boolean startDrag(int position, View floatView, int dragFlags, int deltaX // once float view is created, events are no longer passed // to ListView switch (mCancelMethod) { - case ON_TOUCH_EVENT: - super.onTouchEvent(mCancelEvent); - break; - case ON_INTERCEPT_TOUCH_EVENT: - super.onInterceptTouchEvent(mCancelEvent); - break; + case ON_TOUCH_EVENT: + super.onTouchEvent(mCancelEvent); + break; + case ON_INTERCEPT_TOUCH_EVENT: + super.onInterceptTouchEvent(mCancelEvent); + break; } requestLayout(); @@ -2172,7 +2255,7 @@ private void doDragFloatView(int movePos, View moveItem, boolean forceInvalidate if (updated) { adjustAllItems(); int scroll = adjustScroll(movePos, moveItem, oldFirstExpPos, oldSecondExpPos); - //Log.d("mobeta", " adjust scroll="+scroll); + // Log.d("mobeta", " adjust scroll="+scroll); setSelectionFromTop(movePos, moveItem.getTop() + scroll - getPaddingTop()); layoutChildren(); @@ -2205,7 +2288,7 @@ private void updateFloatView() { mFloatLoc.x = padLeft; } else if ((mDragFlags & DRAG_NEG_X) == 0 && floatX < padLeft) { mFloatLoc.x = padLeft; - } + } // keep floating view from going past bottom of last header view final int numHeaders = getHeaderViewsCount(); @@ -2213,7 +2296,8 @@ private void updateFloatView() { final int firstPos = getFirstVisiblePosition(); final int lastPos = getLastVisiblePosition(); - //Log.d("mobeta", "nHead="+numHeaders+" nFoot="+numFooters+" first="+firstPos+" last="+lastPos); + // Log.d("mobeta", + // "nHead="+numHeaders+" nFoot="+numFooters+" first="+firstPos+" last="+lastPos); int topLimit = getPaddingTop(); if (firstPos < numHeaders) { topLimit = getChildAt(numHeaders - firstPos - 1).getBottom(); @@ -2234,11 +2318,11 @@ private void updateFloatView() { bottomLimit = Math.min(getChildAt(mSrcPos - firstPos).getBottom(), bottomLimit); } } - - //Log.d("mobeta", "dragView top=" + (y - mDragDeltaY)); - //Log.d("mobeta", "limit=" + limit); - //Log.d("mobeta", "mDragDeltaY=" + mDragDeltaY); - + + // Log.d("mobeta", "dragView top=" + (y - mDragDeltaY)); + // Log.d("mobeta", "limit=" + limit); + // Log.d("mobeta", "mDragDeltaY=" + mDragDeltaY); + if (floatY < topLimit) { mFloatLoc.y = topLimit; } else if (floatY + mFloatViewHeight > bottomLimit) { @@ -2248,7 +2332,7 @@ private void updateFloatView() { // get y-midpoint of floating view (constrained to ListView bounds) mFloatViewMid = mFloatLoc.y + mFloatViewHeightHalf; } - + private void destroyFloatView() { if (mFloatView != null) { mFloatView.setVisibility(GONE); @@ -2372,7 +2456,7 @@ public void setRemoveListener(RemoveListener l) { public interface DragListener { public void drag(int from, int to); } - + /** * Your implementation of this has to reorder your ListAdapter! * Make sure to call @@ -2385,7 +2469,7 @@ public interface DragListener { public interface DropListener { public void drop(int from, int to); } - + /** * Make sure to call * {@link BaseAdapter#notifyDataSetChanged()} or something like it @@ -2398,14 +2482,15 @@ public interface RemoveListener { public void remove(int which); } - public interface DragSortListener extends DropListener, DragListener, RemoveListener {} + public interface DragSortListener extends DropListener, DragListener, RemoveListener { + } public void setDragSortListener(DragSortListener l) { setDropListener(l); setDragListener(l); setRemoveListener(l); } - + /** * Completely custom scroll speed profile. Default increases linearly * with position and is constant in time. Create your own by implementing @@ -2418,7 +2503,7 @@ public void setDragScrollProfile(DragScrollProfile ssp) { mScrollProfile = ssp; } } - + /** * Use this to move the check state of an item from one position to another * in a drop operation. If you have a choiceMode which is not none, this @@ -2457,7 +2542,7 @@ public void moveCheckState(int from, int to) { // start and end of the "runs" of checked items, and then moving the // runs. Note that moving an item from A to B is essentially a rotation // of the range of items in [A, B]. Let's say we have - // . . U V X Y Z . . + // . . U V X Y Z . . // and move U after Z. This is equivalent to a rotation one step to the // left within the range you are moving across: // . . V X Y Z U . . @@ -2475,12 +2560,12 @@ public void moveCheckState(int from, int to) { SparseBooleanArray cip = getCheckedItemPositions(); int rangeStart = from; int rangeEnd = to; - if (to position))) { // Only set a new check mark in front of this run if it does // not contain the deleted position. If it does, we only need // to make it one check mark shorter at the end. - setItemChecked(rotate(runStart[i], - 1, rangeStart, rangeEnd), true); + setItemChecked(rotate(runStart[i], -1, rangeStart, rangeEnd), true); } - setItemChecked(rotate(runEnd[i], - 1, rangeStart, rangeEnd), false); + setItemChecked(rotate(runEnd[i], -1, rangeStart, rangeEnd), false); } } private static int buildRunList(SparseBooleanArray cip, int rangeStart, int rangeEnd, int[] runStart, int[] runEnd) { int runCount = 0; - + int i = findFirstSetIndex(cip, rangeStart, rangeEnd); - if(i == -1) + if (i == -1) return 0; - + int position = cip.keyAt(i); int currentRunStart = position; int currentRunEnd = currentRunStart + 1; - for(i++; i < cip.size() && (position = cip.keyAt(i)) < rangeEnd; i++) { - if (!cip.valueAt(i)) // not checked => not interesting + for (i++; i < cip.size() && (position = cip.keyAt(i)) < rangeEnd; i++) { + if (!cip.valueAt(i)) // not checked => not interesting continue; if (position == currentRunEnd) { currentRunEnd++; @@ -2562,7 +2647,7 @@ private static int buildRunList(SparseBooleanArray cip, int rangeStart, currentRunEnd = position + 1; } } - + if (currentRunEnd == rangeEnd) { // rangeStart and rangeEnd are equivalent positions so to be // consistent we translate them to the same integer value. That way @@ -2573,27 +2658,27 @@ private static int buildRunList(SparseBooleanArray cip, int rangeStart, runStart[runCount] = currentRunStart; runEnd[runCount] = currentRunEnd; runCount++; - + if (runCount > 1) { - if (runStart[0] == rangeStart && runEnd[runCount-1] == rangeStart) { + if (runStart[0] == rangeStart && runEnd[runCount - 1] == rangeStart) { // The last run ends at the end of the range, and the first run // starts at the beginning of the range. So they are actually // part of the same run, except they wrap around the end of the // range. To avoid adjacent runs, we need to merge them. - runStart[0] = runStart[runCount-1]; + runStart[0] = runStart[runCount - 1]; runCount--; } } return runCount; } - + private static int rotate(int value, int offset, int lowerBound, int upperBound) { int windowSize = upperBound - lowerBound; - + value += offset; - if(value < lowerBound) { + if (value < lowerBound) { value += windowSize; - } else if(value >= upperBound) { + } else if (value >= upperBound) { value -= windowSize; } return value; @@ -2609,7 +2694,6 @@ private static int findFirstSetIndex(SparseBooleanArray sba, int rangeStart, int return i; } - private static int insertionIndexForKey(SparseBooleanArray sba, int key) { int low = 0; int high = sba.size(); @@ -2623,7 +2707,6 @@ private static int insertionIndexForKey(SparseBooleanArray sba, int key) { return low; } - /** * Interface for controlling * scroll speed as a function of touch position and time. Use @@ -2650,10 +2733,10 @@ public interface DragScrollProfile { private class DragScroller implements Runnable { private boolean mAbort; - + private long mPrevTime; private long mCurrTime; - + private int dy; private float dt; private long tStart; @@ -2662,14 +2745,14 @@ private class DragScroller implements Runnable { public final static int STOP = -1; public final static int UP = 0; public final static int DOWN = 1; - + private float mScrollSpeed; // pixels per ms - + private boolean mScrolling = false; - + private int mLastHeader; private int mFirstFooter; - + public boolean isScrolling() { return mScrolling; } @@ -2678,11 +2761,12 @@ public int getScrollDir() { return mScrolling ? scrollDir : STOP; } - public DragScroller() {} - + public DragScroller() { + } + public void startScrolling(int dir) { if (!mScrolling) { - //Debug.startMethodTracing("dslv-scroll"); + // Debug.startMethodTracing("dslv-scroll"); mAbort = false; mScrolling = true; tStart = SystemClock.uptimeMillis(); @@ -2691,7 +2775,7 @@ public void startScrolling(int dir) { post(this); } } - + public void stopScrolling(boolean now) { if (now) { DragSortListView.this.removeCallbacks(this); @@ -2700,10 +2784,9 @@ public void stopScrolling(boolean now) { mAbort = true; } - //Debug.stopMethodTracing(); + // Debug.stopMethodTracing(); } - - + @Override public void run() { if (mAbort) { @@ -2711,7 +2794,7 @@ public void run() { return; } - //Log.d("mobeta", "scroll"); + // Log.d("mobeta", "scroll"); final int first = getFirstVisiblePosition(); final int last = getLastVisiblePosition(); @@ -2724,7 +2807,7 @@ public void run() { if (scrollDir == UP) { View v = getChildAt(0); - //Log.d("mobeta", "vtop="+v.getTop()+" padtop="+padTop); + // Log.d("mobeta", "vtop="+v.getTop()+" padtop="+padTop); if (v == null) { mScrolling = false; return; @@ -2734,7 +2817,8 @@ public void run() { return; } } - mScrollSpeed = mScrollProfile.getSpeed((mUpScrollStartYF - maxY) / mDragUpScrollHeight, mPrevTime); + mScrollSpeed = mScrollProfile.getSpeed((mUpScrollStartYF - maxY) + / mDragUpScrollHeight, mPrevTime); } else { View v = getChildAt(last - first); if (v == null) { @@ -2746,14 +2830,16 @@ public void run() { return; } } - mScrollSpeed = -mScrollProfile.getSpeed((minY - mDownScrollStartYF) / mDragDownScrollHeight, mPrevTime); + mScrollSpeed = -mScrollProfile.getSpeed((minY - mDownScrollStartYF) + / mDragDownScrollHeight, mPrevTime); } - + mCurrTime = SystemClock.uptimeMillis(); dt = (float) (mCurrTime - mPrevTime); // dy is change in View position of a list item; i.e. positive dy - // means user is scrolling up (list item moves down the screen, remember + // means user is scrolling up (list item moves down the screen, + // remember // y=0 is at top of View). dy = (int) Math.round(mScrollSpeed * dt); @@ -2786,8 +2872,8 @@ public void run() { doDragFloatView(movePos, moveItem, false); mPrevTime = mCurrTime; - //Log.d("mobeta", " updated prevTime="+mPrevTime); - + // Log.d("mobeta", " updated prevTime="+mPrevTime); + post(this); } } @@ -2796,7 +2882,7 @@ private class DragSortTracker { StringBuilder mBuilder = new StringBuilder(); File mFile; - + private int mNumInBuffer = 0; private int mNumFlushes = 0; @@ -2817,7 +2903,7 @@ public DragSortTracker() { } } - + public void startTracking() { mBuilder.append("\n"); mNumFlushes = 0; @@ -2837,7 +2923,7 @@ public void appendState() { mBuilder.append(first + i).append(","); } mBuilder.append("\n"); - + mBuilder.append(" "); for (int i = 0; i < children; ++i) { mBuilder.append(getChildAt(i).getTop()).append(","); @@ -2851,14 +2937,15 @@ public void appendState() { mBuilder.append(" ").append(mFirstExpPos).append("\n"); mBuilder.append(" ") - .append(getItemHeight(mFirstExpPos) - getChildHeight(mFirstExpPos)) - .append("\n"); + .append(getItemHeight(mFirstExpPos) - getChildHeight(mFirstExpPos)) + .append("\n"); mBuilder.append(" ").append(mSecondExpPos).append("\n"); mBuilder.append(" ") - .append(getItemHeight(mSecondExpPos) - getChildHeight(mSecondExpPos)) - .append("\n"); + .append(getItemHeight(mSecondExpPos) - getChildHeight(mSecondExpPos)) + .append("\n"); mBuilder.append(" ").append(mSrcPos).append("\n"); - mBuilder.append(" ").append(mFloatViewHeight + getDividerHeight()).append("\n"); + mBuilder.append(" ").append(mFloatViewHeight + getDividerHeight()) + .append("\n"); mBuilder.append(" ").append(getHeight()).append("\n"); mBuilder.append(" ").append(mLastY).append("\n"); mBuilder.append(" ").append(mFloatViewMid).append("\n"); @@ -2867,7 +2954,7 @@ public void appendState() { mBuilder.append(getShuffleEdge(first + i, getChildAt(i).getTop())).append(","); } mBuilder.append("\n"); - + mBuilder.append("\n"); mNumInBuffer++; @@ -2876,7 +2963,7 @@ public void appendState() { mNumInBuffer = 0; } } - + public void flush() { if (!mTracking) { return; @@ -2909,10 +2996,7 @@ public void stopTracking() { mTracking = false; } } - } - - } diff --git a/pom.xml b/pom.xml index 7048967..f8b156b 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ Copyright 2012 Andreas Schildbach com.mobeta.android.dslv parent - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT pom