ListView侧滑删除的实现,SlideDeleteListView,针对ScrollView嵌套ListView视图和手势冲突优化

来源:互联网 发布:java界面设计 编辑:程序博客网 时间:2024/03/28 21:20

转载:http://blog.csdn.net/ausboyue/article/details/52565237的博客

关于ListView侧滑删除这是个老话题,大多数APP都具有这样类似的功能,对于一位Android初涉者来说,实现这样的功能确实有一点难度,网上的实现方法也层出不穷,我仔细在网上翻了一下,居然看到了还有很多实现侧滑的第三方依赖包,觉得有些无语,尝试使用一番,大多数实现还是很好的,比我今天要说的好的多,当然也有劣质的包,这里也就不多说了。既然是老话题,那么没有一点实现上的优势,我也说不下去,这个优势大概就是只要自定义一个ListView便可以实现侧滑删除功能,尤其是对ScrollView嵌套ListView视图和手势冲突的优化。严格意义上这个实现方式也是我以前在网上看到的,后来基于这个实现思想修改优化的。好了,开教程:

1.先假设一个ListView的Item子布局message_item.xml是这样的:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="62dp"  
  5.     android:background="@drawable/common_list_item_bg"  
  6.     android:gravity="center_vertical"  
  7.     android:minHeight="?android:attr/listPreferredItemHeight"  
  8.     android:orientation="horizontal">  
  9.   
  10.     <RelativeLayout  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="match_parent">  
  13.   
  14.         <ImageView  
  15.             android:id="@+id/iv_icon_read_status"  
  16.             android:layout_width="wrap_content"  
  17.             android:layout_height="wrap_content"  
  18.             android:layout_centerVertical="true"  
  19.             android:layout_marginLeft="15dp"  
  20.             android:src="@drawable/ic_msg_unread" />  
  21.   
  22.         <TextView  
  23.             android:id="@+id/tv_msg_content"  
  24.             android:layout_width="wrap_content"  
  25.             android:layout_height="wrap_content"  
  26.             android:layout_centerVertical="true"  
  27.             android:layout_marginLeft="15dp"  
  28.             android:layout_toRightOf="@id/iv_icon_read_status"  
  29.             android:text="订单预定成功;订单号6607967"  
  30.             android:textColor="@color/common_text"  
  31.             android:textSize="14sp" />  
  32.   
  33.   
  34.         <TextView  
  35.             android:id="@+id/tv_msg_time"  
  36.             android:layout_width="wrap_content"  
  37.             android:layout_height="wrap_content"  
  38.             android:layout_alignParentRight="true"  
  39.             android:layout_centerVertical="true"  
  40.             android:layout_marginRight="15dp"  
  41.             android:text="前天 13:21"  
  42.             android:textColor="@color/text_smoke"  
  43.             android:textSize="12sp" />  
  44.     </RelativeLayout>  
  45.   
  46.     <TextView  
  47.         android:id="@+id/tv_btn_delete"  
  48.         android:layout_width="70dp"  
  49.         android:layout_height="match_parent"  
  50.         android:background="@color/red"  
  51.         android:clickable="true"  
  52.         android:gravity="center"  
  53.         android:text="删除"  
  54.         android:textColor="@color/white"  
  55.         android:textSize="17sp" />  
  56. </LinearLayout>  
前面的RelativeLayout里的内容就是大家常见的ListView的Item视图,后面的TextView就是我们主角删除按钮,这里把它也作为Item的子布局内容了。从布局里可以看出,删除按钮TextView已经被RelativeLayout挤到最右边,而不在屏幕显示区域内。此时该Item的长度实际长度是屏幕的长度+删除的按钮的长度(这里是70dp)。


2.下面我们自定义ListView----->SlideDeleteListView  这里注意一点,就是尽量不要在任何自定义View中传入某布局,那么以后修改或用于别的项目,其要求发生了一些变化,还要针对被改变的布局修改逻辑代码,这是我个人的一种开发思想,大家听听就好了。

