zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Bootstrap-CSS:按钮
    Bootstrap-CSS:表单
    质检总局-版权局
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯地铁换乘(C++)
    java实现第二届蓝桥杯最小公倍数(c++)
    java实现第二届蓝桥杯最小公倍数(c++)
  • 原文地址:https://www.cnblogs.com/Sharley/p/5666696.html
Copyright © 2011-2022 走看看