你的位置:首页 > 操作系统

[操作系统]左右滑动删除ListView条目Item


Android的SwipeToDismiss是github上一个第三方开源框架(github上的项目链接地址:https://github.com/romannurik/Android-SwipeToDismiss )。

该开源项目旨在:使得一个ListView的item在用户的手指在屏幕上左滑或者右滑时候,删除当前的这个ListView Item。

下载下来只需找到其中的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.zzw.testswipetodismiss; 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 client102    * 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 item112      * positions.113      *114      * @param listView        The originating {@link ListView}.115      * @param reverseSortedPositions An array of positions to dismiss, sorted in descending116      *                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 to126    *         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 {@link150    * ListView} using {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}.151    * If a scroll listener is already assigned, the caller should still pass scroll changes through152    * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is153    * paused during list view scrolling.</p>154    *155    * @see SwipeDismissListViewTouchListener156   */157   public AbsListView.OnScrollListener makeScrollListener() {158     return new AbsListView.OnScrollListener() {159       @Override160       public void onScrollStateChanged(AbsListView absListView, int scrollState) {161         setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);162       }163 164       @Override165       public void onScroll(AbsListView absListView, int i, int i1, int i2) {166       }167     };168   }169 170   @Override171   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 flag183 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 201         if (mDownView != null) {202           mDownX = motionEvent.getRawX();203           mDownY = motionEvent.getRawY();204           mDownPosition = mListView.getPositionForView(mDownView);205           if (mCallbacks.canDismiss(mDownPosition)) {206             mVelocityTracker = VelocityTracker.obtain();207             mVelocityTracker.addMovement(motionEvent);208           } else {209             mDownView = null;210           }211         }212         return false;213       }214 215       case MotionEvent.ACTION_CANCEL: {216         if (mVelocityTracker == null) {217           break;218         }219 220         if (mDownView != null && mSwiping) {221           // cancel222           mDownView.animate()223               .translationX(0)224               .alpha(1)225               .setDuration(mAnimationTime)226               .setListener(null);227         }228         mVelocityTracker.recycle();229         mVelocityTracker = null;230         mDownX = 0;231         mDownY = 0;232         mDownView = null;233         mDownPosition = ListView.INVALID_POSITION;234         mSwiping = false;235         break;236       }237 238       case MotionEvent.ACTION_UP: {239         if (mVelocityTracker == null) {240           break;241         }242 243         float deltaX = motionEvent.getRawX() - mDownX;244         mVelocityTracker.addMovement(motionEvent);245         mVelocityTracker.computeCurrentVelocity(1000);246         float velocityX = mVelocityTracker.getXVelocity();247         float absVelocityX = Math.abs(velocityX);248         float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());249         boolean dismiss = false;250         boolean dismissRight = false;251         if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {252           dismiss = true;253           dismissRight = deltaX > 0;254         } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity255             && absVelocityY < absVelocityX && mSwiping) {256           // dismiss only if flinging in the same direction as dragging257           dismiss = (velocityX < 0) == (deltaX < 0);258           dismissRight = mVelocityTracker.getXVelocity() > 0;259         }260         if (dismiss && mDownPosition != ListView.INVALID_POSITION) {261           // dismiss262           final View downView = mDownView; // mDownView gets null'd before animation ends263           final int downPosition = mDownPosition;264           ++mDismissAnimationRefCount;265           mDownView.animate()266               .translationX(dismissRight ? mViewWidth : -mViewWidth)267               .alpha(0)268               .setDuration(mAnimationTime)269               .setListener(new AnimatorListenerAdapter() {270                 @Override271                 public void onAnimationEnd(Animator animation) {272                   performDismiss(downView, downPosition);273                 }274               });275         } else {276           // cancel277           mDownView.animate()278               .translationX(0)279               .alpha(1)280               .setDuration(mAnimationTime)281               .setListener(null);282         }283         mVelocityTracker.recycle();284         mVelocityTracker = null;285         mDownX = 0;286         mDownY = 0;287         mDownView = null;288         mDownPosition = ListView.INVALID_POSITION;289         mSwiping = false;290         break;291       }292 293       case MotionEvent.ACTION_MOVE: {294         if (mVelocityTracker == null || mPaused) {295           break;296         }297 298         mVelocityTracker.addMovement(motionEvent);299         float deltaX = motionEvent.getRawX() - mDownX;300         float deltaY = motionEvent.getRawY() - mDownY;301         if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {302           mSwiping = true;303           mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);304           mListView.requestDisallowInterceptTouchEvent(true);305 306           // Cancel ListView's touch (un-highlighting the item)307           MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);308           cancelEvent.setAction(MotionEvent.ACTION_CANCEL |309               (motionEvent.getActionIndex()310                   << MotionEvent.ACTION_POINTER_INDEX_SHIFT));311           mListView.onTouchEvent(cancelEvent);312           cancelEvent.recycle();313         }314 315         if (mSwiping) {316           mDownView.setTranslationX(deltaX - mSwipingSlop);317           mDownView.setAlpha(Math.max(0f, Math.min(1f,318               1f - 2f * Math.abs(deltaX) / mViewWidth)));319           return true;320         }321         break;322       }323     }324     return false;325   }326 327   class PendingDismissData implements Comparable<PendingDismissData> {328     public int position;329     public View view;330 331     public PendingDismissData(int position, View view) {332       this.position = position;333       this.view = view;334     }335 336     @Override337     public int compareTo(PendingDismissData other) {338       // Sort by descending position339       return other.position - position;340     }341   }342 343   private void performDismiss(final View dismissView, final int dismissPosition) {344     // Animate the dismissed list item to zero-height and fire the dismiss callback when345     // all dismissed list item animations have completed. This triggers layout on each animation346     // frame; in the future we may want to do something smarter and more performant.347 348     final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();349     final int originalHeight = dismissView.getHeight();350 351     ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);352 353     animator.addListener(new AnimatorListenerAdapter() {354       @Override355       public void onAnimationEnd(Animator animation) {356         --mDismissAnimationRefCount;357         if (mDismissAnimationRefCount == 0) {358           // No active animations, process all pending dismisses.359           // Sort by descending position360           Collections.sort(mPendingDismisses);361 362           int[] dismissPositions = new int[mPendingDismisses.size()];363           for (int i = mPendingDismisses.size() - 1; i >= 0; i--) {364             dismissPositions[i] = mPendingDismisses.get(i).position;365           }366           mCallbacks.onDismiss(mListView, dismissPositions);367           368           // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss 369           // animation with a stale position370           mDownPosition = ListView.INVALID_POSITION;371 372           ViewGroup.LayoutParams lp;373           for (PendingDismissData pendingDismiss : mPendingDismisses) {374             // Reset view presentation375             pendingDismiss.view.setAlpha(1f);376             pendingDismiss.view.setTranslationX(0);377             lp = pendingDismiss.view.getLayoutParams();378             lp.height = originalHeight;379             pendingDismiss.view.setLayoutParams(lp);380           }381 382           // Send a cancel event383           long time = SystemClock.uptimeMillis();384           MotionEvent cancelEvent = MotionEvent.obtain(time, time,385               MotionEvent.ACTION_CANCEL, 0, 0, 0);386           mListView.dispatchTouchEvent(cancelEvent);387 388           mPendingDismisses.clear();389         }390       }391     });392 393     animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {394       @Override395       public void onAnimationUpdate(ValueAnimator valueAnimator) {396         lp.height = (Integer) valueAnimator.getAnimatedValue();397         dismissView.setLayoutParams(lp);398       }399     });400 401     mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView));402     animator.start();403   }404 }

SwipeDismissListViewTouchListener.java

下面看测试的代码:

 

activity_main.

 1 <RelativeLayout ="http://schemas.android.com/apk/res/android" 2   ="http://schemas.android.com/tools" 3   android:layout_width="match_parent" 4   android:layout_height="match_parent" > 5  6   <ListView 7     android:id="@+id/listView" 8     android:layout_width="match_parent" 9     android:layout_height="match_parent" />10 11 </RelativeLayout>

activity_main.

 MainActivity.java:

 1 package com.zzw.testswipetodismiss; 2  3 import java.util.ArrayList; 4  5 import android.app.Activity; 6 import android.os.Bundle; 7 import android.widget.ArrayAdapter; 8 import android.widget.ListView; 9 import android.widget.Toast;10 11 public class MainActivity extends Activity {12 13   private ListView listView;14   private ArrayList<String> datas;15   private ArrayAdapter adapter;16 17   @Override18   protected void onCreate(Bundle savedInstanceState) {19     super.onCreate(savedInstanceState);20     setContentView(R.layout.activity_main);21 22     datas = new ArrayList<String>();23 24     // 添加测试数据25     for (int i = 0; i <= 50; i++) {26       datas.add("测试数据-->" + i);27     }28 29     listView = (ListView) findViewById(R.id.listView);30 31     adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, datas);32     listView.setAdapter(adapter);33 34     // 将ListView传递过来。35     SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(listView,36         new SwipeDismissListViewTouchListener.DismissCallbacks() {37 38           // 此处将执行删除,记得要notifyDataSetChanged()39           @Override40           public void onDismiss(ListView listView, int[] reverseSortedPositions) {41             for (int pos : reverseSortedPositions) {42               datas.remove(pos);43               Toast.makeText(getApplicationContext(), "数据" + pos + "删除成功", 0).show();44             }45             adapter.notifyDataSetChanged();46           }47 48           @Override49           public boolean canDismiss(int position) {50 51             return true;52           }53         });54 55     listView.setOnTouchListener(touchListener);56   }57 58 }