左滑操作(删除,置顶等。。。)

  • 在滑动列表中,常常会有左滑出现删除,置顶操作的需求,如下qq的左滑效果:
    左滑操作(删除,置顶等。。。)_第1张图片
今天也来实现下类似的效果,可供大家参考:
  • 1.实现原理

    • 原理图
      左滑操作(删除,置顶等。。。)_第2张图片
  • 2.实现自定义左滑View

    • 1.添加View
      首先在自定义View中通用getChildAt来获取左边显示内容的View和右边的操作View,这里通过getChildAt可以更方便的定制按钮的个数,大小。
// 左边显示内容的View
private View leftContentView;
// 右边操作的View
private View rightActionView;

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    if (getChildCount() < 2) {
        throw new IllegalArgumentException("child view less than 2!!");
    }
    leftContentView = getChildAt(0);
    rightActionView = getChildAt(1);
}
  • 2.测量布局
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 测量子View的宽高
    measureChildren(widthMeasureSpec, heightMeasureSpec);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    /*
     *(0,0)                              (左width,0)
     * --------------                    --------------
     * |    左      |                    |      右    |
     * --------------  (左width,左height)-------------- (左width+右width,右height)
     */
    int leftLeft = 0;
    int leftTop = 0;
    int leftRight = leftContentView.getMeasuredWidth();
    int leftBottom = leftContentView.getMeasuredHeight();
    leftContentView.layout(leftLeft, leftTop, leftRight, leftBottom);

    int rightLeft = leftContentView.getMeasuredWidth();
    int rightTop = 0;
    int rightRight = leftContentView.getMeasuredWidth() + rightActionView.getMeasuredWidth();
    int rightBottom = rightActionView.getMeasuredHeight();
    rightActionView.layout(rightLeft, rightTop, rightRight, rightBottom);
}
  • 写上我们需要的xml布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.welcom.slide.action.view.SlideActionView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@android:color/white">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="小马哥" />
        LinearLayout>

        <LinearLayout
            android:layout_width="100dp"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="#ff0000"
                android:gravity="center"
                android:text="删除" />

            <TextView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="#ff0000"
                android:gravity="center"
                android:text="拉黑" />
        LinearLayout>
    com.welcom.slide.action.view.SlideActionView>
LinearLayout>

运行效果:
左滑操作(删除,置顶等。。。)_第3张图片
可以看到和我们的原理图一样了,左边显示内容区域,右边因为在屏幕外,暂时看不到。

  • 3.实现滑动
// 是否滑动到右边
private boolean isShowRightView = false;
// 滑动辅助类
private ViewDragHelper helper;

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

public SlideActionView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

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

private void initView() {
    helper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {
        @Override
        public boolean tryCaptureView(@NonNull View child, int pointerId) {
            //捕获需要滑动的View,这里返回true
            return true;
        }

        @Override
        public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
            //如果左边控件拖动,我们要让右边控件也重新布局,反之
            if (changedView == leftContentView) {
                rightActionView.layout(rightActionView.getLeft() + dx, 0, rightActionView.getRight() + dx, rightActionView.getBottom() + dy);
            } else if (changedView == rightActionView) {
                leftContentView.layout(leftContentView.getLeft() + dx, 0, leftContentView.getRight() + dx, leftContentView.getBottom() + dy);
            }
        }

        @Override
        public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
            //对左右越界问题的处理
            if (child == leftContentView) {
                //处理两边的越界问题
                if (left >= 0) {
                    left = 0;
                } else if (left <= -rightActionView.getMeasuredWidth()) {
                    left = -rightActionView.getMeasuredWidth();
                }
            } else if (child == rightActionView) {
                if (left <= leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth()) {
                    left = leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth();
                } else if (left >= leftContentView.getMeasuredWidth()) {
                    left = leftContentView.getMeasuredWidth();
                }
            }
            return left;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //松开后,什么时候打开rightActionView,什么时候关闭leftContentView
            //临界值,rightActionView.getLeft() 和 屏幕的宽度-rightActionView.getWidth()/2
            if (releasedChild == leftContentView) {
                if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 2) {
                    //使用ViewDragHelper来滑动
                    helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
                    isShowRightView = true;
                    invalidate();
                } else {
                    helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
                    isShowRightView = false;
                    invalidate();
                }
            } else if (releasedChild == rightActionView) {
                if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 2) {
                    helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
                    isShowRightView = true;
                    invalidate();
                } else {
                    helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
                    isShowRightView = false;
                    invalidate();
                }
            }
        }
    });
}

@Override
public void computeScroll() {
    if (helper.continueSettling(true)) {
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
    //处理父视图接收的触摸事件。此方法将调度回调事件。
    helper.processTouchEvent(event);
    return true;
}

运行代码,效果:
左滑操作(删除,置顶等。。。)_第4张图片

  • 到这里就完了吗?没有,接着我们将这个放入的RecyclerView中,这里直接说问题
    • 1.在RecyclerView中会出现左右滑动的时候也可以上下滑动
    • 2.显示某条item右边时,再次滑动因为未复位右边显示区域导致复用显示问题。
    • 3.点击回调未处理
      解决方法,继承RecyclerView,重写dispatchTouchEvent中判断
      完整代码如下:
public class SlideRecyclerView extends RecyclerView {
    public SlideRecyclerView(Context context) {
        super(context);
    }

    public SlideRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public SlideRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        try {
            if (SlideActionView.isShowRightView()) {
                if (SlideActionView.disTouchArea(ev)) {
                    return super.dispatchTouchEvent(ev);
                } else {
                    SlideActionView.restorePosition();
                    return true;
                }
            }
        } catch (Exception e) {
            //ignore
        }
        return super.dispatchTouchEvent(ev);
    }
}
public class SlideActionView extends ViewGroup {

    private static SlideActionView slideActionView = null;

    // 左边显示内容的View
    private View leftContentView;
    // 右边操作的View
    private View rightActionView;
    // 是否滑动到右边
    private boolean isShowRightView = false;
    // 滑动辅助类
    private ViewDragHelper helper;

    private ISlideRightActionOnClickListener rightActionOnClickListener;

    public void setRightActionOnClickListener(ISlideRightActionOnClickListener rightActionOnClickListener) {
        this.rightActionOnClickListener = rightActionOnClickListener;
    }

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

    public SlideActionView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void initView() {
        helper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(@NonNull View child, int pointerId) {
                //捕获需要滑动的View,这里返回true
                return true;
            }

            @Override
            public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
                //如果左边控件拖动,我们要让右边控件也重新布局,反之
                if (changedView == leftContentView) {
                    rightActionView.layout(rightActionView.getLeft() + dx, 0, rightActionView.getRight() + dx, rightActionView.getBottom() + dy);
                } else if (changedView == rightActionView) {
                    leftContentView.layout(leftContentView.getLeft() + dx, 0, leftContentView.getRight() + dx, leftContentView.getBottom() + dy);
                }
            }

            @Override
            public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
                //对左右越界问题的处理
                if (child == leftContentView) {
                    //处理两边的越界问题
                    if (left >= 0) {
                        left = 0;
                    } else if (left <= -rightActionView.getMeasuredWidth()) {
                        left = -rightActionView.getMeasuredWidth();
                    }
                } else if (child == rightActionView) {
                    if (left <= leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth()) {
                        left = leftContentView.getMeasuredWidth() - rightActionView.getMeasuredWidth();
                    } else if (left >= leftContentView.getMeasuredWidth()) {
                        left = leftContentView.getMeasuredWidth();
                    }
                }
                return left;
            }

            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                //松开后,什么时候打开rightActionView,什么时候关闭leftContentView
                //临界值,rightActionView.getLeft() 和 屏幕的宽度-rightActionView.getWidth()/4
                if (releasedChild == leftContentView) {
                    if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 4) {
                        //使用ViewDragHelper来滑动
                        helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
                        isShowRightView = true;
                        invalidate();
                    } else {
                        helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
                        isShowRightView = false;
                        invalidate();
                    }
                } else if (releasedChild == rightActionView) {
                    if (rightActionView.getLeft() < getMeasuredWidth() - rightActionView.getMeasuredWidth() / 2) {
                        helper.smoothSlideViewTo(rightActionView, getMeasuredWidth() - rightActionView.getMeasuredWidth(), 0);
                        isShowRightView = true;
                        invalidate();
                    } else {
                        helper.smoothSlideViewTo(rightActionView, getMeasuredWidth(), 0);
                        isShowRightView = false;
                        invalidate();
                    }
                }
            }
        });
    }

    @Override
    public void computeScroll() {
        if (helper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        } else {
            if (isShowRightView) {
                slideActionView = this;
            } else {
                slideActionView = null;
            }
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //处理父视图接收的触摸事件。此方法将调度回调事件。
        helper.processTouchEvent(event);
        return true;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() < 2) {
            throw new IllegalArgumentException("child view less than 2!!");
        }
        leftContentView = getChildAt(0);
        rightActionView = getChildAt(1);
        if (rightActionView instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) rightActionView).getChildCount(); i++) {
                ((ViewGroup) rightActionView).getChildAt(i).setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (rightActionOnClickListener != null) {
                            rightActionOnClickListener.onClick(v);
                        }
                    }
                });
            }
        } else {
            rightActionView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (rightActionOnClickListener != null) {
                        rightActionOnClickListener.onClick(v);
                    }
                }
            });
        }
    }

    public static boolean disTouchArea(MotionEvent ev) {
        if (slideActionView == null) return false;
        float rawX = ev.getRawX();
        float rawY = ev.getRawY();
        int[] location = new int[2];
        slideActionView.getLocationOnScreen(location);
        Log.i("wxq", "rawX->" + rawX);
        Log.i("wxq", "rawY->" + rawY);
        Log.i("wxq", "locationX->" + location[0]);
        Log.i("wxq", "locationY->" + location[1]);
        return rawX > location[0] && rawX < (slideActionView.getMeasuredWidth() + location[0])
                && rawY > location[1] && rawY < (slideActionView.getMeasuredHeight() + location[1]);
    }

    public static boolean isShowRightView() {
        return slideActionView != null;
    }

    //还原位置
    public static void restorePosition() {
        if (!isShowRightView()) {
            return;
        }
        try {
            if (slideActionView.helper.continueSettling(true)) return;
            slideActionView.helper.smoothSlideViewTo(slideActionView.rightActionView, slideActionView.getMeasuredWidth(), 0);
            slideActionView.isShowRightView = false;
            slideActionView.invalidate();
        } catch (Exception e) {
            //ignore
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 测量子View的宽高
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        /*
         *(0,0)                              (左width,0)
         * --------------                    --------------
         * |    左      |                    |      右    |
         * --------------  (左width,左height)-------------- (左width+右width,右height)
         */
        int leftLeft = 0;
        int leftTop = 0;
        int leftRight = leftContentView.getMeasuredWidth();
        int leftBottom = leftContentView.getMeasuredHeight();
        leftContentView.layout(leftLeft, leftTop, leftRight, leftBottom);

        int rightLeft = leftContentView.getMeasuredWidth();
        int rightTop = 0;
        int rightRight = leftContentView.getMeasuredWidth() + rightActionView.getMeasuredWidth();
        int rightBottom = rightActionView.getMeasuredHeight();
        rightActionView.layout(rightLeft, rightTop, rightRight, rightBottom);
    }

    public interface ISlideRightActionOnClickListener {
        void onClick(View view);
    }
}

#####传送门git示例代码

你可能感兴趣的:(Android)