Android UI--SwipeDismissListView_直接删除

SwipeDismissTouchListener.java

  1 /*
  2  * Copyright 2013 Google Inc.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *     http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package com.wzq.swipe2dismiss;
 18 
 19 import android.animation.Animator;
 20 import android.animation.AnimatorListenerAdapter;
 21 import android.animation.ValueAnimator;
 22 import android.app.ListActivity;
 23 import android.app.ListFragment;
 24 import android.view.MotionEvent;
 25 import android.view.VelocityTracker;
 26 import android.view.View;
 27 import android.view.ViewConfiguration;
 28 import android.view.ViewGroup;
 29 import android.widget.AdapterView;
 30 import android.widget.ListView;
 31 
 32 /**
 33  * A {@link View.OnTouchListener} that makes any {@link View} dismissable when the
 34  * user swipes (drags her finger) horizontally across the view.
 35  *
 36  * <p><em>For {@link ListView} list items that don't manage their own touch events
 37  * (i.e. you're using
 38  * {@link ListView#setOnItemClickListener(AdapterView.OnItemClickListener)}
 39  * or an equivalent listener on {@link ListActivity} or
 40  * {@link ListFragment}, use {@link SwipeDismissListViewTouchListener} instead.</em></p>
 41  *
 42  * <p>Example usage:</p>
 43  *
 44  * <pre>
 45  * view.setOnTouchListener(new SwipeDismissTouchListener(
 46  *         view,
 47  *         null, // Optional token/cookie object
 48  *         new SwipeDismissTouchListener.OnDismissCallback() {
 49  *             public void onDismiss(View view, Object token) {
 50  *                 parent.removeView(view);
 51  *             }
 52  *         }));
 53  * </pre>
 54  *
 55  * <p>This class Requires API level 12 or later due to use of {@link
 56  * android.view.ViewPropertyAnimator}.</p>
 57  *
 58  * @see SwipeDismissListViewTouchListener
 59  */
 60 public class SwipeDismissTouchListener implements View.OnTouchListener {
 61     // Cached ViewConfiguration and system-wide constant values
 62     private int mSlop;
 63     private int mMinFlingVelocity;
 64     private int mMaxFlingVelocity;
 65     private long mAnimationTime;
 66 
 67     // Fixed properties
 68     private View mView;
 69     private DismissCallbacks mCallbacks;
 70     private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
 71 
 72     // Transient properties
 73     private float mDownX;
 74     private float mDownY;
 75     private boolean mSwiping;
 76     private int mSwipingSlop;
 77     private Object mToken;
 78     private VelocityTracker mVelocityTracker;
 79     private float mTranslationX;
 80 
 81     /**
 82      * The callback interface used by {@link SwipeDismissTouchListener} to inform its client
 83      * about a successful dismissal of the view for which it was created.
 84      */
 85     public interface DismissCallbacks {
 86         /**
 87          * Called to determine whether the view can be dismissed.
 88          */
 89         boolean canDismiss(Object token);
 90 
 91         /**
 92          * Called when the user has indicated they she would like to dismiss the view.
 93          *
 94          * @param view  The originating {@link View} to be dismissed.
 95          * @param token The optional token passed to this object's constructor.
 96          */
 97         void onDismiss(View view, Object token);
 98     }
 99 
100     /**
101      * Constructs a new swipe-to-dismiss touch listener for the given view.
102      *
103      * @param view     The view to make dismissable.
104      * @param token    An optional token/cookie object to be passed through to the callback.
105      * @param callbacks The callback to trigger when the user has indicated that she would like to
106      *                 dismiss this view.
107      */
108     public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) {
109         ViewConfiguration vc = ViewConfiguration.get(view.getContext());
110         mSlop = vc.getScaledTouchSlop();
111         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
112         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
113         mAnimationTime = view.getContext().getResources().getInteger(
114                 android.R.integer.config_shortAnimTime);
115         mView = view;
116         mToken = token;
117         mCallbacks = callbacks;
118     }
119 
120     @Override
121     public boolean onTouch(View view, MotionEvent motionEvent) {
122         // offset because the view is translated during swipe
123         motionEvent.offsetLocation(mTranslationX, 0);
124 
125         if (mViewWidth < 2) {
126             mViewWidth = mView.getWidth();
127         }
128 
129         switch (motionEvent.getActionMasked()) {
130             case MotionEvent.ACTION_DOWN: {
131                 // TODO: ensure this is a finger, and set a flag
132                 mDownX = motionEvent.getRawX();
133                 mDownY = motionEvent.getRawY();
134                 if (mCallbacks.canDismiss(mToken)) {
135                     mVelocityTracker = VelocityTracker.obtain();
136                     mVelocityTracker.addMovement(motionEvent);
137                 }
138                 return false;
139             }
140 
141             case MotionEvent.ACTION_UP: {
142                 if (mVelocityTracker == null) {
143                     break;
144                 }
145 
146                 float deltaX = motionEvent.getRawX() - mDownX;
147                 mVelocityTracker.addMovement(motionEvent);
148                 mVelocityTracker.computeCurrentVelocity(1000);
149                 float velocityX = mVelocityTracker.getXVelocity();
150                 float absVelocityX = Math.abs(velocityX);
151                 float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
152                 boolean dismiss = false;
153                 boolean dismissRight = false;
154                 if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
155                     dismiss = true;
156                     dismissRight = deltaX > 0;
157                 } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
158                         && absVelocityY < absVelocityX
159                         && absVelocityY < absVelocityX && mSwiping) {
160                     // dismiss only if flinging in the same direction as dragging
161                     dismiss = (velocityX < 0) == (deltaX < 0);
162                     dismissRight = mVelocityTracker.getXVelocity() > 0;
163                 }
164                 if (dismiss) {
165                     // dismiss
166                     mView.animate()
167                             .translationX(dismissRight ? mViewWidth : -mViewWidth)
168                             .alpha(0)
169                             .setDuration(mAnimationTime)
170                             .setListener(new AnimatorListenerAdapter() {
171                                 @Override
172                                 public void onAnimationEnd(Animator animation) {
173                                     performDismiss();
174                                 }
175                             });
176                 } else if (mSwiping) {
177                     // cancel
178                     mView.animate()
179                             .translationX(0)
180                             .alpha(1)
181                             .setDuration(mAnimationTime)
182                             .setListener(null);
183                 }
184                 mVelocityTracker.recycle();
185                 mVelocityTracker = null;
186                 mTranslationX = 0;
187                 mDownX = 0;
188                 mDownY = 0;
189                 mSwiping = false;
190                 break;
191             }
192 
193             case MotionEvent.ACTION_CANCEL: {
194                 if (mVelocityTracker == null) {
195                     break;
196                 }
197 
198                 mView.animate()
199                         .translationX(0)
200                         .alpha(1)
201                         .setDuration(mAnimationTime)
202                         .setListener(null);
203                 mVelocityTracker.recycle();
204                 mVelocityTracker = null;
205                 mTranslationX = 0;
206                 mDownX = 0;
207                 mDownY = 0;
208                 mSwiping = false;
209                 break;
210             }
211 
212             case MotionEvent.ACTION_MOVE: {
213                 if (mVelocityTracker == null) {
214                     break;
215                 }
216 
217                 mVelocityTracker.addMovement(motionEvent);
218                 float deltaX = motionEvent.getRawX() - mDownX;
219                 float deltaY = motionEvent.getRawY() - mDownY;
220                 if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
221                     mSwiping = true;
222                     mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
223                     mView.getParent().requestDisallowInterceptTouchEvent(true);
224 
225                     
226                     MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
227                     cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
228                             (motionEvent.getActionIndex() <<
229                                     MotionEvent.ACTION_POINTER_INDEX_SHIFT));
230                     mView.onTouchEvent(cancelEvent);
231                     cancelEvent.recycle();
232                 }
233 
234                 if (mSwiping) {
235                     mTranslationX = deltaX;
236                     mView.setTranslationX(deltaX - mSwipingSlop);
237                     // TODO: use an ease-out interpolator or such
238 //                    mView.setAlpha(Math.max(0f, Math.min(1f,1f - 2f * Math.abs(deltaX) / mViewWidth)));
239                     mView.setAlpha(Math.max(0f, Math.min(1f, 1f - Math.abs(deltaX) * 2f / mViewWidth)));
240                     return true;
241                 }
242                 break;
243             }
244         }
245         return false;
246     }
247 
248     private void performDismiss() {
249         // Animate the dismissed view to zero-height and then fire the dismiss callback.
250         // This triggers layout on each animation frame; in the future we may want to do something
251         // smarter and more performant.
252 
253         final ViewGroup.LayoutParams lp = mView.getLayoutParams();
254         final int originalHeight = mView.getHeight();
255 
256         ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
257 
258         animator.addListener(new AnimatorListenerAdapter() {
259             @Override
260             public void onAnimationEnd(Animator animation) {
261                 mCallbacks.onDismiss(mView, mToken);
262                 // Reset view presentation
263                 mView.setAlpha(1f);
264                 mView.setTranslationX(0);
265                 lp.height = originalHeight;
266                 mView.setLayoutParams(lp);
267             }
268         });
269 
270         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
271             @Override
272             public void onAnimationUpdate(ValueAnimator valueAnimator) {
273                 lp.height = (Integer) valueAnimator.getAnimatedValue();
274                 mView.setLayoutParams(lp);
275             }
276         });
277 
278         animator.start();
279     }
280 }

