ListView滑动删除 ,仿腾讯QQ

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

首先是布局文件:

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

<? xml   version = "1.0"   encoding = "utf-8" ?>   
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="wrap_content"  
  3.     android:layout_height="wrap_content"  
  4.     android:orientation="vertical" >  
  5.       <Button   
  6.         android:id="@+id/id_item_btn"  
  7.         android:layout_width="60dp"  
  8.         android:singleLine="true"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="删除"  
  11.         android:background="@drawable/d_delete_btn"  
  12.         android:textColor="#ffffff"  
  13.         android:paddingLeft="15dp"  
  14.         android:paddingRight="15dp"  
  15.         android:layout_alignParentRight="true"  
  16.         android:layout_centerVertical="true"  
  17.         android:layout_marginRight="15dp"  
  18.         />  
  19. </LinearLayout>  
主布局文件:activity_main.xml ,ListView的每个Item的样式直接使用了系统的android.R.layout.simple_list_item_1

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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>  
接下来看看QQListView的实现:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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
    {
    
       private static final String TAG = "QQlistView";
    
       // private static final int VELOCITY_SANP = 200;
       // private VelocityTracker mVelocityTracker;
       /**
        * 用户滑动的最小距离
        */
       private int touchSlop;
    
       /**
        * 是否响应滑动
        */
       private boolean isSliding;
    
       /**
        * 手指按下时的x坐标
        */
       private int xDown;
       /**
        * 手指按下时的y坐标
        */
       private int yDown;
       /**
        * 手指移动时的x坐标
        */
       private int xMove;
       /**
        * 手指移动时的y坐标
        */
       private int yMove;
    
       private LayoutInflater mInflater;
    
       private PopupWindow mPopupWindow;
       private int mPopupWindowHeight;
       private int mPopupWindowWidth;
    
       private Button mDelBtn;
       /**
        * 为删除按钮提供一个回调接口
        */
       private DelButtonClickListener mListener;
    
       /**
        * 当前手指触摸的View
        */
       private View mCurrentView;
    
       /**
        * 当前手指触摸的位置
        */
       private int mCurrentViewPos;
    
       /**
        * 必要的一些初始化
        * 
        * @param context
        * @param attrs
        */
       public QQListView(Context context, AttributeSet attrs)
       {
          super(context, attrs);
    
          mInflater = LayoutInflater.from(context);
          touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    
          View view = mInflater.inflate(R.layout.delete_btn, null);
          mDelBtn = (Button) view.findViewById(R.id.id_item_btn);
          mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
          /**
           * 先调用下measure,否则拿不到宽和高
           */
          mPopupWindow.getContentView().measure(0, 0);
          mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();
          mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();
       }
    
       @Override
       public boolean dispatchTouchEvent(MotionEvent ev)
       {
          int action = ev.getAction();
          int x = (int) ev.getX();
          int y = (int) ev.getY();
          switch (action)
          {
    
          case MotionEvent.ACTION_DOWN:
             xDown = x;
             yDown = y;
             /**
              * 如果当前popupWindow显示,则直接隐藏,然后屏蔽ListViewtouch事件的下传
              */
             if (mPopupWindow.isShowing())
             {
                dismissPopWindow();
                return false;
             }
             // 获得当前手指按下时的item对应的数据在数据源模型中的索引
             mCurrentViewPos = pointToPosition(xDown, yDown);
             // 获得当前手指按下时的item      getFirstVisiblePosition() 返回当前屏幕上第一个可见的item的数据在数据源模型中的索引
             View view = getChildAt(mCurrentViewPos - getFirstVisiblePosition());
             mCurrentView = view;
             break;
          case MotionEvent.ACTION_MOVE:
             xMove = x;
             yMove = y;
             int dx = xMove - xDown;
             int dy = yMove - yDown;
             /**
              * 判断是否是从右到左的滑动
              */
             if (xMove < xDown && Math.abs(dx) > touchSlop && Math.abs(dy) < touchSlop)
             {
                // Log.e(TAG, "touchslop = " + touchSlop + " , dx = " + dx +
                // " , dy = " + dy);
                isSliding = true;
             }
             break;
          }
    
          boolean state = super.dispatchTouchEvent(ev);
    
          Log.i(TAG, "dispatchTouchEvent = " + ev.getAction() + ", return = " +state);
          /**
           *     通过查看源码super.dispatchTouchEvent(ev)直接调用的是ViewGroup#dispatchTouchEvent(ev)
           *     ViewGroup#onFilterTouchEventForSecurity() 过滤触摸事件到应用的安全策略
           *     如果是ACTION_DOWN事件就取消状态并清除触摸响应对象 重置触摸状态
           *     如果是ACTION_DOWN事件或者触控目标链表中第一次触控的目标不为空,调用onInterceptTouchEvent(ev)进行拦截处理
           *     如果没有拦截且没有取消,中途会调用dispatchTransformedTouchEvent()它的返回值会作为QQListView#dispatchTouchEvent()的返回值
           *
           *     dispatchTransformedTouchEvent()中如果已经拦截或者复位取消接下来标志就调用当前View对象的super.dispatchTouchEvent(event)
           *否则就调用当前View对象的dispatchTouchEvent(event) 后面的情况和刚才的处理逻辑类似;View#dispatchTouchEvent(),会先处理onTouch()事件
           *如果处理了onTouch()事件就返回true,那么就不会执行到onTouchEvent(event),所以我们一般设置了OnTouchListener事件监听就把返回值设置为false,
           *这样才能执行到onTouchEvent(event),执行onTouchEvent(event)后如果执行了单击等事件返回true,否则返回false
           *
           */
    
          return state;
       }
    
       @Override
       public boolean onTouchEvent(MotionEvent ev)
       {
          int action = ev.getAction();
          /**
           * 如果是从右到左的滑动才相应
           */
          if (isSliding)
          {
             switch (action)
             {
             case MotionEvent.ACTION_MOVE:
    
                int[] location = new int[2];
                // 获得当前item的位置xy
                mCurrentView.getLocationOnScreen(location);
                // 设置popupWindow的动画
                mPopupWindow.setAnimationStyle(R.style.popwindow_delete_btn_anim_style);
                mPopupWindow.update();
                mPopupWindow.showAtLocation(mCurrentView, Gravity.LEFT | Gravity.TOP,
                      location[0] + mCurrentView.getWidth(), location[1] + mCurrentView.getHeight() / 2
                            - mPopupWindowHeight / 2);
    
                Log.i(TAG, "location[0]=" + location[0] + " ,location[1] = " + location[1] + ", mCurrentView.getWidth() = " + mCurrentView.getWidth() + " , mCurrentView.getHeight() = " + mCurrentView.getHeight() + " , mPopupWindowHeight = " + mPopupWindowHeight);
    
                // 设置删除按钮的回调
                mDelBtn.setOnClickListener(new OnClickListener()
                {
                   @Override
                   public void onClick(View v)
                   {
                      if (mListener != null)
                      {
                         mListener.clickHappend(mCurrentViewPos);
                         mPopupWindow.dismiss();
                      }
                   }
                });
                // Log.e(TAG, "mPopupWindow.getHeight()=" + mPopupWindowHeight);
    
                break;
             case MotionEvent.ACTION_UP:
                isSliding = false;
    
             }
             // 相应滑动期间屏幕itemClick事件,避免发生冲突
             return true;
          }
    
          /**
           * super.onTouchEvent(ev)这里调用的是AbsListView重写的onTouchEvent(ev)是返回true       */
          boolean state = super.onTouchEvent(ev);
          Log.i(TAG, "state = " + state + ", action = " + ev.getAction());
    
          return state;
       }
    
       /**
        * 隐藏popupWindow
        */
       private void dismissPopWindow()
       {
          if (mPopupWindow != null && mPopupWindow.isShowing())
          {
             mPopupWindow.dismiss();
          }
       }
    
       public void setDelButtonClickListener(DelButtonClickListener listener)
       {
          mListener = listener;
       }
    
       interface DelButtonClickListener
       {
          public void clickHappend(int position);
       }
    
    }
    
代码注释写得很详细,简单说一下,在dispatchTouchEvent中设置当前是否响应用户滑动,然后在onTouchEvent中判断是否响应,如果响应则popupWindow以动画的形式展示出来。当然屏幕上如果存在PopupWindow则屏幕ListView的滚动与Item的点击,以及从右到左滑动时屏幕Item的click事件。

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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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. }  
效果图如下:楼主使用asm.jar以及gifcamera截的gif,由于button的动画很短感觉截图效果很卡不流畅,大家有什么好的截图,还望推荐。有兴趣的还是下载源码看看效果i。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值