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>
时间: 2024-10-13 08:44:55

Android UI--SwipeDismissListView_直接删除的相关文章

Android UI开发——使用Fragment构建灵活的桌面

当我们设计应用程序时,希望能够尽最大限度的适配各种设备,包括4寸屏.7寸屏.10寸屏等等,Android开发文档给了我们参考,而且Google  IO的app(如图二)也实现了这种思想,他们都是使用layout.layout-large里面不同的布局文件实现的.当设计应用程序,你可以在不同的布局结构中重复使用Fragment,以支持众多的屏幕尺寸,,在可用的屏幕空间上优化用户体验.例如在手持设备(如Nexus 4)上,一个屏显示一个Fragment,在更大屏(如Nexus 7)上可以使用多个Fr

Android UI编程之自定义控件初步(下)——CustomEditText

概述: 基于对上一篇博客<Android UI编程之自定义控件初步(上)--ImageButton>的学习,我们对自定义控件也有了一个初步的认识.那现在我们可以再试着对EditText进行一些自定义的学习.以下有两种方式的自定义UI编程分享给大家. 示例:带删除按钮的输入框 效果图展示:   基本雏形搭建: 大家可以从上面的效果图上看到两个东西:左侧的EditText和右侧的图片(这里是一个Button).我们在EditText中的输入为空的时候,不显示右侧的清除按钮.一旦EditText中输

Android UI相关开源项目库汇总

最近做了一个Android UI相关开源项目库汇总,里面集合了OpenDigg 上的优质的Android开源项目库,方便移动开发人员便捷的找到自己需要的项目工具等,感兴趣的可以到GitHub上给个star. 抽屉菜单 MaterialDrawer ★7337 - 安卓抽屉效果实现方案 Side-Menu.Android ★3865 - 创意边侧菜单 FlowingDrawer ★1744 - 向右滑动流动抽屉效果 SlidingRootNav ★1338 - 仿DrawerLayout的View

Android UI开发第三十篇——使用Fragment构建灵活的桌面

摘要: 当我们设计应用程序时,希望能够尽最大限度的适配各种设备,包括4寸屏.7寸屏.10寸屏等等,Android开发文档给了我们参考,而且Google IO的app(如图二)也实现了这种思想,他们都是使用layout.layout-large里 ... 当我们设计应用程序时,希望能够尽最大限度的适配各种设备,包括4寸屏.7寸屏. 10寸屏等等,Android开发文档给了我们参考,而且Google IO的app(如图二)也实现了这种思想,他们都是使用layout.layout-large里面不同的

向产品宣战——开发者眼中的Android UI Design

向产品宣战--开发者眼中的Android UI Design 准备了半个月,思考产品设计.交互设计,见证了公司的产品.UE和开发的撕逼,将自己的思考.感悟,整理成下文,谨代表广大程序猿,向设计狮.产品X开战.希望广大程序猿能够坚持贯彻Google的Material Design,切实认真负责的执行Android的设计思想,将MD设计带到产品中去,不做中国特色的App,将Android的开发风气带到正轨,树立正确的开发观.设计观. 希望广大程序猿朋友在博客后面留言.签名,规范Android设计.树

【转】【Android UI设计与开发】第07期:底部菜单栏(二)Fragment的详细介绍和使用方法

原始地址:http://blog.csdn.net/yangyu20121224/article/category/1431917/1 由于TabActivity在Android4.0以后已经被完全弃用,那么我就不再浪费口水继续讲解它了,取而代之的是Fragment.Fragment是Android3.0新增的概念,Fragment翻译成中文是碎片的意思,不过却和Activity十分的相似,这一篇我花大量的篇幅来详细的讲解Fragment的介绍和使用方法. 一.Fragment的基础知识介绍  

Android UI设计之&lt;十&gt;自定义ListView,实现QQ空间阻尼下拉刷新和渐变菜单栏效果

转载请注明出处:http://blog.csdn.net/llew2011/article/details/51559694 好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客是在2014年写的:Android UI设计之<七>自定义Dialog,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来.近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效

【转】Android UI系列-----时间、日期、Toasts和进度条Dialog

原文网址:http://www.cnblogs.com/xiaoluo501395377/p/3421727.html 您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内容有任何疑问, 可以通过评论或发邮件的方式联系我: [email protected] / [email protected] 如果需要转载,请注明出处,谢谢!! 本篇随笔将继续学

Android UI学习 - ListView (android.R.layout.simple_list_item_1是个什么东西)

Android UI学习 - ListView 2010-06-20 18:21:35 标签:Android UI 移动开发 ListView ListActivity 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://android.blog.51cto.com/268543/336162 ListActivity ListActivity是一个专门显示ListView的Activity类,它内置了ListView对象,只要我

Android UI组件进阶(2)——仿Windows对话框

Android UI组件进阶(2)--仿Windows对话框 在开始本章前先祝大家中秋节快乐哈,相信很多上班的朋友都是放三天假的哈! 有时间的话回家陪陪父母吧!树欲静而风不止,子欲养而亲不待!岁月不饶人! 好了,道理和祝福语就说到这里了,今天给大家准备的是模仿Windows风格对话框! 效果图: 相信大部分的AlertDialog都是下面这个样子的: 今天给大家讲解的对话框是下面这样的: 对比两种对话框,站在用户的角度,相信你更加钟情于第二种颜色鲜明的对话框 好了下面就开始讲解如何制作模仿win