Skip to content

Commit

Permalink
Do not allow dragging of items when items are in an undo state.
Browse files Browse the repository at this point in the history
  • Loading branch information
nhaarman committed Aug 7, 2014
1 parent 6413b18 commit f443437
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.nhaarman.listviewanimations.itemmanipulation;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.os.Build;
import android.support.annotation.NonNull;
Expand All @@ -25,10 +26,10 @@
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.WrapperListAdapter;

import com.nhaarman.listviewanimations.BaseAdapterDecorator;
import com.nhaarman.listviewanimations.itemmanipulation.animateaddition.AnimateAdditionAdapter;
Expand All @@ -45,16 +46,23 @@
import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoCallback;
import com.nhaarman.listviewanimations.util.Insertable;

import java.util.Collection;
import java.util.HashSet;

/**
* A {@link android.widget.ListView} implementation which provides the following functionality:
* <ul>
* <li>Drag and drop</li>
* <li>Swipe to dismiss</li>
* <li>Swipe to dismiss with contextual undo</li>
* <li>Animate addition</li>
* </ul>
*/
public class DynamicListView extends ListView {

@NonNull
private final MyOnScrollListener mMyOnScrollListener;

/**
* The {@link com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DragAndDropHandler}
* that will handle drag and drop functionality, if set.
Expand All @@ -76,19 +84,30 @@ public class DynamicListView extends ListView {
@Nullable
private TouchEventHandler mCurrentHandlingTouchEventHandler;

/**
* The {@link com.nhaarman.listviewanimations.itemmanipulation.animateaddition.AnimateAdditionAdapter}
* that is possibly set to animate insertions.
*/
@Nullable
private AnimateAdditionAdapter<?> mAnimateAdditionAdapter;

@Nullable
private SwipeUndoAdapter mSwipeUndoAdapter;

public DynamicListView(@NonNull final Context context) {
super(context);
this(context, null);
}

public DynamicListView(@NonNull final Context context, @NonNull final AttributeSet attrs) {
super(context, attrs);
public DynamicListView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
//noinspection HardCodedStringLiteral
this(context, attrs, Resources.getSystem().getIdentifier("listViewStyle", "attr", "android"));
}

public DynamicListView(@NonNull final Context context, @NonNull final AttributeSet attrs, final int defStyle) {
public DynamicListView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);

mMyOnScrollListener = new MyOnScrollListener();
super.setOnScrollListener(mMyOnScrollListener);
}

@Override
Expand All @@ -100,6 +119,11 @@ public void setOnTouchListener(final OnTouchListener onTouchListener) {
super.setOnTouchListener(onTouchListener);
}

@Override
public void setOnScrollListener(final OnScrollListener onScrollListener) {
mMyOnScrollListener.addOnScrollListener(onScrollListener);
}

