Skip to content

Commit

Permalink
Added preDismiss hook. Useful for showing things like Alert Dialog be…
Browse files Browse the repository at this point in the history
…fore dimissing a view
  • Loading branch information
BharathMG committed Apr 21, 2015
1 parent 9fb7a5c commit ded7f15
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.android.swipedismiss;

import android.animation.Animator;
Expand All @@ -39,16 +38,16 @@
* dismissable. {@link ListView} is given special treatment because by default it handles touches
* for its list items... i.e. it's in charge of drawing the pressed state (the list selector),
* handling list item clicks, etc.
*
* <p/>
* <p>After creating the listener, the caller should also call
* {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing
* in the scroll listener returned by {@link #makeScrollListener()}. If a scroll listener is
* already assigned, the caller should still pass scroll changes through to this listener. This will
* ensure that this {@link SwipeDismissListViewTouchListener} is paused during list view
* scrolling.</p>
*
* <p/>
* <p>Example usage:</p>
*
* <p/>
* <pre>
* SwipeDismissListViewTouchListener touchListener =
* new SwipeDismissListViewTouchListener(
Expand All @@ -64,14 +63,14 @@
* listView.setOnTouchListener(touchListener);
* listView.setOnScrollListener(touchListener.makeScrollListener());
* </pre>
*
* <p/>
* <p>This class Requires API level 12 or later due to use of {@link
* ViewPropertyAnimator}.</p>
*
* <p/>
* <p>For a generalized {@link View.OnTouchListener} that makes any view dismissable,
* see {@link SwipeDismissTouchListener}.</p>
* see {@link }.</p>
*
* @see SwipeDismissTouchListener
* @see
*/
public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
// Cached ViewConfiguration and system-wide constant values
Expand All @@ -97,9 +96,6 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
private View mDownView;
private boolean mPaused;

//View properties
private float mAlpha = 1f;

/**
* The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client
* about a successful dismissal of one or more list item positions.
Expand All @@ -119,6 +115,12 @@ public interface DismissCallbacks {
* order for convenience.
*/
void onDismiss(ListView listView, int[] reverseSortedPositions);

/**
* Called before dismissing list item. Can be used to show alert dialog to confirm removal of an item.
* @param dismissDirection
*/
void preDismiss(boolean dismissDirection);
}

/**
Expand Down Expand Up @@ -202,7 +204,6 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
}

if (mDownView != null) {
mAlpha = mDownView.getAlpha();
mDownX = motionEvent.getRawX();
mDownY = motionEvent.getRawY();
mDownPosition = mListView.getPositionForView(mDownView);
Expand All @@ -225,17 +226,11 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
// cancel
mDownView.animate()
.translationX(0)
.alpha(mAlpha)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
mDownX = 0;
mDownY = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;
reset();
break;
}

Expand All @@ -262,35 +257,13 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
dismissRight = mVelocityTracker.getXVelocity() > 0;
}
if (dismiss && mDownPosition != ListView.INVALID_POSITION) {
// dismiss
final View downView = mDownView; // mDownView gets null'd before animation ends
final int downPosition = mDownPosition;
++mDismissAnimationRefCount;
mDownView.animate()
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
performDismiss(downView, downPosition);
}
});
// pre dismiss
mCallbacks.preDismiss(dismissRight);
} else {
// cancel
mDownView.animate()
.translationX(0)
.alpha(mAlpha)
.setDuration(mAnimationTime)
.setListener(null);
onDismissCancelled();
}
mVelocityTracker.recycle();
mVelocityTracker = null;
mDownX = 0;
mDownY = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;

break;
}

Expand Down Expand Up @@ -328,6 +301,49 @@ public void onAnimationEnd(Animator animation) {
return false;
}

/**
* Called from the View Activity or Fragment if the current row can be successfully dismissed.
* @param dismissRight
*/
public void onDismissAllowed(boolean dismissRight) {
final View downView = mDownView; // mDownView gets null'd before animation ends
final int downPosition = mDownPosition;
++mDismissAnimationRefCount;
mDownView.animate()
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
performDismiss(downView, downPosition);
}
});
reset();
}

private void reset() {
mVelocityTracker.recycle();
mVelocityTracker = null;
mDownX = 0;
mDownY = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;
}

/**
* Called from the View Activity or Fragment if the current row should not be dismissed and revert the swipe.
*/
public void onDismissCancelled() {
mDownView.animate()
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
reset();
}

