资讯专栏INFORMATION COLUMN

Android LRecyclerView实现Item侧滑菜单、长按拖拽Item、滑动删除Item等

wawor4827 / 2803人阅读

摘要:当在移动之前,获取拖拽的方向。只能左右滑动删除。具体实现如下当移动的时候。现在的第条被删除。为了实现滑动菜单的功能,定义了。

LRecyclerView能做什么?

如果你之前没有听说过LRecyclerView,那么请参考:
Android LRecyclerView实现下拉刷新,滑动到底部自动加载更多

经过再三思考,同时也为了大家使用方便,LRecyclerView集成了SwipeMenu系列功能,包括Item侧滑菜单、长按拖拽Item,滑动删除Item等功能。

demo apk下载地址:点我下载

功能演示

本次新增SwipeMenu系列功能描述如下:

左右两侧都有菜单;

根据ViewType显示菜单;

长按拖拽Item(List),与菜单结合;

长按拖拽Item(Grid);

滑动删除Item;

指定某个Item不能拖拽或者不能滑动删除;

用SwipeMenuLayout实现你自己的侧滑。

项目地址:https://github.com/jdsjlzx/LR...

SwipeMenuAdapter

为了实现SwipeMenu的功能,此次新增了一个SwipeMenuAdapter类。

SwipeMenuAdapter与library中已经存在的LRecyclerViewAdapter会不会冲突呢?答案是不会。SwipeMenuAdapter是用户级别的基类adapter,也就是用户需要继承SwipeMenuAdapter去实现自己的adapter,还像之前那样使用即可。

SwipeMenuAdapter类的定义:

public abstract class SwipeMenuAdapter extends RecyclerView.Adapter 

实现自己的MenuAdapter:

public class MenuAdapter extends SwipeMenuAdapter {

    protected List mDataList = new ArrayList<>();

    public MenuAdapter() {
    }

    @Override
    public int getItemCount() {
        return mDataList.size();
    }

    public List getDataList() {
        return mDataList;
    }

    public void setDataList(Collection list) {
        this.mDataList.clear();
        this.mDataList.addAll(list);
        notifyDataSetChanged();
    }

    public void addAll(Collection list) {
        int lastIndex = this.mDataList.size();
        if (this.mDataList.addAll(list)) {
            notifyItemRangeInserted(lastIndex, list.size());
        }
    }

    public void remove(int position) {
        mDataList.remove(position);
        notifyItemRemoved(position);
        if(position != mDataList.size()){ // 如果移除的是最后一个,忽略
            notifyItemRangeChanged(position, mDataList.size() - position);
        }

    }

    public void clear() {
        mDataList.clear();
        notifyDataSetChanged();
    }

    @Override
    public View onCreateContentView(ViewGroup parent, int viewType) {
        return LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_text_swipe, parent, false);
    }

    @Override
    public MenuAdapter.DefaultViewHolder onCompatCreateViewHolder(View realContentView, int viewType) {
        return new DefaultViewHolder(realContentView);
    }

    @Override
    public void onBindViewHolder(MenuAdapter.DefaultViewHolder holder, int position) {

        String item = mDataList.get(position).title;

        DefaultViewHolder viewHolder = holder;
        viewHolder.tvTitle.setText(item);
    }

    static class DefaultViewHolder extends RecyclerView.ViewHolder {
        TextView tvTitle;

        public DefaultViewHolder(View itemView) {
            super(itemView);
            tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
        }

    }

}

是不是很方便?MenuAdapter基本的功能都满足了,直接拷贝到项目中即可使用。

上面说了那么多,关键的也就这几句:

mDataAdapter = new MenuAdapter();
mDataAdapter.setDataList(dataList);

mLRecyclerViewAdapter = new LRecyclerViewAdapter(this, mDataAdapter);
mRecyclerView.setAdapter(mLRecyclerViewAdapter);

下面具体分析每个功能。

左右两侧都有菜单

效果图:

具体使用步骤如下。

为SwipeRecyclerView的Item创建菜单

