赞
踩
开发中离不开的RecyclerView,用好它让开发UI更加丝滑。通常在使用RecyclerView的时候,我们都会用到多布局,尤其是APP的首页。像那些大厂的首页,比如:京东,淘宝,美团,QQ音乐等。都不是简单的布局,有些涉及到嵌套问题。一旦使用了嵌套,会有各种问题,性能问题,滑动问题等。因此我们在开发中尽量避免多层嵌套。
当然本篇文章没有涉及到更复杂的布局使用,像列表中存在横向滑动,分页,嵌套viewpager,嵌套tablayout等,这些使用场景有同学做过优化的可以一起讨论交流。
写一个简单布局只有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);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this,RecyclerView.HORIZONTAL,false));
mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));
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;
}
}
});
如果要实现上图的效果,我们该如何去设计呢。首先需要分析上图一共有几种布局,再者由于是一维布局,我们可以采用非嵌套模式去实现。当然这里如果支持二维布局的横向滑动呀,分页滑动呀等,这种就得需要使用嵌套了,我们先不说嵌套。就是利用上文中介绍的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的该怎么办呢,这种方式就不行了。
好了,我们开始写代码吧,实现上图效果。
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>
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);
}
}
…更多省略
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)); } } }
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 } } }); } }
对于数据这一块,同学们需要理解一下,不要弄混。记着咱们的布局是一维的,也就是相当于只有一级分类。分类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; } }
实现效果和上图是一样的。
https://blog.csdn.net/qq_29882585/article/details/108818849
https://juejin.cn/post/6844903661726859271
https://juejin.cn/post/6984974879296585764
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。