Listview之侧滑删除

本文详细介绍如何在Android中自定义ListView实现侧滑删除功能,包括获取屏幕宽度、重写触摸事件、处理手指按下、移动及离开等操作,以及解决事件冲突和滑动无效的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果图:
在这里插入图片描述
自定义ListView

需要自定义ListView,继承于ListView,如:

 public class SideslipListView extends ListView {
...
public SideslipListView(Context context) {
    super(context);
}
public SideslipListView(Context context, AttributeSet attrs) {
    super(context, attrs);
}
public SideslipListView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

}

首先获取屏幕宽度。

	private int mScreenWidth;//屏幕的宽度
	...
	// 获取屏幕宽度
	WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
	DisplayMetrics dm = new DisplayMetrics();
	wm.getDefaultDisplay().getMetrics(dm);
	mScreenWidth = dm.widthPixels;

接着重写onTouchEvent(MotionEvent ev)方法,对手指按下、移动、离开分别进行相应处理。如

	@Override
	public boolean onTouchEvent(MotionEvent ev) {//事件响应
	    switch (ev.getAction()) {
	        case MotionEvent.ACTION_DOWN:
	            performActionDown(ev);
	            break;
	        case MotionEvent.ACTION_MOVE:
	            performActionMove(ev);
	            break;
	        case MotionEvent.ACTION_UP:
	            performActionUp(ev);
	            break;
	    }
	    return super.onTouchEvent(ev);
	}

当手指按下时我们需要获取到按下时所在的item,得到正常显示时的视图,并设置其宽度为屏幕的宽度;另外还需得到删除组件的宽度。如:

	private int mDownX;//手指初次按下的X坐标
	private int mDownY;//手指初次按下的Y坐标
	private boolean isDeleteShow;//删除组件是否显示
	private ViewGroup mPointChild;//手指按下位置的item组件
	private int mDeleteWidth;//删除组件的宽度
	private LinearLayout.LayoutParams mItemLayoutParams;//手指按下时所在的item的布局参数

private void performActionDown(MotionEvent ev) {
    mDownX = (int) ev.getX();
    mDownY = (int) ev.getY();
    if (isDeleteShow) {
        ViewGroup tmpViewGroup = (ViewGroup) getChildAt(pointToPosition(mDownX, mDownY) - getFirstVisiblePosition());
        Log.i(TAG, "*********mPointChild.equals(tmpViewGroup): " + mPointChild.equals(tmpViewGroup));
        if (!mPointChild.equals(tmpViewGroup)) {
            turnNormal();
        }
    }
    //获取当前的item
    mPointChild = (ViewGroup) getChildAt(pointToPosition(mDownX, mDownY) - getFirstVisiblePosition());

mDeleteWidth = mPointChild.getChildAt(1).getLayoutParams().width;//获取删除组件的宽度
Log.i(TAG, "**********pointToPosition(x,y): " + pointToPosition(mDownX, mDownY)
        + ", getFirstVisiblePosition() = " + getFirstVisiblePosition()
        + ", mDeleteWidth = " + mDeleteWidth);
mItemLayoutParams = (LinearLayout.LayoutParams) mPointChild.getChildAt(0).getLayoutParams();

mItemLayoutParams.width = mScreenWidth;
mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams);

}
当手指移动时动态修改正常是显示视图组件的leftMargin,如果删除按钮没有显示,手指向左滑动删除按钮就会根据滑动的距离显示相应的可见宽度。如:

	private boolean performActionMove(MotionEvent ev) {
	    int nowX = (int) ev.getX();
	    int nowY = (int) ev.getY();
	    int diffX = nowX - mDownX;
	    if (Math.abs(diffX) > Math.abs(nowY - mDownY) && Math.abs(nowY - mDownY) < 20) {
	        if (!isDeleteShow && nowX < mDownX) {//删除按钮未显示时向左滑
	            if (-diffX >= mDeleteWidth) {//如果滑动距离大于删除组件的宽度时进行偏移的最大处理
	                diffX = -mDeleteWidth;
	            }
	            mItemLayoutParams.leftMargin = diffX;
	            mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams);
	            isAllowItemClick = false;
	        } else if (isDeleteShow && nowX > mDownX) {//删除按钮显示时向右滑
	            if (diffX >= mDeleteWidth) {
	                diffX = mDeleteWidth;
	            }
	            mItemLayoutParams.leftMargin = diffX - mDeleteWidth;
	            mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams);
	            isAllowItemClick = false;
	        }
	        return true;
	    }
	    return super.onTouchEvent(ev);
	}
	当手指离开时,根据移动的距离判断是否显示删除按钮。如:
	private void performActionUp(MotionEvent ev) {
	    //如果向左滑出超过隐藏的二分之一就全部显示
	    if (-mItemLayoutParams.leftMargin >= mDeleteWidth / 2) {
	        mItemLayoutParams.leftMargin = -mDeleteWidth;
	        isDeleteShow = true;
	        mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams);
	    } else {
	        turnNormal();
	    }
	}