SwipeDismissListViewTouchListener.java

  1 /*
  2  * Copyright 2013 Google Inc.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *     http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package com.wzq.swipe2dismiss;
 18 
 19 import android.animation.Animator;
 20 import android.animation.AnimatorListenerAdapter;
 21 import android.animation.ValueAnimator;
 22 import android.graphics.Rect;
 23 import android.os.SystemClock;
 24 import android.view.MotionEvent;
 25 import android.view.VelocityTracker;
 26 import android.view.View;
 27 import android.view.ViewConfiguration;
 28 import android.view.ViewGroup;
 29 import android.view.ViewPropertyAnimator;
 30 import android.widget.AbsListView;
 31 import android.widget.ListView;
 32 
 33 import java.util.ArrayList;
 34 import java.util.Collections;
 35 import java.util.List;
 36 
 37 /**
 38  * A {@link View.OnTouchListener} that makes the list items in a {@link ListView}
 39  * dismissable. {@link ListView} is given special treatment because by default it handles touches
 40  * for its list items... i.e. it's in charge of drawing the pressed state (the list selector),
 41  * handling list item clicks, etc.
 42  *
 43  * <p>After creating the listener, the caller should also call
 44  * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing
 45  * in the scroll listener returned by {@link #makeScrollListener()}. If a scroll listener is
 46  * already assigned, the caller should still pass scroll changes through to this listener. This will
 47  * ensure that this {@link SwipeDismissListViewTouchListener} is paused during list view
 48  * scrolling.</p>
 49  *
 50  * <p>Example usage:</p>
 51  *
 52  * <pre>
 53  * SwipeDismissListViewTouchListener touchListener =
 54  *         new SwipeDismissListViewTouchListener(
 55  *                 listView,
 56  *                 new SwipeDismissListViewTouchListener.OnDismissCallback() {
 57  *                     public void onDismiss(ListView listView, int[] reverseSortedPositions) {
 58  *                         for (int position : reverseSortedPositions) {
 59  *                             adapter.remove(adapter.getItem(position));
 60  *                         }
 61  *                         adapter.notifyDataSetChanged();
 62  *                     }
 63  *                 });
 64  * listView.setOnTouchListener(touchListener);
 65  * listView.setOnScrollListener(touchListener.makeScrollListener());
 66  * </pre>
 67  *
 68  * <p>This class Requires API level 12 or later due to use of {@link
 69  * ViewPropertyAnimator}.</p>
 70  *
 71  * <p>For a generalized {@link View.OnTouchListener} that makes any view dismissable,
 72  * see {@link SwipeDismissTouchListener}.</p>
 73  *
 74  * @see SwipeDismissTouchListener
 75  */
 76 public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
 77     // Cached ViewConfiguration and system-wide constant values
 78     private int mSlop;
 79     private int mMinFlingVelocity;
 80     private int mMaxFlingVelocity;
 81     private long mAnimationTime;
 82 
 83     // Fixed properties
 84     private ListView mListView;
 85     private DismissCallbacks mCallbacks;
 86     private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
 87 
 88     // Transient properties
 89     private List<PendingDismissData> mPendingDismisses = new ArrayList<PendingDismissData>();
 90     private int mDismissAnimationRefCount = 0;
 91     private float mDownX;
 92     private float mDownY;
 93     private boolean mSwiping;
 94     private int mSwipingSlop;
 95     private VelocityTracker mVelocityTracker;
 96     private int mDownPosition;
 97     private View mDownView;
 98     private boolean mPaused;
 99 