class PendingDismissData implements Comparable<PendingDismissData> {
public int position;
public View view;
Expand Down Expand Up @@ -368,15 +384,15 @@ public void onAnimationEnd(Animator animation) {
dismissPositions[i] = mPendingDismisses.get(i).position;
}
mCallbacks.onDismiss(mListView, dismissPositions);
// Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss

// Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss
// animation with a stale position
mDownPosition = ListView.INVALID_POSITION;

ViewGroup.LayoutParams lp;
for (PendingDismissData pendingDismiss : mPendingDismisses) {
// Reset view presentation
pendingDismiss.view.setAlpha(mAlpha);
pendingDismiss.view.setAlpha(1f);
pendingDismiss.view.setTranslationX(0);
lp = pendingDismiss.view.getLayoutParams();
lp.height = originalHeight;
Expand Down
97 changes: 52 additions & 45 deletions src/com/example/android/swipedismiss/SwipeDismissTouchListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.android.swipedismiss;

import android.animation.Animator;
Expand All @@ -32,15 +31,15 @@
/**
* A {@link View.OnTouchListener} that makes any {@link View} dismissable when the
* user swipes (drags her finger) horizontally across the view.
*
* <p/>
* <p><em>For {@link ListView} list items that don't manage their own touch events
* (i.e. you're using
* {@link ListView#setOnItemClickListener(AdapterView.OnItemClickListener)}
* or an equivalent listener on {@link ListActivity} or
* {@link ListFragment}, use {@link SwipeDismissListViewTouchListener} instead.</em></p>
*
* <p/>
* <p>Example usage:</p>
*
* <p/>
* <pre>
* view.setOnTouchListener(new SwipeDismissTouchListener(
* view,
Expand All @@ -51,7 +50,7 @@
* }
* }));
* </pre>
*
* <p/>
* <p>This class Requires API level 12 or later due to use of {@link
* android.view.ViewPropertyAnimator}.</p>
*
Expand All @@ -78,9 +77,6 @@ public class SwipeDismissTouchListener implements View.OnTouchListener {
private VelocityTracker mVelocityTracker;
private float mTranslationX;

//View properties
private float mAlpha = 1f;

/**
* The callback interface used by {@link SwipeDismissTouchListener} to inform its client
* about a successful dismissal of the view for which it was created.
Expand All @@ -98,15 +94,22 @@ public interface DismissCallbacks {
* @param token The optional token passed to this object's constructor.
*/
void onDismiss(View view, Object token);

/**
* Called before dismissing list item. Can be used to show alert dialog to confirm removal of an item.
*
* @param dismissDirection
*/
void preDismiss(boolean dismissDirection);
}

/**
* Constructs a new swipe-to-dismiss touch listener for the given view.
*
* @param view The view to make dismissable.
* @param token An optional token/cookie object to be passed through to the callback.
* @param view The view to make dismissable.
* @param token An optional token/cookie object to be passed through to the callback.
* @param callbacks The callback to trigger when the user has indicated that she would like to
* dismiss this view.
* dismiss this view.
*/
public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) {
ViewConfiguration vc = ViewConfiguration.get(view.getContext());
Expand Down Expand Up @@ -138,10 +141,6 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(motionEvent);
}

if(mView != null) {
mAlpha = mView.getAlpha();
}
return false;
}

Expand Down Expand Up @@ -169,31 +168,12 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
dismissRight = mVelocityTracker.getXVelocity() > 0;
}
if (dismiss) {
// dismiss
mView.animate()
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
performDismiss();
}
});
// pre dismiss
mCallbacks.preDismiss(dismissRight);
} else if (mSwiping) {
// cancel
mView.animate()
.translationX(0)
.alpha(mAlpha)
.setDuration(mAnimationTime)
.setListener(null);
onDismissCancelled();
}
mVelocityTracker.recycle();
mVelocityTracker = null;
mTranslationX = 0;
mDownX = 0;
mDownY = 0;
mSwiping = false;
break;
}

Expand All @@ -204,15 +184,10 @@ public void onAnimationEnd(Animator animation) {

mView.animate()
.translationX(0)
.alpha(mAlpha)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
mVelocityTracker.recycle();
mVelocityTracker = null;
mTranslationX = 0;
mDownX = 0;
mDownY = 0;
mSwiping = false;
reset();
break;
}

Expand Down Expand Up @@ -252,6 +227,38 @@ public void onAnimationEnd(Animator animation) {
return false;
}

private void reset() {
mVelocityTracker.recycle();
mVelocityTracker = null;
mTranslationX = 0;
mDownX = 0;
mDownY = 0;
mSwiping = false;
}

public void onDismissAllowed(boolean dismissRight) {
mView.animate()
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
performDismiss();
}
});
reset();
}

public void onDismissCancelled() {
mView.animate()
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
reset();
}

private void performDismiss() {
// Animate the dismissed view to zero-height and then fire the dismiss callback.
// This triggers layout on each animation frame; in the future we may want to do something
Expand All @@ -267,7 +274,7 @@ private void performDismiss() {
public void onAnimationEnd(Animator animation) {
mCallbacks.onDismiss(mView, mToken);
// Reset view presentation
mView.setAlpha(mAlpha);
mView.setAlpha(1f);
mView.setTranslationX(0);
lp.height = originalHeight;
mView.setLayoutParams(lp);
Expand Down

0 comments on commit ded7f15

Please sign in to comment.