ListView滑动删除

本来准备在ListView的每个Item的布局上设置一个隐藏的Button,当滑动的时候显示。但是因为每次只要存在一个Button,发现每个Item上的Button相互间不好控制。所以决定继承ListView然后结合PopupWindow。

首先是布局文件:

delete_btn.xml:这里只需要一个Button

 1  <?xml version="1.0" encoding="utf-8"?>  
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
 3     android:layout_width="wrap_content"  
 4     android:layout_height="wrap_content"  
 5     android:orientation="vertical" >  
 6       <Button   
 7         android:id="@+id/id_item_btn"  
 8         android:layout_width="60dp"  
 9         android:singleLine="true"  
10         android:layout_height="wrap_content"  
11         android:text="删除"  
12         android:background="@drawable/d_delete_btn"  
13         android:textColor="#ffffff"  
14         android:paddingLeft="15dp"  
15         android:paddingRight="15dp"  
16         android:layout_alignParentRight="true"  
17         android:layout_centerVertical="true"  
18         android:layout_marginRight="15dp"  
19         />  
20 </LinearLayout> 
View Code

主布局文件:activity_main.xml,ListView的每个Item的样式直接使用了系统的android.R.layout.simple_list_item_1

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
 2     xmlns:tools="http://schemas.android.com/tools"  
 3     android:layout_width="match_parent"  
 4     android:layout_height="match_parent" >  
 5   
 6     <com.example.listviewitemslidedeletebtnshow.QQListView  
 7         android:id="@+id/id_listview"  
 8         android:layout_width="fill_parent"  
 9         android:layout_height="wrap_content" >  
10     </com.example.listviewitemslidedeletebtnshow.QQListView>  
11   
12 </RelativeLayout>  
View Code