100     /**
101      * The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client
102      * about a successful dismissal of one or more list item positions.
103      */
104     public interface DismissCallbacks {
105         /**
106          * Called to determine whether the given position can be dismissed.
107          */
108         boolean canDismiss(int position);
109 
110         /**
111          * Called when the user has indicated they she would like to dismiss one or more list item
112          * positions.
113          *
114          * @param listView               The originating {@link ListView}.
115          * @param reverseSortedPositions An array of positions to dismiss, sorted in descending
116          *                               order for convenience.
117          */
118         void onDismiss(ListView listView, int[] reverseSortedPositions);
119     }
120 
121     /**
122      * Constructs a new swipe-to-dismiss touch listener for the given list view.
123      *
124      * @param listView  The list view whose items should be dismissable.
125      * @param callbacks The callback to trigger when the user has indicated that she would like to
126      *                  dismiss one or more list items.
127      */
128     public SwipeDismissListViewTouchListener(ListView listView, DismissCallbacks callbacks) {
129         ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
130         mSlop = vc.getScaledTouchSlop();
131         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
132         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
133         mAnimationTime = listView.getContext().getResources().getInteger(
134                 android.R.integer.config_shortAnimTime);
135         mListView = listView;
136         mCallbacks = callbacks;
137     }
138 
139     /**
140      * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures.
141      *
142      * @param enabled Whether or not to watch for gestures.
143      */
144     public void setEnabled(boolean enabled) {
145         mPaused = !enabled;
146     }
147 
148     /**
149      * Returns an {@link AbsListView.OnScrollListener} to be added to the {@link
150      * ListView} using {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}.
151      * If a scroll listener is already assigned, the caller should still pass scroll changes through
152      * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is
153      * paused during list view scrolling.</p>
154      *
155      * @see SwipeDismissListViewTouchListener
156      */
157     public AbsListView.OnScrollListener makeScrollListener() {
158         return new AbsListView.OnScrollListener() {
159             @Override
160             public void onScrollStateChanged(AbsListView absListView, int scrollState) {
161                 setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
162             }
163 
164             @Override
165             public void onScroll(AbsListView absListView, int i, int i1, int i2) {
166             }
167         };
168     }
169 
170     @Override
171     public boolean onTouch(View view, MotionEvent motionEvent) {
172         if (mViewWidth < 2) {
173             mViewWidth = mListView.getWidth();
174         }
175 
176         switch (motionEvent.getActionMasked()) {
177             case MotionEvent.ACTION_DOWN: {
178                 if (mPaused) {
179                     return false;
180                 }
181 
182                 // TODO: ensure this is a finger, and set a flag
183 
184                 // Find the child view that was touched (perform a hit test)
185                 Rect rect = new Rect();
186                 int childCount = mListView.getChildCount();
187                 int[] listViewCoords = new int[2];
188                 mListView.getLocationOnScreen(listViewCoords);
189                 int x = (int) motionEvent.getRawX() - listViewCoords[0];
190                 int y = (int) motionEvent.getRawY() - listViewCoords[1];
191                 View child;
192                 for (int i = 0; i < childCount; i++) {
193                     child = mListView.getChildAt(i);
194                     child.getHitRect(rect);
195                     if (rect.contains(x, y)) {
196                         mDownView = child;
197                         break;
198                     }
199                 }
200                 //另外一种获取mDownView 的方法,不知是否有效~~
201 //                int downViewIndex = mListView.pointToPosition((int)motionEvent.getX(),(int)motionEvent.getY());
202 //                mDownView = mListView.getChildAt(downViewIndex - mListView.getFirstVisiblePosition());
203 
204                 if (mDownView != null) {
205                     mDownX = motionEvent.getRawX();
206                     mDownY = motionEvent.getRawY();
207                     mDownPosition = mListView.getPositionForView(mDownView);
208                     if (mCallbacks.canDismiss(mDownPosition)) {
209                         mVelocityTracker = VelocityTracker.obtain();
210                         mVelocityTracker.addMovement(motionEvent);
211                     } else {
212                         mDownView = null;
213                     }
214                 }
215                 return false;
216             }
217 
218             case MotionEvent.ACTION_CANCEL: {
219                 if (mVelocityTracker == null) {
220                     break;
221                 }
222 
223                 if (mDownView != null && mSwiping) {
224                     // cancel
225                     mDownView.animate()
226                             .translationX(0)
227                             .alpha(1)
228                             .setDuration(mAnimationTime)
229                             .setListener(null);
230                 }
231                 mVelocityTracker.recycle();
232                 mVelocityTracker = null;
233                 mDownX = 0;
234                 mDownY = 0;
235                 mDownView = null;
236                 mDownPosition = ListView.INVALID_POSITION;
237                 mSwiping = false;
238                 break;
239             }
240 
241             case MotionEvent.ACTION_UP: {
242                 if (mVelocityTracker == null) {
243                     break;
244                 }
245 
246                 float deltaX = motionEvent.getRawX() - mDownX;
247                 mVelocityTracker.addMovement(motionEvent);
248                 mVelocityTracker.computeCurrentVelocity(1000);
249                 float velocityX = mVelocityTracker.getXVelocity();
250                 float absVelocityX = Math.abs(velocityX);
251                 float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
252                 boolean dismiss = false;
253                 boolean dismissRight = false;
254                 if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
255                     dismiss = true;
256                     dismissRight = deltaX > 0;
257                 } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
258                         && absVelocityY < absVelocityX && mSwiping) {
259                     // dismiss only if flinging in the same direction as dragging
260                     dismiss = (velocityX < 0) == (deltaX < 0);
261                     dismissRight = mVelocityTracker.getXVelocity() > 0;
262                 }
263                 if (dismiss && mDownPosition != ListView.INVALID_POSITION) {
264                     // dismiss
265                     final View downView = mDownView; // mDownView gets null'd before animation ends
266                     final int downPosition = mDownPosition;
267                     ++mDismissAnimationRefCount;
268                     mDownView.animate()
269                             .translationX(dismissRight ? mViewWidth : -mViewWidth)
270                             .alpha(0)
271                             .setDuration(mAnimationTime)
272                             .setListener(new AnimatorListenerAdapter() {
273                                 @Override
274                                 public void onAnimationEnd(Animator animation) {
275                                     performDismiss(downView, downPosition);
276                                 }
277                             });
278                 } else {
279                     // cancel
280                     mDownView.animate()
281                             .translationX(0)
282                             .alpha(1)
283                             .setDuration(mAnimationTime)
284                             .setListener(null);
285                 }
286                 mVelocityTracker.recycle();
287                 mVelocityTracker = null;
288                 mDownX = 0;
289                 mDownY = 0;
290                 mDownView = null;
291                 mDownPosition = ListView.INVALID_POSITION;
292                 mSwiping = false;
293                 break;
294             }
295 
296             case MotionEvent.ACTION_MOVE: {
297                 if (mVelocityTracker == null || mPaused) {
298                     break;
299                 }
300 
301                 mVelocityTracker.addMovement(motionEvent);
302                 float deltaX = motionEvent.getRawX() - mDownX;
303                 float deltaY = motionEvent.getRawY() - mDownY;
304                 if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
305                     mSwiping = true;
306                     mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
307                     mListView.requestDisallowInterceptTouchEvent(true);
308 
309                     // Cancel ListView's touch (un-highlighting the item)
310                     MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
311                     cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
312                             (motionEvent.getActionIndex()
313                                     << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
314                     mListView.onTouchEvent(cancelEvent);
315                     cancelEvent.recycle();
316                 }
317 
318                 if (mSwiping) {
319                     mDownView.setTranslationX(deltaX - mSwipingSlop);
320                     mDownView.setAlpha(Math.max(0f, Math.min(1f,
321                             1f - 2f * Math.abs(deltaX) / mViewWidth)));
322                     return true;
323                 }
324                 break;
325             }
326         }
327         return false;
328     }
329 
330     class PendingDismissData implements Comparable<PendingDismissData> {
331         public int position;
332         public View view;
333 
334         public PendingDismissData(int position, View view) {
335             this.position = position;
336             this.view = view;
337         }
338 
339         @Override
340         public int compareTo(PendingDismissData other) {
341             // Sort by descending position
342             return other.position - position;
343         }
344     }
345 
346     private void performDismiss(final View dismissView, final int dismissPosition) {
347         // Animate the dismissed list item to zero-height and fire the dismiss callback when
348         // all dismissed list item animations have completed. This triggers layout on each animation
349         // frame; in the future we may want to do something smarter and more performant.
350 
351         final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
352         final int originalHeight = dismissView.getHeight();
353 
354         ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
355 
356         animator.addListener(new AnimatorListenerAdapter() {
357             @Override
358             public void onAnimationEnd(Animator animation) {
359                 --mDismissAnimationRefCount;
360                 if (mDismissAnimationRefCount == 0) {
361                     // No active animations, process all pending dismisses.
362                     // Sort by descending position
363                     Collections.sort(mPendingDismisses);
364 
365                     int[] dismissPositions = new int[mPendingDismisses.size()];
366                     for (int i = mPendingDismisses.size() - 1; i >= 0; i--) {
367                         dismissPositions[i] = mPendingDismisses.get(i).position;
368                     }
369                     mCallbacks.onDismiss(mListView, dismissPositions);
370                     
371                     // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss 
372                     // animation with a stale position
373                     mDownPosition = ListView.INVALID_POSITION;
374 
375                     ViewGroup.LayoutParams lp;
376                     for (PendingDismissData pendingDismiss : mPendingDismisses) {
377                         // Reset view presentation // TODO 
378                         pendingDismiss.view.setAlpha(1f);
379                         pendingDismiss.view.setTranslationX(0);
380                         lp = pendingDismiss.view.getLayoutParams();
381                         lp.height = originalHeight;
382                         pendingDismiss.view.setLayoutParams(lp);
383                     }
384 
385                     // Send a cancel event
386                     long time = SystemClock.uptimeMillis();
387                     MotionEvent cancelEvent = MotionEvent.obtain(time, time,
388                             MotionEvent.ACTION_CANCEL, 0, 0, 0);
389                     mListView.dispatchTouchEvent(cancelEvent);
390 
391                     mPendingDismisses.clear();
392                 }
393             }
394         });
395 
396         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
397             @Override
398             public void onAnimationUpdate(ValueAnimator valueAnimator) {
399                 lp.height = (Integer) valueAnimator.getAnimatedValue();// TODO 
400                 dismissView.setLayoutParams(lp);
401             }
402         });
403 
404         mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView));
405         animator.start();
406     }
407 }