// 设置菜单创建器。
mRecyclerView.setSwipeMenuCreator(swipeMenuCreator);
//设置菜单Item点击监听事件        mRecyclerView.setSwipeMenuItemClickListener(menuItemClickListener);

其中swipeMenuCreator和menuItemClickListener代码如下:

/**
 * 菜单创建器。在Item要创建菜单的时候调用。
 */
private SwipeMenuCreator swipeMenuCreator = new SwipeMenuCreator() {
        @Override
        public void onCreateMenu(SwipeMenu swipeLeftMenu, SwipeMenu swipeRightMenu, int viewType) {
            int size = getResources().getDimensionPixelSize(R.dimen.item_height);

            // 添加左侧的,如果不添加,则左侧不会出现菜单。
            {
                SwipeMenuItem addItem = new SwipeMenuItem(mContext)
                        .setBackgroundDrawable(R.drawable.selector_green)// 点击的背景。
                        .setImage(R.mipmap.ic_action_add) // 图标。
                        .setWidth(size) // 宽度。
                        .setHeight(size); // 高度。
                swipeLeftMenu.addMenuItem(addItem); // 添加一个按钮到左侧菜单。

                SwipeMenuItem closeItem = new SwipeMenuItem(mContext)
                        .setBackgroundDrawable(R.drawable.selector_red)
                        .setImage(R.mipmap.ic_action_close)
                        .setWidth(size)
                        .setHeight(size);

                swipeLeftMenu.addMenuItem(closeItem); // 添加一个按钮到左侧菜单。
            }

            // 添加右侧的,如果不添加,则右侧不会出现菜单。
            {
                SwipeMenuItem deleteItem = new SwipeMenuItem(mContext)
                        .setBackgroundDrawable(R.drawable.selector_red)
                        .setImage(R.mipmap.ic_action_delete)
                        .setText("删除") // 文字,还可以设置文字颜色,大小等。。
                        .setTextColor(Color.WHITE)
                        .setWidth(size)
                        .setHeight(size);
                swipeRightMenu.addMenuItem(deleteItem);// 添加一个按钮到右侧侧菜单。

                SwipeMenuItem closeItem = new SwipeMenuItem(mContext)
                        .setBackgroundDrawable(R.drawable.selector_purple)
                        .setImage(R.mipmap.ic_action_close)
                        .setWidth(size)
                        .setHeight(size);
                swipeRightMenu.addMenuItem(closeItem); // 添加一个按钮到右侧菜单。

                SwipeMenuItem addItem = new SwipeMenuItem(mContext)
                        .setBackgroundDrawable(R.drawable.selector_green)
                        .setText("添加")
                        .setTextColor(Color.WHITE)
                        .setWidth(size)
                        .setHeight(size);
                swipeRightMenu.addMenuItem(addItem); // 添加一个按钮到右侧菜单。
            }
        }
    };

    /**
     * 菜单点击监听。
     */
    private OnSwipeMenuItemClickListener menuItemClickListener = new OnSwipeMenuItemClickListener() {
        /**
         * Item的菜单被点击的时候调用。
         * @param closeable       closeable. 用来关闭菜单。
         * @param adapterPosition adapterPosition. 这个菜单所在的item在Adapter中position。
         * @param menuPosition    menuPosition. 这个菜单的position。比如你为某个Item创建了2个MenuItem,那么这个position可能是是 0、1,
         * @param direction       如果是左侧菜单,值是:SwipeMenuRecyclerView#LEFT_DIRECTION,如果是右侧菜单,值是:SwipeMenuRecyclerView#RIGHT_DIRECTION.
         */
        @Override
        public void onItemClick(Closeable closeable, int adapterPosition, int menuPosition, int direction) {
            closeable.smoothCloseMenu();// 关闭被点击的菜单。

            if (direction == LRecyclerView.RIGHT_DIRECTION) {
                Toast.makeText(mContext, "list第" + adapterPosition + "; 右侧菜单第" + menuPosition, Toast.LENGTH_SHORT).show();
            } else if (direction == LRecyclerView.LEFT_DIRECTION) {
                Toast.makeText(mContext, "list第" + adapterPosition + "; 左侧菜单第" + menuPosition, Toast.LENGTH_SHORT).show();
            }
        }
    };