[java] view plain copy
  1. /** 
  2.  * 构造方法,实例化入口,初始化相关数据或实例 
  3.  * 
  4.  * @param context 
  5.  * @param attrs 
  6.  * @param defStyleAttr 
  7.  */  
  8. public SlideDeleteListView(Context context, AttributeSet attrs, int defStyleAttr) {  
  9.     super(context, attrs, defStyleAttr);  
  10.     // 窗口管理器  
  11.     WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
  12.     // 新建显示度量尺  
  13.     DisplayMetrics metrics = new DisplayMetrics();  
  14.     // 对度量尺进行包装,附参  
  15.     wm.getDefaultDisplay().getMetrics(metrics);  
  16.     // 初始化屏幕宽度参数  
  17.     mSreeenWidth = metrics.widthPixels;  
  18. }  

SlideDeleteListView构造方法中获取屏幕宽度mSreeenWidth


[java] view plain copy
  1. /** 
  2.  * 手势操作 
  3.  *  
  4.  * @param ev 
  5.  * @return 
  6.  */  
  7. @SuppressLint("ClickableViewAccessibility")  
  8. @Override  
  9. public boolean onTouchEvent(MotionEvent ev) {  
  10.     switch (ev.getAction()) {  
  11.     case MotionEvent.ACTION_DOWN:// 按压  
  12.         onActionDowm(ev);  
  13.         break;  
  14.     case MotionEvent.ACTION_MOVE:// 移动  
  15.         return onActionMove(ev);  
  16.     case MotionEvent.ACTION_UP:// 释放  
  17.         onActionUp(ev);  
  18.         break;  
  19.     }  
  20.     return super.onTouchEvent(ev);  
  21. }  
重写SlideDeleteListView手势事件


[java] view plain copy
  1. /** 
  2.      * 手指按下逻辑 
  3.      */  
  4.     private void onActionDowm(MotionEvent e) {  
  5.         if (isBtnDelShow) {  
  6.             resetItemView();  
  7.         }  
  8.         mDownX = (int) e.getX();  
  9.         mDownY = (int) e.getY();  
  10.         // 获得被按下位置的item  
  11.         Integer currentPosition = pointToPosition(mDownX, mDownY);  
  12.         if (-1 == currentPosition) {  
  13.             return;  
  14.         }  
  15.         itemViewGroup = (ViewGroup) getChildAt(currentPosition - getFirstVisiblePosition());  
  16.         // 获得删除按钮的宽度,删除按钮属于第二个子View(上述布局中能看得出来),position为1  
  17.         mBtnDelWidth = itemViewGroup.getChildAt(1).getLayoutParams().width;  
  18.         /* 将第一个子View也就是我们常见的Item显示的View的宽固定为屏幕同宽度 */  
  19.         params = (LinearLayout.LayoutParams) itemViewGroup.getChildAt(0).getLayoutParams();  
  20.         params.width = mSreeenWidth;  
  21.         itemViewGroup.getChildAt(0).setLayoutParams(params);  
  22.     }  

手指按下的时候,把刚才那个item常见显示的视图RelativeLayout宽度成屏幕的宽度,以及获得删除按钮TextView 的宽度,isBtnDelShow为flag,用于标记删除是否处于显示状态,若显示,点击时重置下Item显示状态(即不显示删除按钮的视图状态),Integer currentPosition = pointToPosition(mDownX, mDownY),currentPosition 为-1时表示手指点击点是在item之间的分割线上,不作逻辑处理。itemViewGroup即Item的布局,itemViewGroup.getChildAt(0)为Item子View,即上述的RelativeLayout。


