自定义View之仿QQ消息滑动删除

本文介绍了一种仿照QQ消息界面实现的滑动删除功能。通过自定义ViewGroup并利用ViewDragHelper来处理触摸事件,实现了内容视图与删除视图之间的交互。文章详细解释了实现步骤及关键代码。

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

效果

仿QQ的滑动删除,效果如下:
这里写图片描述

实现思路

1.通过include标签引入内容和删除部分
2.需要继承ViewGroup
3.要控制子View的摆放,实现onLayout,需要测量,实现onMeasure
4.在onMeasure需要测量孩子的大小,设置自己的真实大小
5.在onLaout里面初始化子View的摆放
6.接下来就是控制触摸事件了(down,move,up)
7.处理拖地可以在onTouchEvent里面处理,也可以使用工具类ViewDragHelper

代码

public class SweepView extends ViewGroup {

    private View mContentView;
    private View mDeleteView;
    private ViewDragHelper mViewDragHelper;

    private int mWidth;
    private int mHeight;

    private int mDeleteWidth;
    private int mDeleteHeight;

    public SweepView(Context context) {
        this(context, null);
    }

    public SweepView(Context context, AttributeSet attrs) {

        super(context, attrs);

        // 两个参数 ViewGroup,callBack
        mViewDragHelper = ViewDragHelper.create(this, new SweepViewCallback());
    }

    // xml布局加载完成,所有的孩子已经加载完成
    @Override
    protected void onFinishInflate() {

        mContentView = getChildAt(0);
        mDeleteView = getChildAt(1);

        LayoutParams layoutParams = mContentView.getLayoutParams();
        // 拿到layoutParams
        mWidth = layoutParams.width;
        mHeight = layoutParams.height;

        Log.d("ch", "mWidth:" + mWidth);
        Log.d("ch", "mHeight:" + mHeight);

        LayoutParams layoutParams1 = mDeleteView.getLayoutParams();
        mDeleteWidth = layoutParams1.width;
        mDeleteHeight = layoutParams1.height;
        super.onFinishInflate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        // 测量孩子的大小
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        Log.d("ch", "width =====" + width);
        Log.d("ch", "height =====" + height);
        // 期望孩子的大小
        mContentView.measure(widthMeasureSpec, heightMeasureSpec);
        mDeleteView.measure(MeasureSpec.makeMeasureSpec(mDeleteWidth, MeasureSpec.EXACTLY), heightMeasureSpec);

        //设置自己的大小
        setMeasuredDimension(width, height);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {

        // 内容部分
        int left = 0;
        int top = 0;
        int right = mContentView.getMeasuredWidth();
        int bottom = mContentView.getMeasuredHeight();

        Log.d("ch ", "mContentView:  " + mContentView.getMeasuredWidth());
        mContentView.layout(left, top, right, bottom);
        // 删除部分
        int left1 = mContentView.getMeasuredWidth();
        int top1 = 0;
        int right1 = mContentView.getMeasuredWidth() + mDeleteView.getMeasuredWidth();
        int bottom1 = mDeleteView.getMeasuredHeight();

        Log.d("ch1", "---left1 " + left1 + "  ---right" + right1);
        mDeleteView.layout(left1, top1, right1, bottom1);
    }

    // 处理控件的拖动事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // 将onTouchEvent的up move down事件交给ViewDragHelper分析
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    private class SweepViewCallback extends ViewDragHelper.Callback {

        // 分析触摸事件
        @Override
        public boolean tryCaptureView(View child, int pointerId) {

            // child为分析的子View,分析的id标记
            return child == mContentView || child == mDeleteView;
        }


        // 分析view的水平拖地
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {

            // child为触摸的child,left为child的左上角到到直系父容器的左上角的距离

            Log.d("ch111","left"+left+mDeleteView.getMeasuredWidth());
            //处理边界的问题
            if (child == mContentView) {

                if (left < -mDeleteView.getMeasuredWidth()) {

                    left = -mDeleteView.getMeasuredWidth();
                }else if (left > 0) {

                    left = 0;
                }

            } else if (child == mDeleteView) {

                int temp = mContentView.getMeasuredWidth() - mDeleteView.getMeasuredWidth();
                if (left < temp) {

                    left = temp;
                } else if (left > mContentView.getMeasuredWidth()) {

                    left = mContentView.getMeasuredWidth();
                }
            }
            return left;
        }

        // touchEvent的move事件
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {

//            ViewCompat.postInvalidateOnAnimation(SweepView.this);
            invalidate();
            if (changedView == mContentView) {

                int l = mContentView.getMeasuredWidth() + left;
                int r = mContentView.getMeasuredWidth() + left + mDeleteView.getMeasuredWidth();
                int b = mContentView.getMeasuredHeight();

                Log.d("ch---", "left  " + left);
                Log.d("ch ", "l" + l + " r" + r + " b" + b);
                mDeleteView.layout(l, 0, r, b);

// 平缓滑动的效果               mViewDragHelper.smoothSlideViewTo(mDeleteView,l,0);
            }else if (changedView == mDeleteView) {

                int mleft = left - mContentView.getMeasuredWidth();
                int mtop = 0;
                int mright = left;
                int mbottom = mContentView.getMeasuredHeight();
                mContentView.layout(mleft,mtop,mright,mbottom);

//                mViewDragHelper.smoothSlideViewTo(mContentView,mleft,0);
            }

            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }

        public void onViewReleased(View releasedChild, float xvel, float yvel) {
        }
    }

    @Override
    public void computeScroll() {

        if (mViewDragHelper.continueSettling(true)) {

            invalidate();
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }
}

总结

这个效果值做拖地,对于多条消息的滑动打开关闭,删除等效果没有做,实现起来也不难,通过接口回调,即可实现.

在调试的时候如果看不到效果,仔细分析原因,通过打印日志可以很快的找到原因.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值