SwipeDismissActivity.java

  1 /*
  2  * Copyright 2013 Google Inc.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *     http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package com.wzq.swipe2dismiss;
 18 
 19 import android.app.ListActivity;
 20 import android.os.Bundle;
 21 import android.view.View;
 22 import android.view.ViewGroup;
 23 import android.widget.ArrayAdapter;
 24 import android.widget.Button;
 25 import android.widget.ListView;
 26 import android.widget.Toast;
 27 
 28 import java.util.ArrayList;
 29 import java.util.Arrays;
 30 
 31 import com.example.wzq1412.R;
 32 
 33 public class Swipe2DismissActivity extends ListActivity {
 34     ArrayAdapter<String> mAdapter;
 35 
 36     public void onCreate(Bundle savedInstanceState) {
 37         super.onCreate(savedInstanceState);
 38         setContentView(R.layout.a_swipe2dismiss);
 39 
 40         // Set up ListView example
 41         String[] items = new String[20];
 42         for (int i = 0; i < items.length; i++) {
 43             items[i] = "Item " + (i + 1);
 44         }
 45 
 46         mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
 47                 android.R.id.text1, new ArrayList<String>(Arrays.asList(items)));
 48         setListAdapter(mAdapter);
 49 
 50         ListView listView = getListView();
 51         // Create a ListView-specific touch listener. ListViews are given
 52         // special treatment because
 53         // by default they handle touches for their list items... i.e. they're
 54         // in charge of drawing
 55         // the pressed state (the list selector), handling list item clicks,
 56         // etc.
 57         SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(
 58                 listView, new SwipeDismissListViewTouchListener.DismissCallbacks() {
 59                     @Override
 60                     public boolean canDismiss(int position) {
 61                         return true;
 62                     }
 63 
 64                     @Override
 65                     public void onDismiss(ListView listView, int[] reverseSortedPositions) {
 66                         // TODO at
 67                         // java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
 68                         if (mAdapter.getCount() == 0) {
 69                             return;
 70                         }
 71                         for (int position : reverseSortedPositions) {
 72                             if (position >= mAdapter.getCount()) {
 73                                 return;
 74                             }
 75                             mAdapter.remove(mAdapter.getItem(position));
 76                         }
 77                         mAdapter.notifyDataSetChanged();
 78                     }
 79                 });
 80         listView.setOnTouchListener(touchListener);
 81         // Setting this scroll listener is required to ensure that during
 82         // ListView scrolling,
 83         // we don't look for swipes.
 84         listView.setOnScrollListener(touchListener.makeScrollListener());
 85 
 86         // Set up normal ViewGroup example
 87         final ViewGroup dismissableContainer = (ViewGroup) findViewById(R.id.dismissable_container);
 88         for (int i = 0; i < items.length; i++) {
 89             final Button dismissableButton = new Button(this);
 90             dismissableButton.setLayoutParams(new ViewGroup.LayoutParams(
 91                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
 92             dismissableButton.setText("Button " + (i + 1));
 93             dismissableButton.setOnClickListener(new View.OnClickListener() {
 94                 @Override
 95                 public void onClick(View view) {
 96                     Toast.makeText(Swipe2DismissActivity.this,
 97                             "Clicked " + ((Button) view).getText(), Toast.LENGTH_SHORT).show();
 98                 }
 99             });
100             // Create a generic swipe-to-dismiss touch listener.
101             dismissableButton.setOnTouchListener(new SwipeDismissTouchListener(dismissableButton,
102                     null, new SwipeDismissTouchListener.DismissCallbacks() {
103                         @Override
104                         public boolean canDismiss(Object token) {
105                             return true;
106                         }
107 
108                         @Override
109                         public void onDismiss(View view, Object token) {
110                             dismissableContainer.removeView(dismissableButton);
111                         }
112                     }));
113             dismissableContainer.addView(dismissableButton);
114         }
115     }
116 
117     @Override
118     protected void onListItemClick(ListView listView, View view, int position, long id) {
119         Toast.makeText(this, "Clicked " + getListAdapter().getItem(position).toString(),
120                 Toast.LENGTH_SHORT).show();
121     }
122 }

a_swipe2dismiss.xml

 1 <!--
 2   Copyright 2013 Google Inc.
 3 
 4   Licensed under the Apache License, Version 2.0 (the "License");
 5   you may not use this file except in compliance with the License.
 6   You may obtain a copy of the License at
 7 
 8       http://www.apache.org/licenses/LICENSE-2.0
 9 
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15   -->
16 
17 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
18     android:layout_width="match_parent"
19     android:layout_height="match_parent"
20     android:orientation="horizontal"
21     android:baselineAligned="false"
22     android:padding="16dp">
23 
24     <LinearLayout android:layout_width="0dp"
25         android:layout_weight="1"
26         android:layout_height="match_parent"
27         android:layout_marginRight="8dp"
28         android:orientation="vertical">
29 
30         <TextView android:layout_width="match_parent"
31             android:layout_height="wrap_content"
32             style="?android:listSeparatorTextViewStyle"
33             android:text="Simple Views" />
34 
35         <ScrollView android:fillViewport="true"
36             android:layout_width="match_parent"
37             android:layout_weight="1"
38             android:layout_height="0dp">
39             <LinearLayout android:id="@+id/dismissable_container"
40                 android:layout_width="match_parent"
41                 android:layout_height="wrap_content"
42                 android:orientation="vertical" />
43         </ScrollView>
44 
45     </LinearLayout>
46 
47     <LinearLayout android:layout_width="0dp"
48         android:layout_weight="1"
49         android:layout_height="match_parent"
50         android:layout_marginLeft="8dp"
51         android:orientation="vertical">
52 
53         <TextView android:layout_width="match_parent"
54             android:layout_height="wrap_content"
55             style="?android:listSeparatorTextViewStyle"
56             android:text="ListView" />
57 
58         <ListView
59             android:id="@android:id/list"
60             android:layout_weight="1"
61             android:layout_width="match_parent"
62             android:layout_height="0dp" />
63     </LinearLayout>
64 </LinearLayout>

 

posted @ 2015-01-12 18:20  王自强  阅读(420)  评论(0编辑  收藏  举报