从上面代码可以看出,swipeMenuCreator完成了左右菜单的创建,menuItemClickListener实现了菜单的点击事件。

需要注意的是,LRecyclerView提供了下面两个方法,具体使用请详见demo。

public void openLeftMenu(int position, int duration) {
        openMenu(position, LEFT_DIRECTION, duration);
    }

    public void openRightMenu(int position) {
        openMenu(position, RIGHT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION);
    }

openLeftMenu:打开item的左边菜单
openRightMenu:打开item的右边菜单

这里关键的就是这个position(详细请参考demo),先埋下个伏笔,后面介绍。

根据ViewType显示菜单

效果图:

根据ViewType决定SwipeMenu在哪一行出现,可以左侧,可以右侧。

自定义MenuViewTypeAdapter,代码如下:

public class MenuViewTypeAdapter extends MenuAdapter {

    public static final int VIEW_TYPE_MENU = 1;
    public static final int VIEW_TYPE_NONE = 2;

    @Override
    public int getItemViewType(int position) {
        return position % 2 == 0 ? VIEW_TYPE_MENU : VIEW_TYPE_NONE;
    }
}

在实现swipeMenuCreator 时,需要根据ItemViewType值来决定是否创建左右菜单。

    private SwipeMenuCreator swipeMenuCreator = new SwipeMenuCreator() {
        @Override
        public void onCreateMenu(SwipeMenu swipeLeftMenu, SwipeMenu swipeRightMenu, int viewType) {
        // 根据Adapter的ViewType来决定菜单的样式、颜色等属性、或者是否添加菜单。
            if (viewType == MenuViewTypeAdapter.VIEW_TYPE_NONE) {
            
                // Do nothing.
            } else if (viewType == MenuViewTypeAdapter.VIEW_TYPE_MENU) {
                int size = getResources().getDimensionPixelSize(R.dimen.item_height);

                ......
            }
        }
    };
长按拖拽Item(List),与菜单结合

效果图:

关键代码:

mRecyclerView.setLongPressDragEnabled(true);// 开启拖拽功能        mRecyclerView.setOnItemMoveListener(onItemMoveListener);// 监听拖拽,更新UI。

onItemMoveListener具体如下:

    /**
     * 当Item移动的时候。
     */
    private OnItemMoveListener onItemMoveListener = new OnItemMoveListener() {
        @Override
        public boolean onItemMove(int fromPosition, int toPosition) {
            final int adjFromPosition = mLRecyclerViewAdapter.getAdapterPosition(true, fromPosition);
            final int adjToPosition = mLRecyclerViewAdapter.getAdapterPosition(true, toPosition);
            // 当Item被拖拽的时候。
            Collections.swap(mDataAdapter.getDataList(), adjFromPosition, adjToPosition);
            //Be carefull in here!
            mLRecyclerViewAdapter.notifyItemMoved(fromPosition, toPosition);
            return true;// 返回true表示处理了,返回false表示你没有处理。
        }

        @Override
        public void onItemDismiss(int position) {
            // 当Item被滑动删除掉的时候,在这里是无效的,因为这里没有启用这个功能。
            // 使用Menu时就不用使用这个侧滑删除啦,两个是冲突的。
        }
    };

注意下面代码:

final int adjFromPosition = mLRecyclerViewAdapter.getAdapterPosition(true, fromPosition);
final int adjToPosition = mLRecyclerViewAdapter.getAdapterPosition(true, toPosition);

关于position的位置,为了大家使用方便,特在LRecyclerViewAdapter中提供了一个方法getAdapterPosition(boolean isCallback, int position)。

isCallback 含义:position是否接口回调中带来的

position 含义:如果不是接口回调,就是用户自己指定的position

getAdapterPosition(boolean isCallback, int position)只用于非LRecyclerViewAdapter提供的接口。

