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

版权说明 : ListView侧滑删除的实现,SlideDeleteListView,针对ScrollView嵌套ListView视图和手势冲突优化于当前CSDN博客乘月网属同一原创,转载请说明出处,谢谢。


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

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="62dp"
    android:background="@drawable/common_list_item_bg"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:orientation="horizontal">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/iv_icon_read_status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dp"
            android:src="@drawable/ic_msg_unread" />

        <TextView
            android:id="@+id/tv_msg_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            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" />


        <TextView
            android:id="@+id/tv_msg_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="15dp"
            android:text="前天 13:21"
            android:textColor="@color/text_smoke"
            android:textSize="12sp" />
    </RelativeLayout>

    <TextView
        android:id="@+id/tv_btn_delete"
        android:layout_width="70dp"
        android:layout_height="match_parent"
        android:background="@color/red"
        android:clickable="true"
        android:gravity="center"
        android:text="删除"
        android:textColor="@color/white"
        android:textSize="17sp" />
</LinearLayout>

前面的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 < mDownX) {
				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源码下载


  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值