RecyclerView使用到原理

前言:

开发中离不开的RecyclerView,用好它让开发UI更加丝滑。通常在使用RecyclerView的时候,我们都会用到多布局,尤其是APP的首页。像那些大厂的首页,比如:京东,淘宝,美团,QQ音乐等。都不是简单的布局,有些涉及到嵌套问题。一旦使用了嵌套,会有各种问题,性能问题,滑动问题等。因此我们在开发中尽量避免多层嵌套。

当然本篇文章没有涉及到更复杂的布局使用,像列表中存在横向滑动,分页,嵌套viewpager,嵌套tablayout等,这些使用场景有同学做过优化的可以一起讨论交流。

一,基础使用

1.1 简单布局

写一个简单布局只有TextView,简单点,写代码的方式简单点。

public class TestAdapter extends RecyclerView.Adapter<TestAdapter.TestViewHolder> {
    private List<String> mList = new ArrayList<>();

    public TestAdapter(List<String> list) {
        this.mList = list;
    }

    @NonNull
    @Override
    public TestViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_test, parent,false);
        return new TestViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull TestViewHolder holder, int position) {
        holder.textView.setText(mList.get(position));
    }

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

    class TestViewHolder extends RecyclerView.ViewHolder {
        private AppCompatTextView textView;

        public TestViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.item_tv);
        }
    }

}
 mRecyclerView=findViewById(R.id.activity_rv);
 mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

 List<String> list=new ArrayList<>();
 list.add("学生");
 list.add("老师");
 list.add("家长");
 list.add("同事");

 TestAdapter testAdapter=new TestAdapter(list);
 mRecyclerView.setAdapter(testAdapter);

代码很简单,但是需要注意的是一下两种布局加载方式。如果用了第二种,你会发现无法居中显示。

  View view = LayoutInflater.from(context).inflate(R.layout.item_test, parent,false);
  View view1=LayoutInflater.from(context).inflate(R.layout.item_test,null);

在这里插入图片描述

1.2 横向显示

   mRecyclerView.setLayoutManager(new LinearLayoutManager(this,RecyclerView.HORIZONTAL,false));

1.3 网格显示

   mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));

1.4 网格显示升级版

 GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 4);
 gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                // 显示的列数 = spanCount / spanSize ;
                if (position % 5 == 4) {
                    //SpanSize 返回4 代表该行只显示1列    列数 = 4/4 =1
                    return 4;
                } else {
                    //SpanSize 返回1代表该行显示4列    列数 = 4/1 =4
                    return 1;
                }
            }
        });

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200527144553156.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfeW9uZ19odWlfMTIzNA==,size_16,color_FFFFFF,t_70

二,网格布局升级版使用

在这里插入图片描述

如果要实现上图的效果,我们该如何去设计呢。首先需要分析上图一共有几种布局,再者由于是一维布局,我们可以采用非嵌套模式去实现。当然这里如果支持二维布局的横向滑动呀,分页滑动呀等,这种就得需要使用嵌套了,我们先不说嵌套。就是利用上文中介绍的setSpanSizeLookup方式去实现。

开始分析:上图布局种类,一共可以看成4中,当然第一个位置一般都是banner轮番图,在这里我们可以看成是一张图片。这四种布局显示类型分别是,1,2,4,5,也就是一行显示几个。我们知道在网格布局中,我们需要指定一行显示几个,比如:GridLayoutManager(this, 4),这里的4代表一行显示4个,但是我们要用setSpanSizeLookup方式去显示,怎么去计算呢,这里我找到了一个通用的做法。

通用方式:基数代表GridLayoutManager(this, 4)传入的值,这里是4

比如:基数4,可以显示几种布局呢,1,2,4
比如:基数5,可以显示几种布局呢,1,5
比如:基数6,可以显示几种布局呢,1,2,3,6

有没有发现规律,能显示几种布局,就看哪些数可以被基数整除了。

因此要实现上文中的1,2,4,5类型的布局,那就要找到被他们整除的数。当然这里是20了。

这里有同学就会问了,为什么是整数倍呢,因为上文getSpanSize方法返回的是整数。假如布局中有显示1,2,3,4,5的该怎么办呢,这种方式就不行了。

好了,我们开始写代码吧,实现上图效果。

在这里插入图片描述

2.1 第一部分页面

item_banner.xml内容

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="#78D2DD" />
</LinearLayout>

item_icon.xml内容

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center">
    <ImageView
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:background="#B66666" />
</LinearLayout>