举例说明:

setOnItemMoveListener不是 LRecyclerViewAdapter自带接口(也就是内部方法),需要调用getAdapterPosition方法获得正确的position

如setOnItemClickLitener 是 LRecyclerViewAdapter自带接口,接口里面自带了position,用户就不必调用getAdapterPosition方法,直接使用就可以了。

mLRecyclerViewAdapter.setOnItemClickLitener(new OnItemClickLitener() {
            @Override
            public void onItemClick(View view, int position) {
                String text = "Click position = " + position;
               
            }

            @Override
            public void onItemLongClick(View view, int position) {


            }
        });
长按拖拽Item(Grid)

效果图:

与list功能一样,只是布局不一样。

滑动直接删除Item

效果图:

注意:

滑动删除和滑动菜单是互相冲突的,两者只能出现一个。

关键代码:

mRecyclerView.setLongPressDragEnabled(true);
mRecyclerView.setItemViewSwipeEnabled(true);// 开启滑动删除        mRecyclerView.setOnItemMoveListener(onItemMoveListener);// 监听拖拽,更新UI

按照配置就可以实现滑动删除。

指定某个Item不能拖拽或者不能滑动删除

效果图:

关键代码:

mRecyclerView.setLongPressDragEnabled(true);
mRecyclerView.setItemViewSwipeEnabled(true);// 开启滑动删除。        mRecyclerView.setOnItemMoveListener(onItemMoveListener);// 监听拖拽,更新UI。        mRecyclerView.setOnItemMovementListener(onItemMovementListener);

其中,onItemMovementListener具体实现如下:

/**
 * 当Item被移动之前。
 */
public static OnItemMovementListener onItemMovementListener = new OnItemMovementListener() {
        /**
         * 当Item在移动之前,获取拖拽的方向。
         * @param recyclerView     {@link RecyclerView}.
         * @param targetViewHolder target ViewHolder.
         * @return
         */
        @Override
        public int onDragFlags(RecyclerView recyclerView, RecyclerView.ViewHolder targetViewHolder) {
            // 我们让第一个不能拖拽
            if (targetViewHolder.getAdapterPosition() == 0) {
                return OnItemMovementListener.INVALID;// 返回无效的方向。
            }

            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
            if (layoutManager instanceof LinearLayoutManager) {// 如果是LinearLayoutManager。
                LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
                if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {// 横向的List。
                    return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT; // 只能左右拖拽。
                } else {// 竖向的List。
                    return OnItemMovementListener.UP | OnItemMovementListener.DOWN; // 只能上下拖拽。
                }
            } else if (layoutManager instanceof GridLayoutManager) {// 如果是Grid。
                return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT | OnItemMovementListener.UP | OnItemMovementListener.DOWN; // 可以上下左右拖拽。
            }
            return OnItemMovementListener.INVALID;// 返回无效的方向。
        }

        @Override
        public int onSwipeFlags(RecyclerView recyclerView, RecyclerView.ViewHolder targetViewHolder) {
            // 我们让第一个不能滑动删除。
            if (targetViewHolder.getAdapterPosition() == 0) {
                return OnItemMovementListener.INVALID;// 返回无效的方向。
            }

            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
            if (layoutManager instanceof LinearLayoutManager) {// 如果是LinearLayoutManager
                LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
                if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {// 横向的List。
                    return OnItemMovementListener.UP | OnItemMovementListener.DOWN; // 只能上下滑动删除。
                } else {// 竖向的List。
                    return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT; // 只能左右滑动删除。
                }
            }
            return OnItemMovementListener.INVALID;// 其它均返回无效的方向。
        }
    };