/**
* Enables the drag and drop functionality for this {@code DynamicListView}.
* <p/>
Expand Down Expand Up @@ -150,26 +174,12 @@ public void enableSwipeUndo(@NonNull final UndoCallback undoCallback) {
* @throws java.lang.IllegalStateException if the adapter that was set does not extend {@code SwipeUndoAdapter}.
*/
public void enableSimpleSwipeUndo() {
ListAdapter adapter = getAdapter();

if (adapter instanceof WrapperListAdapter) {
adapter = ((WrapperListAdapter) adapter).getWrappedAdapter();
}

SwipeUndoAdapter swipeUndoAdapter = null;
while (adapter instanceof BaseAdapterDecorator) {
if (adapter instanceof SwipeUndoAdapter) {
swipeUndoAdapter = (SwipeUndoAdapter) adapter;
}
adapter = ((BaseAdapterDecorator) adapter).getDecoratedBaseAdapter();
}

if (swipeUndoAdapter == null) {
if (mSwipeUndoAdapter == null) {
throw new IllegalStateException("enableSimpleSwipeUndo requires a SwipeUndoAdapter to be set as an adapter");
}

mSwipeTouchListener = new SwipeUndoTouchListener(new DynamicListViewWrapper(this), swipeUndoAdapter.getUndoCallback());
swipeUndoAdapter.setSwipeUndoTouchListener((SwipeUndoTouchListener) mSwipeTouchListener);
mSwipeTouchListener = new SwipeUndoTouchListener(new DynamicListViewWrapper(this), mSwipeUndoAdapter.getUndoCallback());
mSwipeUndoAdapter.setSwipeUndoTouchListener((SwipeUndoTouchListener) mSwipeTouchListener);
}

/**
Expand All @@ -195,10 +205,14 @@ public void disableSwipeToDismiss() {
@Override
public void setAdapter(final ListAdapter adapter) {
ListAdapter wrappedAdapter = adapter;
mSwipeUndoAdapter = null;

if (adapter instanceof BaseAdapter) {
BaseAdapter rootAdapter = (BaseAdapter) wrappedAdapter;
while (rootAdapter instanceof BaseAdapterDecorator) {
if (rootAdapter instanceof SwipeUndoAdapter) {
mSwipeUndoAdapter = (SwipeUndoAdapter) rootAdapter;
}
rootAdapter = ((BaseAdapterDecorator) rootAdapter).getDecoratedBaseAdapter();
}

Expand All @@ -216,24 +230,22 @@ public void setAdapter(final ListAdapter adapter) {
}
}

public void setAdapter(final SwipeUndoAdapter adapter) {
mSwipeTouchListener = new SwipeUndoTouchListener(new DynamicListViewWrapper(this), adapter.getUndoCallback());
setAdapter((BaseAdapter) adapter);
}

@Override
public boolean dispatchTouchEvent(@NonNull final MotionEvent ev) {
if (mCurrentHandlingTouchEventHandler == null) {
/* None of the TouchEventHandlers are actively consuming events yet. */
boolean firstTimeInteracting = false;

/* Offer the event to the DragAndDropHandler */
if (mDragAndDropHandler != null) {
mDragAndDropHandler.onTouchEvent(ev);
firstTimeInteracting = mDragAndDropHandler.isInteracting();
if (firstTimeInteracting) {
mCurrentHandlingTouchEventHandler = mDragAndDropHandler;
sendCancelEvent(mSwipeTouchListener, ev);
/* We don't support dragging items when there are items in the undo state. */
if (!(mSwipeTouchListener instanceof SwipeUndoTouchListener) || !((SwipeUndoTouchListener) mSwipeTouchListener).hasPendingItems()) {
/* Offer the event to the DragAndDropHandler */
if (mDragAndDropHandler != null) {
mDragAndDropHandler.onTouchEvent(ev);
firstTimeInteracting = mDragAndDropHandler.isInteracting();
if (firstTimeInteracting) {
mCurrentHandlingTouchEventHandler = mDragAndDropHandler;
sendCancelEvent(mSwipeTouchListener, ev);
}
}
}

Expand Down Expand Up @@ -411,6 +423,11 @@ public void setOnItemMovedListener(@Nullable final OnItemMovedListener onItemMov
* or if there is no adapter set.
*/
public void startDragging(final int position) {
/* We don't support dragging items when items are in the undo state. */
if (mSwipeTouchListener instanceof SwipeUndoTouchListener && ((SwipeUndoTouchListener) mSwipeTouchListener).hasPendingItems()) {
return;
}

if (mDragAndDropHandler != null) {
mDragAndDropHandler.startDragging(position);
}
Expand Down Expand Up @@ -525,4 +542,33 @@ public void undo(@NonNull final View view) {
}
}
}

private class MyOnScrollListener implements OnScrollListener {

private final Collection<OnScrollListener> mOnScrollListeners = new HashSet<>();

@Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
for (OnScrollListener onScrollListener : mOnScrollListeners) {
onScrollListener.onScrollStateChanged(view, scrollState);
}

if (scrollState == SCROLL_STATE_TOUCH_SCROLL) {
if (mSwipeTouchListener instanceof SwipeUndoTouchListener) {
((SwipeUndoTouchListener) mSwipeTouchListener).dimissPending();
}
}
}

@Override
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) {
for (OnScrollListener onScrollListener : mOnScrollListeners) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}

public void addOnScrollListener(final OnScrollListener onScrollListener) {
mOnScrollListeners.add(onScrollListener);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,20 @@
import com.nineoldandroids.animation.ObjectAnimator;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* A {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissTouchListener} that adds an undo stage to the item swiping.
*/
public class SwipeUndoTouchListener extends SwipeDismissTouchListener {

private static final String ALPHA = "alpha";

private static final String TRANSLATION_X = "translationX";

/**
* The callback which gets notified of events.
*/
Expand All @@ -48,6 +54,12 @@ public class SwipeUndoTouchListener extends SwipeDismissTouchListener {
@NonNull
private final Collection<Integer> mUndoPositions = new LinkedList<>();

/**
* The {@link android.view.View}s that are in the undo state.
*/
@NonNull
private final Map<Integer, View> mUndoViews = new HashMap<>();

/**
* The positions that have been dismissed.
*/
Expand All @@ -60,12 +72,6 @@ public class SwipeUndoTouchListener extends SwipeDismissTouchListener {
@NonNull
private final Collection<View> mDismissedViews = new LinkedList<>();

/**
* Constructs a new {@code SwipeDismissTouchListener} for the given {@link android.widget.AbsListView}.
*
* @param absListView The {@code AbsListView} whose items should be dismissable.
*/
@SuppressWarnings("UnnecessaryFullyQualifiedName")
public SwipeUndoTouchListener(@NonNull final ListViewWrapper listViewWrapper, @NonNull final UndoCallback callback) {
super(listViewWrapper, callback);
mCallback = callback;
Expand All @@ -75,10 +81,12 @@ public SwipeUndoTouchListener(@NonNull final ListViewWrapper listViewWrapper, @N
protected void afterViewFling(@NonNull final View view, final int position) {
if (mUndoPositions.contains(position)) {
mUndoPositions.remove(position);
mUndoViews.remove(position);
performDismiss(view, position);
hideUndoView(view);
} else {
mUndoPositions.add(position);
mUndoViews.put(position, view);
mCallback.onUndoShown(view, position);
showUndoView(view);
restoreViewPresentation(view);
Expand All @@ -105,6 +113,19 @@ protected void performDismiss(@NonNull final View view, final int position) {
mCallback.onDismiss(view, position);
}

public boolean hasPendingItems() {
return !mUndoPositions.isEmpty();
}

/**
* Dismisses all items that are in the undo state.
*/
public void dimissPending() {
for (int position : mUndoPositions) {
performDismiss(mUndoViews.get(position), position);
}
}

/**
* Sets the visibility of the primary {@link android.view.View} to {@link android.view.View#GONE}, and animates the undo {@code View} in to view.
*
Expand All @@ -115,7 +136,7 @@ private void showUndoView(@NonNull final View view) {

View undoView = mCallback.getUndoView(view);
undoView.setVisibility(View.VISIBLE);
ObjectAnimator.ofFloat(undoView, "alpha", 0f, 1f).start();
ObjectAnimator.ofFloat(undoView, ALPHA, 0f, 1f).start();
}

/**
Expand All @@ -128,6 +149,7 @@ private void hideUndoView(@NonNull final View view) {
mCallback.getUndoView(view).setVisibility(View.GONE);
}


/**
* If necessary, notifies the {@link UndoCallback} to remove dismissed object from the adapter,
* and restores the {@link android.view.View} presentations.
Expand Down Expand Up @@ -173,9 +195,9 @@ public void undo(@NonNull final View view) {

primaryView.setVisibility(View.VISIBLE);

ObjectAnimator undoAlphaAnimator = ObjectAnimator.ofFloat(undoView, "alpha", 1f, 0f);
ObjectAnimator primaryAlphaAnimator = ObjectAnimator.ofFloat(primaryView, "alpha", 0f, 1f);
ObjectAnimator primaryXAnimator = ObjectAnimator.ofFloat(primaryView, "translationX", primaryView.getWidth(), 0f);
ObjectAnimator undoAlphaAnimator = ObjectAnimator.ofFloat(undoView, ALPHA, 1f, 0f);
ObjectAnimator primaryAlphaAnimator = ObjectAnimator.ofFloat(primaryView, ALPHA, 0f, 1f);
ObjectAnimator primaryXAnimator = ObjectAnimator.ofFloat(primaryView, TRANSLATION_X, primaryView.getWidth(), 0f);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(undoAlphaAnimator, primaryAlphaAnimator, primaryXAnimator);
Expand Down

0 comments on commit f443437

Please sign in to comment.