item_title.xml内容

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:textColor="#000000"
        android:text="分类"
        android:textSize="24sp" />
</LinearLayout>

item_content1.xml内容

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center">

    <ImageView
        android:layout_width="160dp"
        android:layout_height="60dp"
        android:layout_marginBottom="10dp"
        android:background="#F33737" />
</LinearLayout>

item_content2.xml内容

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:gravity="center"
    android:layout_height="wrap_content">
    <ImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:background="#CAC27B" />
</LinearLayout>

2.2 bean类和holder类

BaseItem类

public interface  BaseItem {
    int getType();
}

BannerItem类

public class BannerItem implements BaseItem {

    @Override
    public int getType() {
        return ItemHolderFactory.BANNER_TYPE;
    }
}

…更多省略

BaseHolder类

public abstract class BaseHolder extends RecyclerView.ViewHolder {
    BaseHolder(View item) {
        super(item);
    }
}

BannerHolder 类

public class BannerHolder extends BaseHolder {
    public BannerHolder(View item) {
        super(item);
    }
}

…更多省略

2.3 ItemHolderFactory 类

public class ItemHolderFactory {
    public static final int BANNER_TYPE = 0;
    public static final int TITLE_TYPE = 1;
    public static final int ICON_TYPE = 2;
    public static final int CONTENT1_TYPE = 4;
    public static final int CONTENT2_TYPE = 5;

    public static BaseHolder getItemHolder(ViewGroup parent, int type) {
        switch (type) {
            default:
            case BANNER_TYPE:
                return new BannerHolder(LayoutInflater
                        .from(parent.getContext()).inflate(R.layout.item_banner, parent, false));
            case TITLE_TYPE:
                return new TitleHolder(LayoutInflater
                        .from(parent.getContext()).inflate(R.layout.item_title, parent, false));
            case ICON_TYPE:
                return new IconHolder(LayoutInflater
                        .from(parent.getContext()).inflate(R.layout.item_icon, parent, false));
            case CONTENT1_TYPE:
                return new ContentHolder1(LayoutInflater
                        .from(parent.getContext()).inflate(R.layout.item_content1, parent, false));
            case CONTENT2_TYPE:
                return new ContentHolder2(LayoutInflater
                        .from(parent.getContext()).inflate(R.layout.item_content2, parent, false));
        }
    }
}

2.4 MultiViewAdapter 类

public class MultiViewAdapter extends RecyclerView.Adapter<BaseHolder> {
    private List<BaseItem> mDataList;

    public MultiViewAdapter(List<BaseItem> dataList) {
        mDataList = dataList;
    }

    @NonNull
    @Override
    public BaseHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {
        return ItemHolderFactory.getItemHolder(viewGroup, type);
    }

    @Override
    public void onBindViewHolder(@NonNull BaseHolder viewHolder, int i) {
    }

    @Override
    public int getItemViewType(int position) {
        //Get the type of item
        return mDataList.get(position).getType();
    }

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

    public void setSpanCount(GridLayoutManager layoutManager) {
        layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int i) {
                int type = getItemViewType(i);
                switch (type) {
                    default:
                    case ItemHolderFactory.BANNER_TYPE:
                    case ItemHolderFactory.TITLE_TYPE:
                        return 20;//一行显示1个item   20/20=1
                    case ItemHolderFactory.ICON_TYPE:
                        return 4;//一行显示5个item   20/4=5
                    case ItemHolderFactory.CONTENT1_TYPE:
                        return 10;//一行显示2个item   20/10=2
                    case ItemHolderFactory.CONTENT2_TYPE:
                        return 5;//一行显示4个item   20/5=4
                }
            }
        });
    }
}

2.5 MainActivity类

对于数据这一块,同学们需要理解一下,不要弄混。记着咱们的布局是一维的,也就是相当于只有一级分类。分类title和分类子内容是同级的,所以当我们传入一个title数据的时候,对应的就要传入要显示几个子内容。

       List<BaseItem> list = new ArrayList<>();
        //banner数据
        list.add(new BannerItem());
        //icon数据
        for (int i = 0; i < 10; i++) {
            list.add(new IconItem());
        }
        //content1数据
        list.add(new ContentItem1());
        list.add(new ContentItem1());
        //title
        list.add(new TitleItem());
        //content2
        for (int i=0;i<4;i++){
            list.add(new ContentItem2());
        }
        //title
        list.add(new TitleItem());
        //content1
        for (int i=0;i<4;i++){
            list.add(new ContentItem1());
        }
        
        GridLayoutManager layoutManager = new GridLayoutManager(this, 20);
        MultiViewAdapter adapter = new MultiViewAdapter(list);
        adapter.setSpanCount(layoutManager);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);

