美文网首页Android开发程序员
RecyclerView之瀑布流(三)

RecyclerView之瀑布流(三)

作者: 侯蛋蛋_ | 来源:发表于2017-09-18 20:42 被阅读0次

    概述

    RecyclerView(一)使用完全指南
    RecyclerView(二)之万能分割线
    RecyclerView之瀑布流(三)

    RecyclerView提供了三种布局管理器:

    • LinerLayoutManager 以垂直或者水平列表方式展示Item
    • GridLayoutManager 以网格方式展示Item
    • StaggeredGridLayoutManager 以瀑布流方式展示Item

    瀑布流样式

    RecyclerView的瀑布流布局管理器是taggeredGridLayoutManager,它最常用的构造函数就一个,StaggeredGridLayoutManager(int spanCount, int orientation),spanCount代表每行或每列的Item个数,orientation代表列表的方向,竖直或者水平。

    看在代码中的使用。

    // 初始化布局管理器
    mLayoutManager = new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL);
    // 设置布局管理器
    mRecyclerView.setLayoutManager(mLayoutManager);
    // 设置adapter
    mRecyclerView.setAdapter(mAdapter);
    // 设置间隔样式
    mRecyclerView.addItemDecoration(new MDStaggeredRvDividerDecotation(this));
    
    

    要实现瀑布流效果(仅讨论竖直方向的瀑布流样式),每一个Item的高度要有所差别,如果所有的item的高度相同,就和网格样式是一样的展示效果。示例中就实现两中不同高度的Item,一个高度为80dp,一个高度为100dp。

    view_rv_staggered_item.xml布局:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="80dp">
        <TextView
            android:id="@+id/item_tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            tools:text="item"/>
    </LinearLayout>
    
    

    view_rv_staggered_item_two.xml布局:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="100dp">
        <TextView
            android:id="@+id/item_tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            tools:text="item"/>
    </LinearLayout>
    
    

    Item不同的布局是在Adapter里面绑定的,看一下Adapter的实现。

    public class MDStaggeredRvAdapter extends RecyclerView.Adapter<MDStaggeredRvAdapter.ViewHolder> {
    
        /**
         * 展示数据
         */
        private ArrayList<String> mData;
    
        public MDStaggeredRvAdapter(ArrayList<String> data) {
            this.mData = data;
        }
    
        public void updateData(ArrayList<String> data) {
            this.mData = data;
            notifyDataSetChanged();
        }
    
        @Override
        public int getItemViewType(int position) {
            // 瀑布流样式外部设置spanCount为2,在这列设置两个不同的item type,以区分不同的布局
            return position % 2;
        }
    
        @Override
        public MDStaggeredRvAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            // 实例化展示的view
            View v;
            if(viewType == 1) {
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_rv_staggered_item, parent, false);
            } else {
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_rv_staggered_item_two, parent, false);
            }
            // 实例化viewholder
            ViewHolder viewHolder = new ViewHolder(v);
            return viewHolder;
        }
    
        @Override
        public void onBindViewHolder(MDStaggeredRvAdapter.ViewHolder holder, int position) {
            // 绑定数据
            holder.mTv.setText(mData.get(position));
        }
    
        @Override
        public int getItemCount() {
            return mData == null ? 0 : mData.size();
        }
    
        public static class ViewHolder extends RecyclerView.ViewHolder {
    
            TextView mTv;
    
            public ViewHolder(View itemView) {
                super(itemView);
                mTv = (TextView) itemView.findViewById(R.id.item_tv);
            }
        }
    }
    
    

    接下来是设置瀑布流样式的间隔线样式的,上面代码中使用的是MDStaggeredRvDividerDecotation
    类,其实是直接拷贝的网格样式的间隔线绘制类。看一下运行效果。

    RecyclerView-瀑布流2列.jpg

    很奇怪,间隔线并没有按照我们想象中的方式绘制,仔细看瀑布流中Item的分布,发现瀑布流样式的Item分布和网格样式的Item分布有些不同。对比一下两者Item的分布,如下图。

    RecyclerView-对比.png

    网格样式的Item分布规律很明显,竖直方向的网格,Item是从左向右从上到下依次按顺序排列分布。

    瀑布流样式的Item分布也是从上到下,从左到右的顺序排列,但是有一个高度的优先级,如果某一列中有一个高度最低的位置为空,最优先在此处添加Item。看第三张图的3 item,因为该位置最低,优先在此处添加Item。

    分析出了瀑布流样式的Item的分布规律,就会发现,按照以往列表样式或者网格样式去设置间隔线是有问题的,因为不知道Item具体的位置,上下左右间隔线是否需要绘制不确定,参考第二张图,其实第三张图的间隔线也有问题,向上滑动就会展示出来。

    目前能考虑到的瀑布流添加间隔线的思路:

    • Item布局中设置四周间隔padding/margin
    • 代码中动态修改ItemView的间隔padding/margin

    设置间隔有两个方法:

    • 上下左右都设置间隔
    • 相邻两边设置间隔(左上/左下/右上/右下)

    第一种设置间隔的方法会导致相邻的Item间距是间隔的两倍,第二种设置间隔的方法会导致Item某一个方向上的与父布局边缘无间隔,但是另一个方向与父布局边缘有间隔,例如左上相邻两边设置了间隔,最左边一列的Item左边与父布局边缘有间隔,但是最右边一列Item右边与父布局无间隔,第一行和最后一行的Item也会出现这种情况。

    要解决上面的问题,父布局RecyclerView也需要根据相应的情况设置padding让整个布局的间隔都一致。下面的例子是选择在Item布局中设置间隔,因为可以自己在布局文件中控制颜色比较方便,选择右下两边设置间隔。

    首先修改两个Item的布局文件。
    view_rv_staggered_item.xml修改背景色和外层间距背景色。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="@dimen/md_common_view_height"
                  android:background="@color/md_divider"
                  android:paddingBottom="5dp"
                  android:paddingRight="5dp">
        <TextView
            android:id="@+id/item_tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:background="@color/md_white"
            tools:text="item"/>
    </LinearLayout>
    
    

    同样修改view_rv_staggered_item_two.xml。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="100dp"
                  android:paddingBottom="5dp"
                  android:paddingRight="5dp"
                  android:background="@color/md_divider">
        <TextView
            android:id="@+id/item_tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:background="@color/md_white"
            tools:text="item"/>
    </LinearLayout>
    
    

    运行一下,看看最后的效果。

    RecyclerView瀑布流.gif

    差不多完美的解决了间隔线的问题,有细心的同学可能发现,在RecyclerView滑动的时候上面一直有一条灰色的间隔线,这个可以通过取消xml布局文件中RecyclerView的paddingTop属性去掉顶部灰色的间隔线。

    总结

    本篇文章主要介绍网格样式和瀑布流样式的RecyclerView,列表样式、网格样式和瀑布流样式在某种程度上是可以转换的。

    • 网格样式的布局管理器的spanCount设置为1,就是列表样式
    • 瀑布流样式如果Item的布局文件是等高,竖直方向,就是竖直方向的网格样式;如果Item是等宽,水平方向,那就是水平方向的网络样式
    • 如果瀑布流样式的布局管理器spanCount设置为1,竖直方向,是竖直方向的列表;水平方向,就是水平方向的列表

    RecyclerView(一)使用完全指南

    RecyclerView(二)之万能分割线

    相关文章

      网友评论

        本文标题:RecyclerView之瀑布流(三)

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