接下来看看QQListView的实现: 

  1 package com.example.listviewitemslidedeletebtnshow;  
  2   
  3 import android.content.Context;  
  4 import android.util.AttributeSet;  
  5 import android.view.Gravity;  
  6 import android.view.LayoutInflater;  
  7 import android.view.MotionEvent;  
  8 import android.view.View;  
  9 import android.view.ViewConfiguration;  
 10 import android.widget.Button;  
 11 import android.widget.LinearLayout;  
 12 import android.widget.ListView;  
 13 import android.widget.PopupWindow;  
 14   
 15 public class QQListView extends ListView  
 16 {  
 17   
 18     private static final String TAG = "QQlistView";  
 19   
 20     // private static final int VELOCITY_SANP = 200;  
 21     // private VelocityTracker mVelocityTracker;  
 22     /** 
 23      * 用户滑动的最小距离 
 24      */  
 25     private int touchSlop;  
 26   
 27     /** 
 28      * 是否响应滑动 
 29      */  
 30     private boolean isSliding;  
 31   
 32     /** 
 33      * 手指按下时的x坐标 
 34      */  
 35     private int xDown;  
 36     /** 
 37      * 手指按下时的y坐标 
 38      */  
 39     private int yDown;  
 40     /** 
 41      * 手指移动时的x坐标 
 42      */  
 43     private int xMove;  
 44     /** 
 45      * 手指移动时的y坐标 
 46      */  
 47     private int yMove;  
 48   
 49     private LayoutInflater mInflater;  
 50   
 51     private PopupWindow mPopupWindow;  
 52     private int mPopupWindowHeight;  
 53     private int mPopupWindowWidth;  
 54   
 55     private Button mDelBtn;  
 56     /** 
 57      * 为删除按钮提供一个回调接口 
 58      */  
 59     private DelButtonClickListener mListener;  
 60   
 61     /** 
 62      * 当前手指触摸的View 
 63      */  
 64     private View mCurrentView;  
 65   
 66     /** 
 67      * 当前手指触摸的位置 
 68      */  
 69     private int mCurrentViewPos;  
 70   
 71     /** 
 72      * 必要的一些初始化 
 73      *  
 74      * @param context 
 75      * @param attrs 
 76      */  
 77     public QQListView(Context context, AttributeSet attrs)  
 78     {  
 79         super(context, attrs);  
 80   
 81         mInflater = LayoutInflater.from(context);  
 82         touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
 83   
 84         View view = mInflater.inflate(R.layout.delete_btn, null);  
 85         mDelBtn = (Button) view.findViewById(R.id.id_item_btn);  
 86         mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT,  
 87                 LinearLayout.LayoutParams.WRAP_CONTENT);  
 88         /** 
 89          * 先调用下measure,否则拿不到宽和高 
 90          */  
 91         mPopupWindow.getContentView().measure(0, 0);  
 92         mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();  
 93         mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();  
 94     }  
 95   
 96     @Override  
 97     public boolean dispatchTouchEvent(MotionEvent ev)  
 98     {  
 99         int action = ev.getAction();  
100         int x = (int) ev.getX();  
101         int y = (int) ev.getY();  
102         switch (action)  
103         {  
104   
105         case MotionEvent.ACTION_DOWN:  
106             xDown = x;  
107             yDown = y;  
108             /** 
109              * 如果当前popupWindow显示,则直接隐藏,然后屏蔽ListView的touch事件的下传 
110              */  
111             if (mPopupWindow.isShowing())  
112             {  
113                 dismissPopWindow();  
114                 return false;  
115             }  
116             // 获得当前手指按下时的item的位置  
117             mCurrentViewPos = pointToPosition(xDown, yDown);  
118             // 获得当前手指按下时的item  
119             View view = getChildAt(mCurrentViewPos - getFirstVisiblePosition());  
120             mCurrentView = view;  
121             break;  
122         case MotionEvent.ACTION_MOVE:  
123             xMove = x;  
124             yMove = y;  
125             int dx = xMove - xDown;  
126             int dy = yMove - yDown;  
127             /** 
128              * 判断是否是从右到左的滑动 
129              */  
130             if (xMove < xDown && Math.abs(dx) > touchSlop && Math.abs(dy) < touchSlop)  
131             {  
132                 // Log.e(TAG, "touchslop = " + touchSlop + " , dx = " + dx +  
133                 // " , dy = " + dy);  
134                 isSliding = true;  
135             }  
136             break;  
137         }  
138         return super.dispatchTouchEvent(ev);  
139     }  
140   
141     @Override  
142     public boolean onTouchEvent(MotionEvent ev)  
143     {  
144         int action = ev.getAction();  
145         /** 
146          * 如果是从右到左的滑动才相应 
147          */  
148         if (isSliding)  
149         {  
150             switch (action)  
151             {  
152             case MotionEvent.ACTION_MOVE:  
153   
154                 int[] location = new int[2];  
155                 // 获得当前item的位置x与y  
156                 mCurrentView.getLocationOnScreen(location);  
157                 // 设置popupWindow的动画  
158                 mPopupWindow.setAnimationStyle(R.style.popwindow_delete_btn_anim_style);  
159                 mPopupWindow.update();  
160                 mPopupWindow.showAtLocation(mCurrentView, Gravity.LEFT | Gravity.TOP,  
161                         location[0] + mCurrentView.getWidth(), location[1] + mCurrentView.getHeight() / 2  
162                                 - mPopupWindowHeight / 2);  
163                 // 设置删除按钮的回调  
164                 mDelBtn.setOnClickListener(new OnClickListener()  
165                 {  
166                     @Override  
167                     public void onClick(View v)  
168                     {  
169                         if (mListener != null)  
170                         {  
171                             mListener.clickHappend(mCurrentViewPos);  
172                             mPopupWindow.dismiss();  
173                         }  
174                     }  
175                 });  
176                 // Log.e(TAG, "mPopupWindow.getHeight()=" + mPopupWindowHeight);  
177   
178                 break;  
179             case MotionEvent.ACTION_UP:  
180                 isSliding = false;  
181   
182             }  
183             // 相应滑动期间屏幕itemClick事件,避免发生冲突  
184             return true;  
185         }  
186   
187         return super.onTouchEvent(ev);  
188     }  
189   
190     /** 
191      * 隐藏popupWindow 
192      */  
193     private void dismissPopWindow()  
194     {  
195         if (mPopupWindow != null && mPopupWindow.isShowing())  
196         {  
197             mPopupWindow.dismiss();  
198         }  
199     }  
200   
201     public void setDelButtonClickListener(DelButtonClickListener listener)  
202     {  
203         mListener = listener;  
204     }  
205   
206     interface DelButtonClickListener  
207     {  
208         public void clickHappend(int position);  
209     }  
210   
211 }  
View Code

代码注释写得很详细,简单说一下,在dispatchTouchEvent中设置当前是否响应用户滑动,然后在onTouchEvent中判断是否响应,如果响应则popupWindow以动画的形式展示出来。当然屏幕上如果存在PopupWindow则屏幕ListView的滚动与Item的点击,以及从右到左滑动时屏幕Item的click事件。