最后的最后,我们看看效果图图

在这里插入图片描述

三,封装

一个项目中,对于多布局的使用场景不止一处。这里有个封装的很好的库,我们可以使用一下了。

  implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.2'

先来看一下实体类QuickMultipleEntity

public class QuickMultipleEntity implements MultiItemEntity {
    public static final int BANNER_TYPE = 0;
    public static final int TITLE_TYPE = 1;
    public static final int ICON_TYPE = 2;
    public static final int CONTENT1_TYPE = 4;
    public static final int CONTENT2_TYPE = 5;
    private int itemType;
    private int spanSize;

    public QuickMultipleEntity(int itemType, int spanSize) {
        this.itemType = itemType;
        this.spanSize = spanSize;
    }

    public int getSpanSize() {
        return spanSize;
    }

    public void setSpanSize(int spanSize) {
        this.spanSize = spanSize;
    }

    @Override
    public int getItemType() {
        return itemType;
    }
}

再来看看adapter

public class MultiTypeAdapter extends BaseMultiItemQuickAdapter<QuickMultipleEntity, BaseViewHolder> {
    public MultiTypeAdapter(List<QuickMultipleEntity> data) {
        super(data);
        addItemType(QuickMultipleEntity.BANNER_TYPE, R.layout.item_banner);
        addItemType(QuickMultipleEntity.TITLE_TYPE, R.layout.item_title);
        addItemType(QuickMultipleEntity.ICON_TYPE, R.layout.item_icon);
        addItemType(QuickMultipleEntity.CONTENT1_TYPE, R.layout.item_content1);
        addItemType(QuickMultipleEntity.CONTENT2_TYPE, R.layout.item_content2);
    }

    @Override
    protected void convert(BaseViewHolder baseViewHolder, QuickMultipleEntity quickMultipleEntity) {
        switch (baseViewHolder.getItemViewType()) {
            case QuickMultipleEntity.BANNER_TYPE:
                Log.e("MultiTypeAdapter", "convert: banner");
                break;
            case QuickMultipleEntity.TITLE_TYPE:
                Log.e("MultiTypeAdapter", "convert: title");
                break;
            default:
                break;
        }
    }
}

接下来就是数据源了

public class MainActivity extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private MultiTypeAdapter multipleItemAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        mRecyclerView = findViewById(R.id.activity_main_rv);

        List<QuickMultipleEntity> data = getMultipleItemData();
        multipleItemAdapter = new MultiTypeAdapter(data);
        final GridLayoutManager manager = new GridLayoutManager(this, 20);
        mRecyclerView.setLayoutManager(manager);
        multipleItemAdapter.setGridSpanSizeLookup(new GridSpanSizeLookup() {
            @Override
            public int getSpanSize(GridLayoutManager gridLayoutManager, int viewType, int position) {
                return data.get(position).getSpanSize();
            }
        });
        mRecyclerView.setAdapter(multipleItemAdapter);

    }

    public static List<QuickMultipleEntity> getMultipleItemData() {
        List<QuickMultipleEntity> list = new ArrayList<>();
        //banner
        list.add(new QuickMultipleEntity(QuickMultipleEntity.BANNER_TYPE, 20));
        //icon
        for (int i = 0; i < 10; i++) {
            list.add(new QuickMultipleEntity(QuickMultipleEntity.ICON_TYPE, 4));
        }
        //content1
        for (int i = 0; i < 2; i++) {
            list.add(new QuickMultipleEntity(QuickMultipleEntity.CONTENT1_TYPE, 10));
        }
        //title
        list.add(new QuickMultipleEntity(QuickMultipleEntity.TITLE_TYPE, 20));
        //content2
        for (int i = 0; i < 4; i++) {
            list.add(new QuickMultipleEntity(QuickMultipleEntity.CONTENT2_TYPE, 5));
        }
        //title
        list.add(new QuickMultipleEntity(QuickMultipleEntity.TITLE_TYPE, 20));
        //content1
        for (int i = 0; i < 4; i++) {
            list.add(new QuickMultipleEntity(QuickMultipleEntity.CONTENT1_TYPE, 10));
        }

        return list;
    }

}

实现效果和上图是一样的。

四,RecyclerView原理

https://blog.csdn.net/qq_29882585/article/details/108818849
https://juejin.cn/post/6844903661726859271
https://juejin.cn/post/6984974879296585764

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值