到这里基本上就实现了侧滑时删除按钮的显示和隐藏了。不过还有些问题。
ListView设置了点击事件时左右滑动会触发改事件冲突处理
当ListView中的item设置了点击事件时,我们左右滑动时就会触发这个点击事件,那么如何避免呢?在自定义的ListView中设置标志isAllowItemClick,当手指按下时设置其值为isAllowItemClick = true,在手指移动的过程中,若是发生左右移动,则设置其值为isAllowItemClick = false,另外向外公布方法:
public boolean isAllowItemClick() {
return isAllowItemClick;
}

当为Listview设置点击事件时这样做就可以避免了。

	mSideslipListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
	    @Override
	    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
	        if (mSideslipListView.isAllowItemClick()) {
	            Log.i(TAG, mDataList.get(position) + "被点击了");
	            Toast.makeText(MainActivity.this, mDataList.get(position) + "被点击了",
	                    Toast.LENGTH_SHORT).show();
	        }
	    }
	});

当item中有按钮时手指按下在改位置时滑动无效处理
这个跟Android的事件分发机制有关系,不知道可以去仔细看看查查资料。我们可以重写onInterceptTouchEvent(MotionEvent ev)这个方法来解决,若手指按下并且是左右移动时则最该Touch事件进行拦截。如:

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {//事件拦截
	    switch (ev.getAction()) {
	        case MotionEvent.ACTION_DOWN: {
	            isAllowItemClick = true;

        //侧滑删除
        mDownX = (int) ev.getX();
        mDownY = (int) ev.getY();
        mPointPosition = pointToPosition(mDownX, mDownY);
        Log.i(TAG, "*******pointToPosition(mDownX, mDownY): " + mPointPosition);
        if (mPointPosition != -1) {
            if (isDeleteShow) {
                ViewGroup tmpViewGroup = (ViewGroup) getChildAt(mPointPosition - getFirstVisiblePosition());
                if (!mPointChild.equals(tmpViewGroup)) {
                    turnNormal();
                }
            }
            //获取当前的item
            mPointChild = (ViewGroup) getChildAt(mPointPosition - getFirstVisiblePosition());

            mDeleteWidth = mPointChild.getChildAt(1).getLayoutParams().width;
            mItemLayoutParams = (LinearLayout.LayoutParams) mPointChild.getChildAt(0).getLayoutParams();

            Log.i(TAG, "*********mItemLayoutParams.height: " + mItemLayoutParams.height +
                    ", mDeleteWidth: " + mDeleteWidth);
            mItemLayoutParams.width = mScreenWidth;
            mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams);
        }
        break;
    }
    case MotionEvent.ACTION_MOVE: {
        int nowX = (int) ev.getX();
        int nowY = (int) ev.getY();
        int diffX = nowX - mDownX;
        Log.i(TAG, "******dp2px(4): " + dp2px(8) + ", dp2px(8): " + dp2px(8) +
                ", density: " + getContext().getResources().getDisplayMetrics().density);
        if (Math.abs(diffX) > dp2px(4) || Math.abs(nowY - mDownY) > dp2px(4)) {
            return true;//避免子布局中有点击的控件时滑动无效
        }
        break;
    }
}
return super.onInterceptTouchEvent(ev);
}

public float dp2px(int dp) {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
            getContext().getResources().getDisplayMetrics());
}

github地址:https://github.com/yuzhushi/ListViewTest

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值