Android条目拖拽删除

项目中需求,要做条目条目拖拽删除效果,实际效果和QQ消息删除一样,侧滑有制定和删除。

效果图


第一步效果图


1.0自定义控件 SwipeLayout 继承 FrameLayout重写里面三个构造方法,分别调用initView().
2.0在布局中使用自定义控件
3.0在initView()方法中,创建拖拽辅辅助工具  ViewDragHelper()
 该方法需要传入回调 MyCallBack()
4.0,创建MyCallBack()回调,继承 ViewDragHelper. Callback
  在回调中 覆盖 tryCaptureView方法,返回true 允许child被拖拽,被 覆盖 clampViewPositionHorizontal 返回left系统提供拖拽位置
5.0  onInterceptTouchEvent 返回:让 ViewDragHelper判断是否需要拦截事件
6.0   onTouchEvent 返回true 并且让 ViewDragHelper分析事件

具体代码:
布局:
<cn.itheima.swipelayout.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <!--正文部分-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fff"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/item_tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="张三"
            android:textSize="20sp" />

    </RelativeLayout>

    <!--按钮部分-->
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#888888"
            android:padding="10dp"
            android:text="呼叫"
            android:textSize="20sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#f00"
            android:padding="10dp"
            android:text="删除"
            android:textSize="20sp" />

    </LinearLayout>

</cn.itheima.swipelayout.SwipeLayout>

SwipeLayout   代码:
public class SwipeLayout extends FrameLayout {

    private ViewDragHelper mDragHelper;

    public SwipeLayout(Context context) {
        super(context);
        initView();
    }

    public SwipeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public SwipeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {

        mDragHelper = ViewDragHelper.create(this,new MyCallBack());
    }
    // 让ViewDragHelper就是拖拽辅助工具 返回true 则表示要拦截触摸事件
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //让拖拽辅助工具判断是否需要拦截 事件
        return mDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //让拖拽辅助工具分析事件 分析用户手势
        mDragHelper.processTouchEvent(event);
        return true;
    }

    private class MyCallBack extends ViewDragHelper.Callback{
        /**
         * 如果返回 true 则表示 child 允许被拖拽
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;
        }
        /**
         * 固定被拖拽控件的水平位置,
         * 参数里的 left 是系统推荐移动到的位置,可以进行修正,
         * 方法返回的值就是 child 将要移动到的位置
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }
    }
}
第二步:
1.0创建onFinishInflate方法获取子控件,并且判断健壮性
/*
     控件初始化时执行,可以用于获取子控件
     */
    @Override
    protected void onFinishInflate() {
        // 健壮性检查
        if (getChildCount()!=2){
            throw new RuntimeException("SwipeLayout 必须存放两个子控件");
        }
        if (!(getChildAt(0) instanceof ViewGroup)||!(getChildAt(1) instanceof ViewGroup)){
            throw new RuntimeException("SwipeLayout 的子控件必须是 ViewGroup");
        }
        mContent = (ViewGroup) getChildAt(0);
        mDeletePanel = (ViewGroup) getChildAt(1);

    }

2.0创建 onSizeChanged方法,在控件大小改变的时候调用,获取控件的宽高,和删除的面板的最大移动范围
/**
     * 当控件大小改变的时候调用这个方法
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int mWith = w;
        int mHeigth = h;
        //界面创建过程中,不能使用 getWidth 方法
        int mRang = mDeletePanel.getMeasuredWidth();
    }
3.0在onLayout中 指定侧拉面板的位置
//指定侧拉面板的位置
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

        super.onLayout(changed, left, top, right, bottom);

        mDeletePanel.layout(mWith,0,mWith+mRang,mHeigth);
    }
4.0在 onViewPositionChanged方法中实现联动效果
 /**
         * 当被拖拽的控件已经移动过后,会调用这个方法,可以用于处理控件间的联动效果
         * @left 被拖拽控件的真实移动位置
         * @dx 被拖拽控件的真实偏移大小
         */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {

            if (changedView==mContent){
                // 移动正文的同时也要移动侧栏
                mDeletePanel.offsetLeftAndRight(dx);
            }else{
                mContent.offsetLeftAndRight(dx);
            }
        }
5.0在  clampViewPositionHorizontal方法中    固定被拖拽控件的水平位置,
/**
         * 固定被拖拽控件的水平位置,
         * 参数里的 left 是系统推荐移动到的位置,可以进行修正,
         * 方法返回的值就是 child 将要移动到的位置
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (child==mContent){
                if (left>0){
                    left=0;
                }else if (left<-mRang){
                      left=-mRang;
                }
            }else{
                if (left>mWith){//mWith是屏幕的宽度
                    left=mWith;
                }else if (left<mWith-mRang){
                    left=mWith-mRang;
                }
            }
            return left;
        }
第三步:
效果图

1.0 onViewReleased 中根据来开局里面,判断是否打开还是关闭
2.0  在   moveContent中第一次滑动
3.0 computeScroll中,继续滑动,直到滑动到指定的位置
4.0注意在 onViewPositionChanged中手动刷新界面,调用invalidate方法
如果不手动刷新界面,效果展示不出来
 /**
         * 当用户松手时执行
         * @xvel 松手时在 X 方向的移动速度,如果为 正数 则说明是向右移动,如果是 负数 则说明是向左移动,如果为零,说明是静止状态
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if (xvel>0){
                //向右移动
                close();
            }else if (xvel<0){
                //向左移动
                opend();
            }else if (xvel>-mRang/2){// 静止状态
                close();// 展开不到一半,关闭面板
            }else{
                opend();
            }
        }
    }
    /**
     * 打开面板
     */
    private void opend() {
        int left=-mRang;
        moveContent(left);
    }
    /**
     * 关闭面板
     */
    private void close() {
        int left=0;
        moveContent(left);
    }

    private void moveContent(int left) {
        // 开启平滑滚动,如果返回 true 则说明要继续刷新界面,保持滚动
        if(mDragHelper.smoothSlideViewTo(mContent,left,0)){
            invalidate();
        }
    }

    @Override
    public void computeScroll() {
        // 继续平滑滚动,如果返回 true 则说明要继续刷新界面,保持滚动
        if (mDragHelper.continueSettling(true)){
            invalidate();
        }
    }
第四步:
1.0现给ListView赋值   在这就省略
2.0在SwipeLayout中使用枚举记录面板的状态
 private enum Status{
    CLOSED,OPENED,DRAGING;
}

    private Status status = Status.CLOSED;

    public Status getStatus() {
        return status;
    }

3.0 //  记录上一个打开的面板。注意:一定要是 静态变量
private static SwipeLayout preSwipeLayout;
4.0在onViewPositionChanged中创建一个方法操作关闭面板
// 关闭上一个打开的面板
closePre();
5.0closePre()在这个方法中,判断当前面板的状态,并且根据状态,关闭上一个打开的面板
// 判断当前面板是否正在打开,如果正在打开则将上一个打开的面板关闭
    private void closePre() {
        //记录旧状态
        Status preStatus=status;
        if (mContent.getLeft()==-mRang){
            //记录当前面板已经打开
            status=status.OPENED;
        }else if (mContent.getLeft()==0){
            //当前面板已经关闭
            status=status.CLOSED;
        }else {
            status=status.DRAGING;
        }
        // 如果当前面板旧状态为关闭,并且新状态为拖拽,那么此时可以关闭之前打开的面板
        if (preStatus==status.CLOSED&&status==status.DRAGING){
            if (preSwipeLayout!=null&&preSwipeLayout!=this){
                // 关闭上一个面板
                preSwipeLayout.close();
            }
            // 将当前面板标记为 打开的面板
            preSwipeLayout=this;
        }
    }

如果项目中还需要集成一键加QQ群功能,请看以下博客
http://blog.csdn.net/wangxiaohuhu1314/article/details/75033072





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值