接下来是MainActivity.java,这里代码很简单不做介绍了。

 1 package com.example.listviewitemslidedeletebtnshow;  
 2   
 3 import java.util.ArrayList;  
 4 import java.util.Arrays;  
 5 import java.util.List;  
 6   
 7 import android.app.Activity;  
 8 import android.os.Bundle;  
 9 import android.view.View;  
10 import android.widget.AdapterView;  
11 import android.widget.AdapterView.OnItemClickListener;  
12 import android.widget.ArrayAdapter;  
13 import android.widget.Toast;  
14   
15 import com.example.listviewitemslidedeletebtnshow.QQListView.DelButtonClickListener;  
16   
17 public class MainActivity extends Activity  
18 {  
19     private QQListView mListView;  
20     private ArrayAdapter<String> mAdapter;  
21     private List<String> mDatas;  
22   
23     @Override  
24     protected void onCreate(Bundle savedInstanceState)  
25     {  
26         super.onCreate(savedInstanceState);  
27         setContentView(R.layout.activity_main);  
28   
29         mListView = (QQListView) findViewById(R.id.id_listview);  
30         // 不要直接Arrays.asList  
31         mDatas = new ArrayList<String>(Arrays.asList("HelloWorld", "Welcome", "Java", "Android", "Servlet", "Struts",  
32                 "Hibernate", "Spring", "HTML5", "Javascript", "Lucene"));  
33         mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mDatas);  
34         mListView.setAdapter(mAdapter);  
35   
36         mListView.setDelButtonClickListener(new DelButtonClickListener()  
37         {  
38             @Override  
39             public void clickHappend(final int position)  
40             {  
41                 Toast.makeText(MainActivity.this, position + " : " + mAdapter.getItem(position), 1).show();  
42                 mAdapter.remove(mAdapter.getItem(position));  
43             }  
44         });  
45   
46         mListView.setOnItemClickListener(new OnItemClickListener()  
47         {  
48             @Override  
49             public void onItemClick(AdapterView<?> parent, View view, int position, long id)  
50             {  
51                 Toast.makeText(MainActivity.this, position + " : " + mAdapter.getItem(position), 1).show();  
52             }  
53         });  
54     }  
55 }  
View Code

效果图如下:楼主使用asm.jar以及gifcamera截的gif,由于button的动画很短感觉截图效果很卡不流畅,大家有什么好的截图,还望推荐。有兴趣的还是下载源码看看效果i。

源码下载:http://download.csdn.net/detail/lmj623565791/7148325

上述文章实现的功能是:在ListView的Item上从右向左滑时,出现删除按钮,点击删除按钮把Item删除。

看过文章后,感觉没有必要把dispatchTouchEvent()和onTouchEvent()两个方法都重写,只要重写onTouchEvent就好了。于是对代码作了一些调整:

MyListView.java
  1 public class MyListView extends ListView {
  2     private static final String TAG = "MyListView";
  3     private int mTouchSlop;
  4     private int mXDown;
  5     private int mYDown;
  6     private int mCurrentPosition;
  7     private View mCurrentView;
  8     private PopupWindow mPopupWindow;
  9     private LayoutInflater mInflater;
 10     private boolean isSliding = false;
 11     // 为删除按钮提供一个回调接口
 12     private DelButtonClickListener mListener;
 13     private Button mDelBtn;
 14     private int mPopupWindowHeight;
 15     private int mPopupWindowWidth;
 16 
 17     public MyListView(Context context, AttributeSet attrs) {
 18         super(context, attrs);
 19         mInflater = LayoutInflater.from(context);
 20         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
 21 
 22         View view = mInflater.inflate(R.layout.delete_btn, null);
 23         mDelBtn = (Button) view.findViewById(R.id.id_item_btn);
 24         mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT,
 25                 LinearLayout.LayoutParams.WRAP_CONTENT);
 26         // 如果需要通过点击PopupWindow之外的地方使其消失,则需要setFocusable(true).
 27         mPopupWindow.setFocusable(true);
 28         // Android 6.0以前的版本需要setBackgroundDrawable(),
 29         // 才能实现通过点击PopupWindow之外的地方使其消失的功能。
 30         mPopupWindow.setBackgroundDrawable(new ColorDrawable(0));
 31         // 先调用下measure,否则拿不到宽和高
 32         mPopupWindow.getContentView().measure(0, 0);
 33         mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();
 34         mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();
 35     }
 36 
 37     @Override
 38     public boolean onTouchEvent(MotionEvent ev) {
 39         int action = ev.getAction();
 40         int x = (int) ev.getX();
 41         int y = (int) ev.getY();
 42 
 43         switch (action){
 44             case MotionEvent.ACTION_DOWN:
 45                 isSliding = false;
 46                 mXDown = x;
 47                 mYDown = y;
 48                 mCurrentPosition = pointToPosition(mXDown, mYDown);
 49                 View view = getChildAt(mCurrentPosition - getFirstVisiblePosition());
 50                 mCurrentView = view;
 51                 break;
 52             case MotionEvent.ACTION_MOVE:
 53                 int dx = x - mXDown;
 54                 int dy = y - mYDown;
 55 
 56                 Log.d(TAG, "mTouchSlop = " + mTouchSlop + ", dx = " + dx + ", dy = " + dy);
 57 
 58                 if(mXDown > x && Math.abs(dx) > mTouchSlop && Math.abs(dy) < mTouchSlop){
 59                     Log.d(TAG, "isSliding");
 60                     isSliding = true;
 61                     int[] location = new int[2];
 62                     mCurrentView.getLocationOnScreen(location);
 63                     mPopupWindow.setAnimationStyle(R.style.popwindow_delete_btn_anim_style);
 64                     mPopupWindow.update();
 65                     Log.d(TAG, "Height: " + mCurrentView.getHeight() + "," + mPopupWindow.getHeight());
 66                     mPopupWindow.showAtLocation(mCurrentView, Gravity.NO_GRAVITY,
 67                             location[0] + mCurrentView.getWidth(),
 68                             location[1] + mCurrentView.getHeight() / 2 - mPopupWindowHeight / 2);
 69                     mDelBtn.setOnClickListener(new OnClickListener() {
 70                         @Override
 71                         public void onClick(View v) {
 72                             mListener.clickHappend(mCurrentPosition);
 73                             mPopupWindow.dismiss();
 74                         }
 75                     });
 76                 }
 77             case MotionEvent.ACTION_UP:
 78                 // isSliding 如果这里恢复为false,则后面会执行super.onTouchEvent事件,
 79                 // 而AbsListView的onTouchEvent调用了onTouchUp方法,在onTouchUp方法中有可能执行
 80                 // performClick.run() --> performItemClick() --> super.performItemClick
 81                 // --> mOnItemClickListener.onItemClick,这样最终触发Item的点击。
 82                 // 因此此处依旧保持isSliding为true的状态,而在ACTION_DOWN事件中恢复isSliding为false,
 83                 // 毕竟每个事件都以ACTION_DOWN开始。
 84                 //isSliding = false;
 85         }
 86 
 87         if(isSliding){
 88             return true;
 89         }
 90 
 91         return super.onTouchEvent(ev);
 92     }
 93 
 94     public void setDelButtonClickListener(DelButtonClickListener listener){
 95         mListener = listener;
 96     }
 97 
 98     interface DelButtonClickListener{
 99         public void clickHappend(int position);
100     }
101 }
102 
103 MyListView.java

通过这个例子学习到:

1、ListView的Item点击事件的触发过程:

自定义ListView的onTouchEvent()  ---调用super.onTouchEvent()---> AbsListView.onTouchEvent() ---MotionEvent.ACTION_UP---> AbsListView.onTouchUp()

---(有可能)调用performClick.run()---> AbsListView.PerformClick.run() ---调用performItemClick()--->AbsListView.performItemClick()

---(有可能)调用super.performItemClick()---> AdapterView.performItemClick() ---mOnItemClickListener.onItemClick---> OnItemClickListener.onItemClick()

也就是Item的点击事件是在MotionEvent.ACTION_UP事件完成的,这样在自定义ListView的onTouchEvent()中,对MotionEvent.ACTION_UP直接return true消费掉事件,而不要调用super.onTouchEvent。这样就避免了删除按钮与Item点击事件的冲突。

2、PopupWindow--通过点击PopupWindow之外的地方使其消失

a、需要调用setFocusable()方法(PopupWindow中showAtLocation() --> createPopupLayoutParams() -->computeFlags() --> 设置FLAG_NOT_FOCUSABLE);

b、Android 6.0以前的版本需要setBackgroundDrawable()(具体原因见:PopupWindow的使用)。


原文:http://blog.csdn.net/lmj623565791/article/details/22961279

   http://www.cnblogs.com/yarightok/p/5666127.html

原文地址:https://www.cnblogs.com/Sharley/p/5666696.html