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

[操作系统]下拉刷新列表添加SwipeDismissListViewTouchListener实现滑动删除某一列。


《Android SwipeToDismiss:左右滑动删除ListView条目Item》

Android的SwipeToDismiss是github上一个第三方开源框架(github上的项目链接地址:https://github.com/romannurik/Android-SwipeToDismiss )。该开源项目旨在:使得一个ListView的item在用户的手指在屏幕上左滑或者右滑时候,删除当前的这个ListView Item。

 

 1 package com.lixu.SwipeRefreshLayoutyongfa; 2  3 import java.util.ArrayList; 4 import java.util.HashMap; 5  6 import com.lixu.SwipeRefreshLayoutyongfa.SwipeDismissListViewTouchListener.DismissCallbacks; 7 import android.app.Activity; 8 import android.content.Context; 9 import android.os.AsyncTask; 10 import android.os.Bundle; 11 import android.support.v4.widget.SwipeRefreshLayout; 12 import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener; 13 import android.view.LayoutInflater; 14 import android.view.View; 15 import android.view.ViewGroup; 16 import android.widget.ArrayAdapter; 17 import android.widget.ImageView; 18 import android.widget.ListView; 19 import android.widget.TextView; 20  21 public class MainActivity extends Activity { 22   private ArrayAdapter<String> adapter; 23   private ArrayList<HashMap<String, Object>> data; 24   private int count = 0; 25   SwipeRefreshLayout srl; 26   private int[] image = { R.drawable.beijing, R.drawable.chengdu, R.drawable.guangzhou, R.drawable.hangzhou, 27       R.drawable.wuhan, R.drawable.xian, R.drawable.shenzhen }; 28  29   private String[] city = { "北京", "成都", "广州", "杭州", "武汉", "西安", "深圳" }; 30   private String KEY_IMAGE = "image"; 31   private String KEY_TXT = "txt"; 32  33   private int i = 0; 34   private int j = 0; 35  36   ListView lv; 37  38   SwipeDismissListViewTouchListener touchlistener; 39  40   @Override 41   protected void onCreate(Bundle savedInstanceState) { 42     super.onCreate(savedInstanceState); 43     setContentView(R.layout.activity_main); 44  45     data = new ArrayList<HashMap<String, Object>>(); 46  47     lv = (ListView) findViewById(R.id.lv); 48  49     srl = (SwipeRefreshLayout) findViewById(R.id.srl); 50     // 设置刷新动画的颜色. 51     srl.setColorSchemeResources(android.R.color.holo_green_light, android.R.color.holo_blue_bright, 52         android.R.color.holo_red_light); 53  54     srl.setOnRefreshListener(new OnRefreshListener() { 55       // SwipeRefreshLayout接管其包裹的ListView下拉事件。 56       // 每一次对ListView的下拉动作,将触发SwipeRefreshLayout的onRefresh()。 57       @SuppressWarnings("unchecked") 58       @Override 59       public void onRefresh() { 60  61         new MyAsyncTask().execute(); 62  63       } 64     }); 65     adapter = new MyAdapter(this, -1); 66  67     lv.setAdapter(adapter); 68     addSwipeDismissListView(); 69     // 往listview里面添加触摸事件 70     lv.setOnTouchListener(touchlistener); 71   } 72  73   private void addSwipeDismissListView() { 74     touchlistener = new SwipeDismissListViewTouchListener(lv, new DismissCallbacks() { 75  76       @Override 77       public void onDismiss(ListView listView, int[] reverseSortedPositions) { 78         for (int pos : reverseSortedPositions) 79           // 获取滑动的下标行后删除这一行 80           data.remove(pos); 81         // 每次要刷新适配器 82         adapter.notifyDataSetChanged();
Toast.makeText(getApplicationContext(), "删除成功!", 0).show(); 83 } 84 85 @Override 86 public boolean canDismiss(int position) { 87 return true; 88 } 89 }); 90 } 91 92 private class MyAdapter extends ArrayAdapter { 93 private LayoutInflater flater; 94 95 public MyAdapter(Context context, int resource) { 96 super(context, resource); 97 flater = LayoutInflater.from(context); 98 } 99 100 @Override101 public int getCount() {102 return data.size();103 }104 105 @Override106 public View getView(int position, View convertView, ViewGroup parent) {107 if (convertView == null)108 convertView = flater.inflate(R.layout.item, null);109 110 TextView tv = (TextView) convertView.findViewById(R.id.tv);111 tv.setText(data.get(position).get(KEY_TXT) + "");112 113 ImageView iv = (ImageView) convertView.findViewById(R.id.iv);114 iv.setImageResource((Integer) data.get(position).get(KEY_IMAGE));115 116 return convertView;117 }118 }119 120 @SuppressWarnings("rawtypes")121 private class MyAsyncTask extends AsyncTask {122 private HashMap<String, Object> map;123 124 @Override125 protected void onPreExecute() {126 // 刷新开始127 srl.setRefreshing(true);128 map = new HashMap<String, Object>();129 }130 131 @Override132 protected Object doInBackground(Object... params) {133 // 防止下标越界,到达数组长度后返回开始134 if (i == image.length)135 i = 0;136 if (j == city.length)137 j = 0;138 139 map.put(KEY_IMAGE, image[i++]);140 map.put(KEY_TXT, city[j++]);141 142 // 处理一些耗时的事件143 return map;144 }145 146 @SuppressWarnings("unchecked")147 @Override148 protected void onPostExecute(Object result) {149 // 每次从头部添加150 data.add(0, (HashMap<String, Object>) result);151 // 刷新适配器152 adapter.notifyDataSetChanged();153 // 刷新完毕154 srl.setRefreshing(false);155 }156 157 }158 159 }

需要用的SwipeDismissListViewTouchListener类

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


 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   tools:context="com.lixu.SwipeRefreshLayoutyongfa.MainActivity" > 6  7   <android.support.v4.widget.SwipeRefreshLayout 8     android:id="@+id/srl" 9     android:layout_width="match_parent"10     android:layout_height="match_parent" >11 12     <ListView13       android:id="@+id/lv"14       android:layout_width="match_parent"15       android:layout_height="match_parent" />16   </android.support.v4.widget.SwipeRefreshLayout>17 18 </RelativeLayout>

 1 <??> 2 <LinearLayout ="http://schemas.android.com/apk/res/android" 3   android:layout_width="match_parent" 4   android:layout_height="match_parent" 5   android:orientation="vertical" > 6  7   <ImageView 8     android:id="@+id/iv" 9     android:layout_width="50dp"10     android:layout_height="50dp" />11 12   <TextView13     android:id="@+id/tv"14     android:layout_width="wrap_content"15     android:layout_height="wrap_content"16     android:textSize="25sp" />17 18 </LinearLayout>

运行效果图: