RecyclerView系列之滑动菜单

一、背景
前面已经实现了 RecyclerView 的下拉刷新和上拉加载更多,给 RecyclerView 添加 header,这两个用的比较多,这次实现的是滑动菜单,实现这个是因为产品经理通常会告诉你,我们要做一个和某某应用一样的效果。有人就说了:“这产品总是模仿,总是让做和别人一样的效果(小声嘀咕:关键这还真不那么容易实现)”。这就不能忍,那还有让你更不能忍的,产品告诉你做一个跟 QQ 的滑动菜单一毛一样的效果,然后你去网上一搜“仿 QQ 滑动菜单”,嚯,这么多写好的裤子,直接拿来用;然后产品说,效果要一样,但是某个地方要小改一下,例如,四面八方都加上图标,就像下面这样:
在这里插入图片描述
你有点郁闷了,做成一样不就好了,这现成的裤子都是彷 QQ 的,怎么加图标呀。然后你只好去 Read the fucking code,看看是否支持设置图标,或者能不能改一改库源码来实现,不过你惊喜的发现这裤子居然支持 setMenuBtnImage(),通过这个就能直接设置滑动菜单的图标了,但是当你测试了下之后发现,这玩意是写死的,图标只能定在左边,这就不好实现在四面八方加图标了呀,然后你决定改一下库源码来实现一下这个效果,可是你刚准备开改时,扫地大妈来了,大妈看了一眼后很感兴趣的说了:“你们这个效果我见过,我给你们提个建议啊,这个滑出来的菜单要让人一看就感觉热血沸腾,让用户有一种冲冠一怒为红颜的冲动,要让用户觉得他只要点一下就能上天了…”,产品一听,行,你这个建议好啊,我们就这样干。这样吧,我也不为难你们研发的,我给你个动画,你给我放到这个图标的位置,我要放一匹骏马儿在那奔跑,用户一滑出来就能看见,嘚儿驾~。
你仿佛听见了心中羊驼跑过的声音,大妈你是干啥的?你是来公司体验生活的吧?这时刚走开的大妈接起了电话:“房子?都租出去了,对,六套都租出去了”。
你看了看这灯红酒绿的城市,天空中飘满的雾霾,心中想起了家乡的小薇的你无语凝噎。得,自己写一个吧,你想怎么改就怎么改。
下面是最终效果:
在这里插入图片描述
二、思路分析
1.要搞一个能跟着手指左右滑动的 Layout,在这个 Layout 里有正文部分和菜单部分,正文部分占满整个控件的宽,菜单部分在正文的右边,手指往左滑动时滑出菜单部分;
2.控制滑动边界,往左滑动时最多滑动到右边菜单完全显示,往右滑动时最多滑动到正文完全显示,也就是初始状态;
3.手指滑完抬起时,判断是否应该是打开状态还是关闭状态,自动滑动到相应位置;
4.Layout 要做滑动冲突处理,以防 Layout 的滑动操作影响菜单的点击事件;
5.把这个写好的 Layout 放到 RecyclerView 的 item 里,并处理这个滑动 Layout 和 RecyclerView 的滑动冲突;
6.仿 QQ 交互优化,用户滑动 RecyclerView 时,开启的滑动菜单会立即关闭;

三、具体实现
1.首先了解一下 Scroller 的用法,不了解的可以看看郭大佬的一篇文章,本文最后贴出了连接,这个类是用来处理 View 滑动的相关操作,主要是为了实现抬起手指后控制 ScrollerLayout平滑滚动到相应的位置的,具体代码如下:

public class ScrollerLayout extends ViewGroup {

    /**
     * 用于完成滚动操作的实例
     */
    private Scroller mScroller;

    /**
     * 判定为拖动的最小移动像素数
     */
    private int mTouchSlop;

    /**
     * 手机按下时的屏幕坐标
     */
    private float mXDown;

    /**
     * 手机当时所处的屏幕坐标
     */
    private float mXMove;

    /**
     * 上次触发ACTION_MOVE事件时的屏幕坐标
     */
    private float mXLastMove;

    /**
     * 界面可滚动的左边界
     */
    private int leftBorder;

    /**
     * 界面可滚动的右边界
     */
    private int rightBorder;
    private int mSwipViewWidth;
    private int scrolledX;
    private float xDown;
    private float yDown;

    public ScrollerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 第一步,创建Scroller的实例
        mScroller = new Scroller(context);
        ViewConfiguration configuration = ViewConfiguration.get(context);
        // 获取TouchSlop值
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
        setClickable(true);
        setFocusable(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredHeight = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            // 为ScrollerLayout中的每一个子控件测量大小
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            measuredHeight = Math.max(measuredHeight, childView.getMeasuredHeight());
        }
        setMeasuredDimension(getMeasuredWidth(), measuredHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int childCount = getChildCount();
            if (childCount > 2) {
                throw new IllegalStateException("you can have at most two child views!");
            }
            if (childCount <= 0) {
                return;
            }
            int width = 0;
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                // 为ScrollerLayout中的每一个子控件在水平方向上进行布局
                int measuredWidth = childView.getMeasuredWidth();
                childView.layout(width, 0, width + measuredWidth, childView.getMeasuredHeight());
                width += measuredWidth;
                if (i == childCount - 1) {
                    mSwipViewWidth = measuredWidth;
                }
            }
            // 初始化左右边界值
            leftBorder = getChildAt(0).getLeft();
            rightBorder = getChildAt(getChildCount() - 1).getRight();
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xDown = ev.getX();
                yDown = ev.getY();
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if (Math.abs(xDown - ev.getX()) > Math.abs(yDown - ev.getY())) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                } else {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                getParent().requestDisallowInterceptTouchEvent(false);
                break;
            case MotionEvent.ACTION_CANCEL:
                getParent().requestDisallowInterceptTouchEvent(false);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mXDown = ev.getRawX();
                mXLastMove = mXDown;
                break;
            case MotionEvent.ACTION_MOVE:
                mXMove = ev.getRawX();
                float diff = Math.abs(mXMove - mXDown);
                mXLastMove = mXMove;
                // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                if (diff > mTouchSlop) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mXMove = event.getRawX();
                scrolledX = (int) (mXLastMove - mXMove);
                if (getScrollX() + scrolledX < leftBorder) {
                    scrollTo(leftBorder, 0);
                    return true;
                } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
                    scrollTo(rightBorder - getWidth(), 0);
                    return true;
                }
                scrollBy(scrolledX, 0);
                mXLastMove = mXMove;
                break;
            case MotionEvent.ACTION_UP:
                int dx;
                int targetX = scrolledX > 0 ? mSwipViewWidth / 5 : mSwipViewWidth * 4 / 5;
                //判断打开还是关闭
                if (getScrollX() > targetX) {
                    dx = mSwipViewWidth - getScrollX();
                } else {
                    dx = -getScrollX();
                }
                // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面
                mScroller.startScroll(getScrollX(), 0, dx, 0);
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }

    public boolean isOpen() {
        return getScrollX() > 0;
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.ScrollerActivity">

    <com.lcp.arecyclerview.ScrollerLayout
        android:id="@+id/scrollView"
        android:layout_width="300dp"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_height="50dp"
        android:background="#e1d9d9">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#e2c4c4">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="This is item view" />
            <Button
                android:id="@+id/btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="button"/>
        </RelativeLayout>

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

            <TextView
                android:id="@+id/settop"
                android:layout_width="100dp"
                android:layout_height="50dp"
                android:background="#C8C7CD"
                android:gravity="center"
                android:text="置顶" />

            <TextView
                android:id="@+id/collect"
                android:layout_width="100dp"
                android:layout_height="50dp"
                android:background="#FDA005"
                android:gravity="center"
                android:text="标记已读" />

            <TextView
                android:id="@+id/delete"
                android:layout_width="100dp"
                android:layout_height="50dp"
                android:background="#FD3B32"
                android:gravity="center"
                android:text="删除" />
        </LinearLayout>
    </com.lcp.arecyclerview.ScrollerLayout>
</android.support.constraint.ConstraintLayout>

代码说明:
在 onLayout 里为每个 childView 按照上面 2.1 的要求布局,正文的宽占满整个控件的宽,菜单部分放到正文的右边,并记录下菜单部分的宽度 mSwipViewWidth 和边界值;

int width = 0;
for (int i = 0; i < childCount; i++) {
    View childView = getChildAt(i);
    // 为ScrollerLayout中的每一个子控件在水平方向上进行布局
    int measuredWidth = childView.getMeasuredWidth();
    childView.layout(width, 0, width + measuredWidth, childView.getMeasuredHeight());
    width += measuredWidth;
    if (i == childCount - 1) {
        mSwipViewWidth = measuredWidth;
    }
}
// 初始化左右边界值
leftBorder = getChildAt(0).getLeft();
rightBorder = getChildAt(getChildCount() - 1).getRight();

然后处理滑动边界和手指抬起时的状态判断:

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE:
            mXMove = event.getRawX();
            scrolledX = (int) (mXLastMove - mXMove);
            if (getScrollX() + scrolledX < leftBorder) {
                scrollTo(leftBorder, 0);
                return true;
            } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
                scrollTo(rightBorder - getWidth(), 0);
                return true;
            }
            scrollBy(scrolledX, 0);
            mXLastMove = mXMove;
            break;
        case MotionEvent.ACTION_UP:
            int dx;
            int targetX = scrolledX > 0 ? mSwipViewWidth / 5 : mSwipViewWidth * 4 / 5;
            //判断打开还是关闭
            if (getScrollX() > targetX) {
                dx = mSwipViewWidth - getScrollX();
            } else {
                dx = -getScrollX();
            }
            // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面
            mScroller.startScroll(getScrollX(), 0, dx, 0);
            invalidate();
            break;
    }
    return super.onTouchEvent(event);
}

再处理上面的 2.4,ScrollerLayout 和其子控件的滑动冲突:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mXDown = ev.getRawX();
                mXLastMove = mXDown;
                break;
            case MotionEvent.ACTION_MOVE:
                mXMove = ev.getRawX();
                float diff = Math.abs(mXMove - mXDown);
                mXLastMove = mXMove;
                // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                if (diff > mTouchSlop) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

到这里滑动控件就做好了,单独使用时已经可以实现滑动菜单效果了,可以放到 RecyclerView 的 item 布局里试上一试,然后你会发现有几个奇怪的地方,例如 item 菜单往出滑时还能同时滑动 RecyclerView,如果是和 RecyclerView 同时滑动滑出的菜单,当你手指松开时,ScrollerLayout 的 onTouchEvent 将收不到 ACTION_UP 事件,这就导致 二.3 的逻辑不会执行;这时就得处理 二.5 问题,处理 ScrollerLayout 与 RecyclerView 的滑动冲突,这就要自定义一下 RecyclerView 了,因为要修改 RecyclerView 拦截触摸事件的逻辑;说到处理滑动冲突,大家肯定条件反射的想到两种方式,一种是通过从父 ViewGroup 里判断是否拦截来控制,另一种是通过在子控件里判断是否要申请滑动权限(getParent().requestDisallowInterceptTouchEvent())来控制,就像提到元歌你就想到1433223一样。
经过深思熟虑之后,我决定把这个逻辑控制放到 ScrollerLayout 里,因为我想让 RecyclerVIew 尽量少的改动,但是即便如此,也得让 RecyclerView 的拦截事件在 ACTION_DOWN 时先返回 false,所以对 RecyclerView 的修改如下:

/**
 * Created by Aislli on 2019/3/18 0018.
 */
public class MyRecyclerView extends RecyclerView {
    int mLastTouchPosition = -1;
    private ScrollerLayout mLastTouchItem;

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction() & MotionEvent.ACTION_MASK;
        boolean intercept = super.onInterceptTouchEvent(ev);
        if (action == MotionEvent.ACTION_DOWN) {
            intercept = false;//1.父View默认不在Down时拦截
            float x = ev.getX();
            float y = ev.getY();
            int childAdapterPosition = getChildAdapterPosition(findChildViewUnder(x, y));
            //3.当这次点击的item和上次不是同一个item,且上一个点击的item是ScrollerLayout类型,并且这个滑动菜单是开着的,就关掉菜单并执行RecyclerView的操作
            if (childAdapterPosition != mLastTouchPosition && null != mLastTouchItem && mLastTouchItem.isOpen()) {
                mLastTouchItem.close();
                intercept = true;
            }
            if (intercept) {
                mLastTouchPosition = -1;
                mLastTouchItem = null;
            } else {
                mLastTouchPosition = childAdapterPosition;
                ViewHolder viewHolderForAdapterPosition = findViewHolderForAdapterPosition(childAdapterPosition);
                //2.如果点击的条目是ScrollerLayout类型(支持滑动菜单的item),就赋值记录
                if (null != viewHolderForAdapterPosition && viewHolderForAdapterPosition.itemView instanceof ScrollerLayout) {
                    mLastTouchItem = (ScrollerLayout) viewHolderForAdapterPosition.itemView;
                }
            }
        }
        return intercept;
    }   
}

ScrollerLayout:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            xDown = ev.getX();
            yDown = ev.getY();
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            if (Math.abs(xDown - ev.getX()) > Math.abs(yDown - ev.getY())) {
                getParent().requestDisallowInterceptTouchEvent(true);
            } else {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            break;
        case MotionEvent.ACTION_UP:
            getParent().requestDisallowInterceptTouchEvent(false);
            break;
        case MotionEvent.ACTION_CANCEL:
            getParent().requestDisallowInterceptTouchEvent(false);
            break;
    }
    return super.dispatchTouchEvent(ev);
}

逻辑就这几行,如果是横向滑动就由 ScrollerLayout 处理滑动事件,如果这次滑动时点击的 item 和上次不是同一个,且上次点击的 item 是ScrollerLayout 类型,并且这个滑动菜单是开着的,就关掉菜单,并由 RecyclerView 来处理滑动事件;然后运行测试了一通,未发现问题,遂交之测试老姐帮忙鉴定一番,五分钟后她牛13 轰轰的回来了,她用抖音里那魔性的配音给你边演示她发现的一个 bug,边给自己的操作配了个音:“小老弟,来看,哦活!哦活!”
。你大腿一拍,恍然大悟,原来还能这样操作,只见她先把一个item的菜单滑出来,然后按住那个被打开菜单的item猛的往上或往下哦(hua)活(dong),这时onInterceptTouchEvent里控制菜单关闭的逻辑就走不进去了,因为她是按的同一个菜单拖动的,就导致上下滑动时 item 的菜单还关不了,知道原因就好办,在 MyRecyclerView 里加个滚动判断,只要列表滚动了就关掉菜单:

@Override
public void onScrollStateChanged(int state) {
    super.onScrollStateChanged(state);
    if (state == RecyclerView.SCROLL_STATE_DRAGGING) {
        //滑动时关掉菜单
        if (null != mLastTouchItem && mLastTouchItem.isOpen()) {
            mLastTouchItem.close();
        }
    }
}

OK,这样就没问题了,大功告成!

源码

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
功能树形结构 RecyclerView支持滑动删除支持长按拖拽支持单个 view 点击或长按时拖拽可开启并更改滑动删除背景色可自由指定滑动删除和拖拽操作的方向展开关闭全部分组下载Demo:下载截图使用方法添加jitpack库//build.gradle(Project:)  allprojects {       repositories {     ...      maven { url 'https://www.jitpack.io' }      }     }添加依赖 //build.gradle(Module:)dependencies {    compile 'com.github.goweii:SwipeDragTreeRecyclerView:v1.2.0'  }在xml布局文件中使用官方RecyclerView<android.support.v7.widget.RecyclerView android:id="@ id/swipe_drag_tree_recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" > </android.support.v7.widget.RecyclerView>继承 TreeState 增加几个静态变量,用于标识 item 的类别当然,你的数据应该存放在 TreeState 中public class TestTreeState extends TreeState {     public static final int TYPE_ONE = 1;     public static final int TYPE_TEO = 2;     public static final int TYPE_THREE = 3;     public static final int TYPE_FOUR = 4; }用你的 adapter 继承 BaseSwipeDragTreeAdapter 实现几个方法putTypeLayoutViewIds(int viewType, int layoutId, int[] viewIds, int[] clickFlags) 这4个参数的含义为:putTypeStartDragViewIds(int viewType, @IdRes int[] viewIds, int[] startDragFlags) 如果你想让某一个 view 在点击或者长按时实现拖拽,而不是在长按整个 item 时,应该调用这个方法完成配置viewType 类别 继承 TreeState 增加的静态变量layoutId 布局idviewIds 布局中需要用到的 view 的 idclickFlags 设置 view 是否需要点击事件,设置为 null 时默认不开启长按和单击, ClickFlag 为 adapter 的静态内部类,你直接使用即可viewType 布局类型viewIds 拖拽操作的 view 的 idstartDragFlags 拖拽标志,StartDragFlag 为 adapter 的静态内部类,你直接使用即可initIds() 在这个方法中你应该调用下面2个方法完成相关初始化bindData(BaseViewHolder holder, TypeData data) 你应该调用 holder.getItemViewType() 方法得到自定义的 item 的类别,依据类别判断 holder 绑定的数据类型,然后调用 holder 的 getView 方法获取 view 实例进行数据绑定public class TestBaseSwipeDragTreeAdapter extends BaseSwipeDragTreeAdapter {     private final int mOrientationType;     public TestBaseSwipeDragTreeAdapter(int orientationType) {         super();         mOrientationType = orientationType;     }     @Override     public void initIds() {         putTypeLayoutViewIds(TestTreeState.TYPE_ONE, R.layout.item1_swipe_drag_tree_recycler_view,                 new int[]{R.id.item1_sdtrv_tv}, null);         putTypeLayoutViewIds(TestTreeState.TYPE_TEO, R.layout.item2_swipe_drag_tree_recycler_view,                 new int[]{R.id.item2_sdtrv_tv}, null);         putTypeLayoutViewIds(TestTreeState.TYPE_THREE, R.layout.item3_swipe_drag_tree_recycler_view,                 new int[]{R.id.item3_sdtrv_tv}, null);         putTypeLayoutViewIds(TestTreeState.TYPE_FOUR, R.layout.item4_swipe_drag_tree_recycler_view,                 new int[]{R.id.item4_sdtrv_tv}, null);         putTypeLayoutViewIds(TestTreeState.TYPE_LEAF, R.layout.item5_swipe_drag_tree_recycler_view,                 new int[]{R.id.item5_sdtrv_tv}, null);         putTypeStartDragViewIds(TestTreeState.TYPE_ONE,                 new int[]{R.id.item1_sdtrv_tv}, null);         putTypeStartDragViewIds(TestTreeState.TYPE_TEO,                 new int[]{R.id.item2_sdtrv_tv}, null);         putTypeStartDragViewIds(TestTreeState.TYPE_THREE,                 new int[]{R.id.item3_sdtrv_tv}, null);         putTypeStartDragViewIds(TestTreeState.TYPE_FOUR,                 new int[]{R.id.item4_sdtrv_tv}, null);         putTypeStartDragViewIds(TestTreeState.TYPE_LEAF,                 new int[]{R.id.item5_sdtrv_tv}, null);     }     @Override     protected void bindData(BaseViewHolder holder, TypeData data) {         SwipeDragTreeViewHolder viewHolder = (SwipeDragTreeViewHolder) holder;         switch (holder.getItemViewType()) {             case TestTreeState.TYPE_ONE:                 TextView textView0 = (TextView) viewHolder.getView(R.id.item1_sdtrv_tv);                 textView0.setText((String) data.getData());                 break;             case TestTreeState.TYPE_TEO:                 TextView textView1 = (TextView) viewHolder.getView(R.id.item2_sdtrv_tv);                 textView1.setText((String) data.getData());                 break;             case TestTreeState.TYPE_THREE:                 TextView textView2 = (TextView) viewHolder.getView(R.id.item3_sdtrv_tv);                 textView2.setText((String) data.getData());                 break;             case TestTreeState.TYPE_FOUR:                 TextView textView3 = (TextView) viewHolder.getView(R.id.item4_sdtrv_tv);                 textView3.setText((String) data.getData());                 break;             case TestTreeState.TYPE_LEAF:                 TextView textView4 = (TextView) viewHolder.getView(R.id.item5_sdtrv_tv);                 textView4.setText((String) data.getData());                 break;             default:                 break;         }     } }在你的 activity 中调用 init() 方法为适配器绑定数据mSwipeDragTreeRecyclerView.setLayoutManager(getLayoutManager()); mSwipeDragTreeRecyclerView.setAdapter(mTestBaseSwipeDragTreeAdapter); mTestBaseSwipeDragTreeAdapter.init(mDatas);Adapter 相关方法说明init(ArrayList datas)给适配器绑定数据isMemoryExpandState()获取分组关闭后是否记忆子分组的展开状态setMemoryExpandState(boolean memoryExpandState)设置分组关闭后是否记忆子分组的展开状态isAllExpand()获取是否已经展开所有分组expandAll()展开所有分组collapseAll()关闭所有分组getPositions(int position)获取该 holder 位置所显示数据在树形结构数据中所处的位置setOnExpandChangeListener(OnExpandChangeListener onExpandChangeListener)设置 item 展开状态改变监听器notifyItemSwipe(int position)更新数据滑动删除,在监听器中调用更新数据notifyItemDrag(int currentPosition, int targetPosition)更新数据拖拽移动,在监听器中调用更新数据setOnItemSwipeListener(SwipeDragCallback.OnItemSwipeListener onItemSwipeListener)设置滑动删除监听器,应该调用 notifyItemSwipe 方法更新数据显示setOnItemDragListener(SwipeDragCallback.OnItemDragListener onItemDragListener)设置 item 拖拽监听器,应该调用 notifyItemDrag 方法更新数据显示setItemViewSwipeEnabled(boolean itemViewSwipeEnabled)设置开启关闭滑动删除setLongPressDragEnabled(boolean longPressDragEnabled)设置开启关闭长按拖拽setSwipeBackgroundColorEnabled(boolean swipeBackgroundColorEnabled)设置开启关闭滑动删除背景色isItemViewSwipeEnabled()获取是否开启滑动删除isLongPressDragEnabled()获取是否开启长按拖拽isSwipeBackgroundColorEnabled()获取是否开启滑动删除背景色setSwipeBackgroundColor(@ColorInt int swipeBackgroundColor)设置滑动删除背景色颜色setCustomSwipeFlag(int customSwipeFlag)设置可以滑动删除的方向,默认为垂直于滚动方向的2个方向setCustomDragFlag(int customDragFlag)设置可以拖拽的方向,线性布局默认为平行于滚动方向的2个方向,网格和流布局默认为上下左右4个方向都可以setOnItemViewClickListener(OnItemViewClickListener onItemViewClickListener)设置 itemView 点击监听器setOnItemViewLongClickListener(OnItemViewLongClickListener onItemViewLongClickListener)设置 itemView 长按监听器setOnCustomViewClickListener(OnCustomViewClickListener onCustomViewClickListener)设置 item 子 view 点击监听器,需要在适配器的 initIds() 方法中开启setOnCustomViewLongClickListener(OnCustomViewLongClickListener onCustomViewLongClickListener)设置 item 子 view 长按监听器,需要在适配器的 initIds() 方法中开启注意发现 bug 请联系 QQ302833254
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值