RecyclerView实现瀑布流

作者: 王路飞的故事 | 来源:发表于2016-06-21 23:29 被阅读4859次

    RecyclerView is a flexible view for providing a limited window into a large data set.

    RecyclerView可以说是ListView和GridView的升级版本,提供了ListView和GridView的基础功能,并且有良好的扩展性,比如可以控制列表的布局和动画。该组件定制型太强,所以相对于ListView提出了很多新概念,刚接触可能会觉得比较复杂。

    先来看下效果图:

    瀑布流

    与RecyclerView相关的重要基础类:

    • RecyclerView.LayoutManager:控制布局,提供的实现有LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager。LinearLayoutManager提供类似ListView的列表布局,并且可以控制列表的方向。GridLayoutManager提供了类似GridView的列表布局。Staggered的意思是错列的,StaggeredGridLayoutManager继承GridLayoutManager,允许宽高不相等,所以可以用来实现瀑布流的布局。

    • ViewHolder:缓存的View Item,避免多次调用findViewById,类似我们自己在ListView设置的ViewHolder

    • RecyclerView.Adapter:提供数据集合以及显示的View Item

    • RecyclerView.ItemDectoration:绘制列表里面项与项之间的装饰,例如分割线 ApiDemo里面有分割线实现。

    • RecyclerView.ItemAnimator:列表项添加,移除,重排序的动画效果

    RecyclerView是android-support-library-v7里面的类,需要在gradle里面导入:

    compile 'com.android.support:recyclerview-v7:23.3.0'
    //或者 compile 'com.android.support:cardview-v7:21.0.+'
    

    RecyclerView实现瀑布流的实现步骤如下,其实也就是通常使用RecyclerView的步骤:

    1.在布局文件里面声明RecyclerView,findViewById获取实例,或者通过代码的方式将RecyclerView添加到布局里面。

    这两种方式还是有区别的,xml的方式可以声明RecyclerView的ScrollBar,代码方式由于初始化的时候系统未初始化ScrollBar的相关属性,所以这种方式初始化的Recycler是没有ScrollBar的。具体参考

        <!-- 声明scrollbars -->
        <android.support.v7.widget.RecyclerView
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:scrollbars="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    

    2.初始化Recycler的数据适配器

    在我们使用ListView的时候,我们会在Adapter#getView的时候使用ViewHolder来缓存我们的Sub View对象避免重复的findViewById,已到达优化ListView的目的,但是我们可以选择用与不用。但是在RecyclerView.Adapter中就必须使用,因为在创建View对象的使用明确地要求我们返回ViewHolder对象,并且在给View加载数据也是使用ViewHolder。

    在实现RecyclerView.Adapter必须要实现的三个方法,我们的列表项只有一个ImageView所以比较好理解。并且当我们的数据源发生改变是可以通过notifyItemInserted(int index),notifyItemRemoved(int index),notifyItemRangeChanged(int start,int end)通知数据适配器更新数据,如果我们在给RecyclerView设置了ItemAnimator还会显示相应的动画效果。

     public class MyViewHolder extends RecyclerView.ViewHolder {
    
            ImageView imageView;
    
            public MyViewHolder(View itemView) {
                super(itemView);
                imageView = (ImageView) itemView.findViewById(R.id.iv);
            }
    
        }
    
    
    public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>{
    
        private List<Integer> resIds;
    
        public MyAdapter(List<Integer> resIds){
            this.resIds = resIds;
        }
    
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View item = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_item,parent,false);
            return new MyViewHolder(item);
        }
    
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position) {
             holder.imageView.setImageResource(resIds.get(position));
        }
    
        @Override
        public int getItemCount() {
            return resIds.size();
        }
    }
    

    3.默认我们的列表项与项之间是没有间隙的,可以通过添加自定义的ItemDecorations来实现列表项之间的间隙。

    我们可以通过重写ItemDecorations的getItemOffsets来控制项与项之间的偏移量来实现列表之间的间隙。

    /**
     * 代码来自 http://stackoverflow.com/a/30701422
     */
    public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
    
      private int spanCount;
      private int spacing;
      private boolean includeEdge;
    
      public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
      }
    
      @Override
      public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view); // item position
        int column = position % spanCount; // item column
    
        if (includeEdge) {
          outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
          outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
    
          if (position < spanCount) { // top edge
            outRect.top = spacing;
          }
          outRect.bottom = spacing; // item bottom
        } else {
          outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
          outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
          if (position >= spanCount) {
            outRect.top = spacing; // item top
          }
        }
      }
    }
    

    4.定义列表项增加,删除,重排序的动画,提供了默认的实现DefaultItemAnimator,效果为平移动画。

    5.给列表项增加点击事件与长按事件,RecyclerView默认是没有提供的,可以通过在RecyclerView.Adapter中绑定数据的时候加上我们需要的事件,比如:

    public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>{
    
        private List<Integer> resIds;
    
        public MyAdapter(List<Integer> resIds){
            this.resIds = resIds;
        }
    
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View item = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_item,parent,false);
            return new MyViewHolder(item);
        }
    
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position) {
             holder.imageView.setImageResource(resIds.get(position));
    
             holder.imageView.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     if(mOnItemClickListener != null) {
                         mOnItemClickListener.onClick(holder,position);
                     }
                 }
             });
    
             holder.imageView.setOnLongClickListener(new View.OnLongClickListener() {
                 @Override
                 public boolean onLongClick(View v) {
                     if(mOnItemLongClickListener != null) {
                         mOnItemLongClickListener.onLongClick(holder,position);
                         return true;
                     }
                     return false;
                 }
             });
        }
    
        @Override
        public int getItemCount() {
            return resIds.size();
        }
    
        private OnItemClickListener mOnItemClickListener;
        private OnItemLongClickListener mOnItemLongClickListener;
    
        public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
            mOnItemClickListener = onItemClickListener;
        }
    
        public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
            mOnItemLongClickListener = onItemLongClickListener;
        }
    
    
        interface OnItemClickListener {
            void onClick(RecyclerView.ViewHolder VH ,int position);
        }
    
        interface OnItemLongClickListener {
            void onLongClick(RecyclerView.ViewHolder VH,int position);
        }
    
    }
    

    这样,就可以监听列表项的点击事件和长按事件了。

    综合一下,实现瀑布流的RecyclerView的代码就如下:

        RecyclerView recyclerView = new RecyclerView(this);
        RecyclerView.LayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        adapter = new MyAdapter(getData());
        recyclerView.setAdapter(adapter);
        recyclerView.addItemDecoration(new GridSpacingItemDecoration(2,10,true));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerView.setHasFixedSize(true);
        adapter.setOnItemClickListener(this);
    

    完整的示例代码查看Github上面的源码:)

    参考资料:
    http://www.grokkingandroid.com/first-glance-androids-recyclerview/

    https://developer.android.com/training/material/lists-cards.html

    相关文章

      网友评论

      • freddyyao:StaggeredGridLayoutManager 是继承LayoutManager吧
        freddyyao:@_Monkey_ support 库里面也是继承 RecyclerView.LayoutManager ,难道是support版本不同吗
        王路飞的故事:这个是support library提供的类
      • f9332573869d:其实还有另外一个方法,消耗内存小,条目不会乱动。是我自己的一些想法http://www.jianshu.com/p/1ad7437c9e24
        ,楼主可以帮我测试测试找找bug吗?
      • LucasJin:大神,我用starrgergridlayout的时候,下拉数据就没有了,怎么回事,我从api中获取的数据,每次下拉就会重新获取一次数据,应该说是一页数据,然后插入到那个model的第一个位置,在LinearLayoutManegerlayout里面是完全没有问题啊,求大神指点一二。
        LucasJin:@_Monkey_ 就是更新的时候,下啦刷新 notifyDataUpdate的时候,就没有数据显示了
        王路飞的故事: @Nicholas_Jela 数据适配器更新出现的问题吗?我的demo里面也有更新数据源的操作,你可以看下和你的情况是否一样

      本文标题:RecyclerView实现瀑布流

      本文链接:https://www.haomeiwen.com/subject/ctwgdttx.html