[java] view plain copy
  1. /** 
  2.  * 手指移动逻辑 
  3.  */  
  4. private boolean onActionMove(MotionEvent e) {  
  5.     int nowX = (int) e.getX();  
  6.     int nowY = (int) e.getY();  
  7.     // 判断是否为偏向左右的滑动  
  8.     if (Math.abs(nowX - mDownX) > Math.abs(nowY - mDownY)) {  
  9.         // 左右滑动请求消费该事件,防止上下滑动以及被ScrollView嵌套的手势冲突  
  10.         requestDisallowInterceptTouchEvent(true);  
  11.         // 判断是否为向左滑动  
  12.         if (nowX < mDownX) {  
  13.             int srollX = mDownX - nowX;  
  14.             // 判断左滑距离是否超过删除按钮宽  
  15.             if (srollX >= mBtnDelWidth) {  
  16.                 srollX = mBtnDelWidth;  
  17.             }  
  18.             params.leftMargin = -srollX;  
  19.             itemViewGroup.getChildAt(0).setLayoutParams(params);  
  20.         }  
  21.         // 消费掉该移动事件  
  22.         return true;  
  23.     }  
  24.     return super.onTouchEvent(e);  
  25. }  
这里的思路就是判定左滑时,并根据左滑的绝对距离(即手指向左边滑动的实际水平距离),实时设定RelativeLayout视图的MarginLeft为相应距离的负值以达到感觉item布局像是被手指划走的效果,删除按钮也随即从左边逐渐显示出来。注意下requestDisallowInterceptTouchEvent(true)这行代码的注释,手指点击的位置是在ListView上,且是左右滑,为了避免手势冲突,不让父View即ScrollView拦截该手势事件。



手指释放时判断向左滑动的距离,做显示按钮或重置最初的Item显示状态逻辑。
[java] view plain copy
  1. /** 
  2.  * 手指释放逻辑 
  3.  */  
  4. private void onActionUp(MotionEvent e) {  
  5.     //判断手指释放后,删除按钮是否已显示超过其宽度的一半  
  6.     if (-params.leftMargin >= mBtnDelWidth / 2) {  
  7.         params.leftMargin = -mBtnDelWidth;  
  8.         isBtnDelShow = true;  
  9.     } else {  
  10.         //恢复滑动前的视图状态  
  11.         resetItemView();  
  12.     }  
  13.     itemViewGroup.getChildAt(0).setLayoutParams(params);  
  14. }  


[java] view plain copy
  1.     /** 
  2.      * 重写该方法是用来应对ScrollView嵌套显示不全的问题 
  3.      * 
  4.      * @param widthMeasureSpec 
  5.      * @param heightMeasureSpec 
  6.      */  
  7.     @Override  
  8.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  9.         // 获得ScrollView或其子类对象,这里视情况而定,可能不需要只需要一个getParent()或多次,视自己的布局层次而定  
  10.         Object object = getParent().getParent();  
  11.         if (object instanceof ScrollView) {// 是ScrollView或其子类  
  12.             /*解决与ScrollView的布局冲突,让ListView完全显示*/  
  13.             int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);  
  14.             super.onMeasure(widthMeasureSpec, expandSpec);  
  15.         } else {  
  16.             // 没有ScrollView嵌套,正常super的方法  
  17.             super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  18.         }  
  19.     }  

[java] view plain copy
  1. /** 
  2.  * 重置itemView,恢复原显示状态 
  3.  */  
  4. public void resetItemView() {  
  5.     params.leftMargin = 0;  
  6.     itemViewGroup.getChildAt(0).setLayoutParams(params);  
  7.     isBtnDelShow = false;  
  8. }  
看注释。



3.在适配器Adapter中获取该ListView对象,当删除按钮显示时,点击删除,删除集合里对应的数据,ListView对象再调用上述的resetItemView()方法,再调用adapter的notifyDataSetChanged()方法更新界面。

[java] view plain copy
  1. holder.tv_btn_delete.setOnClickListener(new View.OnClickListener() {  
  2.             @Override  
  3.             public void onClick(View view) {  
  4.                 items.remove(items.get(position));  
  5.                 lv_messages.resetItemView();  
  6.                 notifyDataSetChanged();  
  7.             }  
  8.         });  


效果图:          

Demo源码下载


0 0