onItemMoveListener具体实现如下:

    /**
     * 当Item移动的时候。
     */
    private OnItemMoveListener onItemMoveListener = new OnItemMoveListener() {
        @Override
        public boolean onItemMove(int fromPosition, int toPosition) {
            final int adjFromPosition = mLRecyclerViewAdapter.getAdapterPosition(true, fromPosition);
            final int adjToPosition = mLRecyclerViewAdapter.getAdapterPosition(true, toPosition);
            if (adjToPosition == 0) {// 保证第一个不被挤走。
                return false;
            }
            // 当Item被拖拽的时候。
            Collections.swap(mDataAdapter.getDataList(), adjFromPosition, adjToPosition);
            //Be carefull in here!
            mLRecyclerViewAdapter.notifyItemMoved(fromPosition, toPosition);
            return true;
        }

        @Override
        public void onItemDismiss(int position) {
            final int adjPosition = mLRecyclerViewAdapter.getAdapterPosition(true, position);
            mDataAdapter.remove(adjPosition);
            AppToast.showShortText(DragSwipeFlagsActivity.this, "现在的第" + adjPosition + "条被删除。");
        }

    };

通过代码中的注释,就可以明白了,一切尽在代码中。

用SwipeMenuLayout实现你自己的侧滑

效果图:

这个与LRecyclerView关系不大,但是与SwipeMenu关系密切。为了实现滑动菜单的功能,定义了SwipeMenuLayout。

SwipeMenuLayout类的定义:

public class SwipeMenuLayout extends FrameLayout implements SwipeSwitch

在开头提到的SwipeMenuAdapter的

    @Override
    public final VH onCreateViewHolder(ViewGroup parent, int viewType) {
        View contentView = onCreateContentView(parent, viewType);
        if (mSwipeMenuCreator != null) {
            SwipeMenuLayout swipeMenuLayout = (SwipeMenuLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_recyclerview_swipe_item_default, parent, false);

......
}

layout_recyclerview_swipe_item_default.xml




    

    

    

看来这个布局,你是不是有种恍然大悟的感觉呢?左右滑动就是通过SwipeMenuView来实现的。

项目地址:https://github.com/jdsjlzx/LR...,欢迎Star!

Thanks

SwipeRecyclerView 作者:严振杰

别看他年龄小,也很牛啊!

如果你觉得这篇文章对你有用,那么赞一个或者留个言吧!

另外下载Demo有意外收获啊!

如果你对LRecyclerView有什么好的想法或者建议,期待你的留言!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65018.html

相关文章

  • 复杂type页面封装库,支持多种状态切换和下拉刷新上拉加载

    摘要:支持复杂页面,例如添加自定义头部和底部布局,支持横向滑动,还可以支持粘贴头部类似微信好友分组,支持不规则瀑布流效果,支持侧滑删除功能。十分方便实现复杂的布局页面,结构上层次分明,便于维护。 目录介绍 1.复杂页面库介绍 2.本库优势亮点 2.1 支持多种状态切换管理 2.2 支持添加多个header和footer 2.3 支持侧滑功能和拖拽移动 2.4 其他亮点介绍 3.如...

    Karrdy 评论0 收藏0
  • RecyclerView封装库和综合案例【包含25篇博客】

    摘要:支持复杂页面,例如添加自定义头部和底部布局,支持横向滑动,还可以支持粘贴头部类似微信好友分组,支持不规则瀑布流效果,支持侧滑删除功能。支持粘贴头部的需求效果,这种效果类似微信好友分组的那种功能界面。 目录介绍 1.复杂页面库介绍 2.本库优势亮点 2.1 支持多种状态切换管理 2.2 支持添加多个header和footer 2.3 支持侧滑功能和拖拽移动 2.4 其他亮点介绍 ...

    silenceboy 评论0 收藏0
  • [Android] 开源View组件(一)

    摘要:系列,自定义实现知乎首页仿今日头条最强顶部导航指示器,支持种模式系列之一使用打造千变万化的指示器优雅的为添加和实现快速滑动实现条目拖拽排序与滑动删除高仿网易新闻首页添加,删除,排序类似大众点评美团等应用的城市选择器那些酷炫的开源库整理 Material Design系列,自定义Behavior实现Android知乎首页 showImg(http://img.blog.csdn.net/...

    scola666 评论0 收藏0

发表评论

0条评论

wawor4827

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<