热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

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


android:layout_
android:layout_
android:background="@drawable/common_list_item_bg"
android:gravity="center_vertical"
android:min
android:orientation="horizontal">

android:layout_
android:layout_>

android:id="@+id/iv_icon_read_status"
android:layout_
android:layout_
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:src="@drawable/ic_msg_unread" />

android:id="@+id/tv_msg_content"
android:layout_
android:layout_
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"
android:layout_toRightOf="@id/iv_icon_read_status"
android:text="订单预定成功;订单号6607967"
android:textColor="@color/common_text"
android:textSize="14sp" />


android:id="@+id/tv_msg_time"
android:layout_
android:layout_
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="15dp"
android:text="前天 13:21"
android:textColor="@color/text_smoke"
android:textSize="12sp" />


android:id="@+id/tv_btn_delete"
android:layout_
android:layout_
android:background="@color/red"
android:clickable="true"
android:gravity="center"
android:text="删除"
android:textColor="@color/white"
android:textSize="17sp" />
前面的RelativeLayout里的内容就是大家常见的ListView的Item视图,后面的TextView就是我们主角删除按钮,这里把它也作为Item的子布局内容了。从布局里可以看出,删除按钮TextView已经被RelativeLayout挤到最右边,而不在屏幕显示区域内。此时该Item的长度实际长度是屏幕的长度+删除的按钮的长度(这里是70dp)。


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

/**
* 构造方法,实例化入口,初始化相关数据或实例
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public SlideDeleteListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 窗口管理器
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// 新建显示度量尺
DisplayMetrics metrics = new DisplayMetrics();
// 对度量尺进行包装,附参
wm.getDefaultDisplay().getMetrics(metrics);
// 初始化屏幕宽度参数
mSreeenWidth = metrics.widthPixels;
}

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


/**
* 手势操作
*
* @param ev
* @return
*/
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:// 按压
onActionDowm(ev);
break;
case MotionEvent.ACTION_MOVE:// 移动
return onActionMove(ev);
case MotionEvent.ACTION_UP:// 释放
onActionUp(ev);
break;
}
return super.onTouchEvent(ev);
}
重写SlideDeleteListView手势事件


/**
* 手指按下逻辑
*/
private void onActionDowm(MotionEvent e) {
if (isBtnDelShow) {
resetItemView();
}
mDownX = (int) e.getX();
mDownY = (int) e.getY();
// 获得被按下位置的item
Integer currentPosition = pointToPosition(mDownX, mDownY);
if (-1 == currentPosition) {
return;
}
itemViewGroup = (ViewGroup) getChildAt(currentPosition - getFirstVisiblePosition());
// 获得删除按钮的宽度,删除按钮属于第二个子View(上述布局中能看得出来),position为1
mBtnDelWidth = itemViewGroup.getChildAt(1).getLayoutParams().width;
/* 将第一个子View也就是我们常见的Item显示的View的宽固定为屏幕同宽度 */
params = (LinearLayout.LayoutParams) itemViewGroup.getChildAt(0).getLayoutParams();
params.width = mSreeenWidth;
itemViewGroup.getChildAt(0).setLayoutParams(params);
}

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


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



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


    /**
     * 重写该方法是用来应对ScrollView嵌套显示不全的问题
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 获得ScrollView或其子类对象,这里视情况而定,可能不需要只需要一个getParent()或多次,视自己的布局层次而定
        Object object = getParent().getParent();
        if (object instanceof ScrollView) {// 是ScrollView或其子类
            /*解决与ScrollView的布局冲突,让ListView完全显示*/
            int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
        } else {
            // 没有ScrollView嵌套,正常super的方法
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

/**
* 重置itemView,恢复原显示状态
*/
public void resetItemView() {
params.leftMargin = 0;
itemViewGroup.getChildAt(0).setLayoutParams(params);
isBtnDelShow = false;
}
看注释。



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

holder.tv_btn_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
items.remove(items.get(position));
lv_messages.resetItemView();
notifyDataSetChanged();
}
});


效果图:          

OK,实现方式的核心代码已贴上,如果还有什么不懂的地方或有更好的建议欢迎留言。Demo源码下载



推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 本文介绍了使用Python解析C语言结构体的方法,包括定义基本类型和结构体类型的字典,并提供了一个示例代码,展示了如何解析C语言结构体。 ... [详细]
  • WPF开发心率检测大数据曲线图的高性能实现方法
    本文介绍了在WPF开发中实现心率检测大数据曲线图的高性能方法。作者尝试过使用Canvas和第三方开源库,但性能和功能都不理想。最终作者选择使用DrawingVisual对象,并结合局部显示的方式实现了自己想要的效果。文章详细介绍了实现思路和具体代码,对于不熟悉DrawingVisual的读者可以去微软官网了解更多细节。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
author-avatar
PrinceVince_820
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有