博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进级的RecyclerView——LRecyclerView
阅读量:4292 次
发布时间:2019-05-27

本文共 13838 字,大约阅读时间需要 46 分钟。

2016-09-07 一叶飘舟
郭霖
郭霖

guolin_blog

Android技术分享平台,每天都有优质技术文章推送。你还可以向公众号投稿,将自己总结的技术心得分享给大家。

本篇来自 一叶飘舟 (也是一位csdn大神)再次投稿,在他原作的基础上进行了大幅度升级,之前就很受大家欢迎,希望依旧给你们带来惊喜。

读完本文大概需要8分钟~~~eh~~~或许还要再久点。。。

一叶飘舟 的博客地址:

http://blog.csdn.net/jdsjlzx

LRecyclerView能做什么

如果你之前没有听说过 LRecyclerView,那么请点击作者之前推文(之前取名 SuperRecyclerView,但是github有重名项目,故现改为LRecyclerView): 

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

demo apk下载地址:

https://raw.githubusercontent.com/jdsjlzx/LRecyclerView/master/app/app-release.apk

功能演示

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

  1. 左右两侧都有菜单;

  2. 根据ViewType显示菜单;

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

  4. 长按拖拽Item(Grid);

  5. 滑动删除Item;

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

  7. 用SwipeMenuLayout实现你自己的侧滑。

项目地址:

https://github.com/jdsjlzx/LRecyclerView

SwipeMenuAdapter

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

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

SwipeMenuAdapter 类的定义:

public abstract class SwipeMenuAdapter
extends RecyclerView.Adapter

实现自己的 MenuAdapter

public class MenuAdapter extends SwipeMenuAdapter<MenuAdapter.DefaultViewHolder> {
      protected List<ItemModel> mDataList = new ArrayList<>();
      public MenuAdapter() {
      }
      @Override
      public int getItemCount() {
          return mDataList.size();
      }
      public List<ItemModel> getDataList() {
          return mDataList;
      }
      public void setDataList(Collection<ItemModel> list) {
          this.mDataList.clear();
          this.mDataList.addAll(list);
          notifyDataSetChanged();
      }
      public void addAll(Collection<ItemModel> 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 voidonCreateMenu(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 voidonItemClick(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 voidopenLeftMenu(int position,int duration) {

        openMenu(position, LEFT_DIRECTION, duration);
    }
    public voidopenRightMenu(int position) {
        openMenu(position, RIGHT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION);
    }

  • openLeftMenu:打开item的左边菜单 

  • openRightMenu:打开item的右边菜单

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

根据ViewType显示菜单

效果图:

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

自定义 MenuViewTypeAdapter,代码如下:

public classMenuViewTypeAdapter extendsMenuAdapter {

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

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

private SwipeMenuCreator swipeMenuCreator = new SwipeMenuCreator() {

        @Override
        public voidonCreateMenu(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);
                ......
            }
        }
    };

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

效果图:

关键代码:

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

onItemMoveListener 具体如下:

/** * 当Item移动的时候。 */

    private OnItemMoveListener onItemMoveListener =new OnItemMoveListener() {
        @Override
        public booleanonItemMove(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 voidonItemDismiss(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 voidonItemClick(View view, int position) {
                    String text = "Click position = " + position;
                }
                @Override
                public voidonItemLongClick(View view, int position) {
                }
            });

长按拖拽Item(Grid)

效果图:

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

滑动直接删除Item

效果图:

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

关键代码:

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

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

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

效果图:

关键代码:

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

其中,onItemMovementListener 具体实现如下:

/** * 当Item被移动之前。 */

public static OnItemMovementListener onItemMovementListener =new OnItemMovementListener() {
        /** * 当Item在移动之前,获取拖拽的方向。 * @param recyclerView {@link RecyclerView}. * @param targetViewHolder target ViewHolder. * @return */
        @Override
        public intonDragFlags(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 (layoutManagerinstanceof GridLayoutManager) {
// 如果是Grid。
                return OnItemMovementListener.LEFT | OnItemMovementListener.RIGHT | OnItemMovementListener.UP | OnItemMovementListener.DOWN;// 可以上下左右拖拽。
            }
            return OnItemMovementListener.INVALID;// 返回无效的方向。
        }
        @Override
        public intonSwipeFlags(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 booleanonItemMove(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 voidonItemDismiss(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(ViewGroupparent, 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:

<?xml version="1.0" encoding="utf-8"?>

<com.github.jdsjlzx.swipe.SwipeMenuLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:swipe="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    swipe:contentViewId="@+id/swipe_content"
    swipe:leftViewId="@+id/swipe_left"
    swipe:rightViewId="@+id/swipe_right">
    <com.github.jdsjlzx.swipe.SwipeMenuView
        android:id="@id/swipe_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <FrameLayout
        android:id="@id/swipe_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <com.github.jdsjlzx.swipe.SwipeMenuView
        android:id="@id/swipe_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</com.github.jdsjlzx.swipe.SwipeMenuLayout>

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

项目地址,欢迎Star:

https://github.com/jdsjlzx/LRecyclerView


如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击
“投稿”
菜单查看。


你可能感兴趣的文章
使用Eclipse把java文件打包成jar 含有第三方jar库的jar包
查看>>
3种web会话管理的方式
查看>>
SSM(框架)-异常1:面向接口式编程异常
查看>>
Android蓝牙4.0之玩爆智能穿戴、家具(二)
查看>>
使用Condition实现多线程之间调用
查看>>
javaAPI之String
查看>>
JQ 新窗口打开链接并设置参数
查看>>
JS中常遇到的浏览器兼容问题和解决方法
查看>>
JAVA学习笔记之-servlet知识点
查看>>
apache 配置不同的端口访问不同的站点
查看>>
2017年3月Java9带来的革新!
查看>>
Log4j容器深入探究
查看>>
记glide框架使用中所遇到的问题
查看>>
学习AOP之透过Spring的Ioc理解Advisor
查看>>
Jquery一个简单的注册验证
查看>>
SpringMVC基础_ControllerAdvice
查看>>
Toast还能显示图片你知道么?
查看>>
安卓三状态切换按钮TriStateToggleButton
查看>>
Spring框架-AOP细节
查看>>
java.lang.Instrument 代理Agent使用
查看>>