【Android】RecyclerView多布局展示案例
三三要成为安卓糕手
零:需求
怎么在recycle中实现不同的item样式,如新闻列表中不同样式 item(单图、双图、三图、广告)的展示。
下面先对代码逻辑进行设计分析
其执行逻辑顺序可以分为初始化阶段、数据准备阶段、RecyclerView 渲染阶段三个核心部分
一、初始化阶段
-
**
onCreate
**方法触发
当 Activity 启动时,调用onCreate
方法- 加载布局:
activity_article_more_list)
,布局中包含RecyclerView
控件 - 初始化
RecyclerView
:通过findViewById
获取RecyclerView
实例,并为其设置布局管理器(垂直线性布局)
- 加载布局:
二、数据准备阶段(数据源创建与传递)
-
创建数据源
调用createData()
方法生成模拟数据,具体逻辑:- 最终返回包含 22 条数据的列表(20 条新闻 + 2 条广告,广告通过
add(索引, 对象)
插入到指定位置)。
- 最终返回包含 22 条数据的列表(20 条新闻 + 2 条广告,广告通过
-
初始化适配器并传递数据
- 创建
ArticleMoreAdapter
实例(适配器是RecyclerView
与数据之间的桥梁)。 - 通过
adapter.setArticles(articles)
将数据源传递给适配器,适配器内部会调用notifyDataSetChanged()
通知RecyclerView
:“数据已更新,请重新渲染”。
- 创建
三、RecyclerView 渲染阶段(核心流程)
RecyclerView
通过适配器(ArticleMoreAdapter
)完成从数据到 UI 的渲染,核心步骤由getItemCount
、getItemViewType
、onCreateViewHolder
、onBindViewHolder
四个方法协作完成,执行顺序如下:
1. getItemCount()
:确定列表长度
适配器首先调用此方法,返回articles.size()
(即 22),告诉RecyclerView
:“需要展示 22 条数据”。
2. getItemViewType(int position)
:确定每条数据的布局类型
重写adpter中的getItemViewType方法,根据不同的position决定里面到底显示成什么样
对每个位置(0 到 21),RecyclerView
会调用此方法判断该位置应使用哪种布局:
- 若
article.isIdAd()
为true
(广告),返回ITEM_TYPE_AD
。 - 若只有 1 张图片(
picture2
和picture3
为 0),返回ITEM_TYPE_ARTICLE_1
。 - 若有 2 张图片(
picture3
为 0),返回ITEM_TYPE_ARTICLE_2
。 - 若有 3 张图片,返回
ITEM_TYPE_ARTICLE_3
。
(作用:为不同数据匹配不同布局,实现多样式展示)
节点:已经完成了告诉recycleview返回一个不同的viewtype,这个viewtype是根据我们数据本身来做决定的;
现在要做的是:告诉recycelvie 怎么样根据不同的viewtype做出一些view的关联
3. onCreateViewHolder(ViewGroup parent, int viewType)
:创建布局对应的 ViewHolder
根据getItemViewType
返回值,传参给viewType
,创建对应布局的ViewHolder
(布局与控件的管理者):
- 若
viewType
为ITEM_TYPE_ARTICLE_1
:加载item_article_simple.xml
布局,创建MyViewHolder
(管理单张图片、标题、作者)。 - 若为
ITEM_TYPE_ARTICLE_2
:加载item_article_picture2.xml
,创建MyViewHolder2
(管理两张图片、标题、作者)。 - 若为
ITEM_TYPE_ARTICLE_3
:加载item_article_picture3.xml
,创建MyViewHolder3
(管理三张图片、标题、作者)。 - 若为
ITEM_TYPE_AD
:加载item_article_ad.xml
,创建AdViewHolder
(管理广告图片和关闭按钮)。
(作用:将 XML 布局转换为 Java 可操作的 View,并通过 ViewHolder 缓存控件,提升性能)
4. onBindViewHolder(ViewHolder holder, int position)
绑定数据到控件对每个位置,将数据中的内容设置到ViewHolder
缓存的控件中:
- 根据
position
获取对应Article
对象和viewType
。 - 若为
ITEM_TYPE_ARTICLE_2
:通过MyViewHolder2
的ivPicture1
、ivPicture2
设置两张图片,tvTitle
设置标题,tvAuthor
设置作者。 - 若为
ITEM_TYPE_AD
:通过AdViewHolder
的ivPicture
设置广告图片。 - 其他类型同理,完成数据与 UI 的绑定。
(作用:将数据源中的数据 “填充” 到布局控件中,最终展示到屏幕上)
我只能说非常这一集炸裂,搞了好久,终于从宏观角度把代码逻辑梳理清楚了
package com.xlong.myapplication.recyclerview;import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;import com.xlong.myapplication.R;import java.util.ArrayList;public class ArticleMoreAdapter extends RecyclerView.Adapter {public static final int ITEM_TYPE_ARTICLE_1 = 1;//对应一张图片的itemViewpublic static final int ITEM_TYPE_ARTICLE_2 = 2;//对应2张图片itemViewpublic static final int ITEM_TYPE_ARTICLE_3 = 3;//对应3张图片的itemViewpublic static final int ITEM_TYPE_AD = 4;//对应广告的itemViewprivate static final String TAG = "ArticleMoreAdapter";private ArrayList<Article> articles;//构造函数中不装东西就可以完成初始化工作了public ArticleMoreAdapter() {}public void setArticles(ArrayList<Article> articles) {this.articles = articles;//告诉adapter,关联的数据源有变化,请重新根据数据做出调整notifyDataSetChanged();Log.i(TAG, "setArticles: article.size = " + articles.size());}public ArrayList<Article> getArticles() {return articles;}/*** 数据如何与item布局做关联* 其实就是根据拿到的不同的viewType创建对应的ViewHolder(不同类型的杯子)*/@NonNull@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {//找item布局,把item转为视图,与LayoutInflater有异曲同工之处Log.i(TAG, "onCreateViewHolder: viewType = " + viewType);switch (viewType) {case ITEM_TYPE_ARTICLE_2:View view2 = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_article_picture2, parent, false);return new MyViewHolder2(view2);case ITEM_TYPE_ARTICLE_3:View view3 = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_article_picture3, parent, false);return new MyViewHolder3(view3);case ITEM_TYPE_AD:View viewAd = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_article_ad, parent, false);return new AdViewHolder(viewAd);case ITEM_TYPE_ARTICLE_1:default://查找item布局View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_article_simple, parent, false);//创建一个ViewHolder对itemView做管理MyViewHolder myViewHolder = new MyViewHolder(view);//ViewHolder和RecyclerView做关联return myViewHolder;}}/*** 有一条item就调用一次这个方法*/@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {Log.i(TAG, "onBindViewHolder: position" + position);//通过position获取对应的viewTypeint itemViewType = getItemViewType(position);Article article = articles.get(position);switch (itemViewType) {case ITEM_TYPE_ARTICLE_2:MyViewHolder2 holder2 = (MyViewHolder2) holder;holder2.ivPicture1.setImageResource(article.getPicture());holder2.ivPicture2.setImageResource(article.getPicture2());holder2.tvTitle.setText(article.getTitle());holder2.tvAuthor.setText(article.getAuthor());break;case ITEM_TYPE_ARTICLE_3:MyViewHolder3 holder3 = (MyViewHolder3) holder;holder3.ivPicture1.setImageResource(article.getPicture());holder3.ivPicture2.setImageResource(article.getPicture2());holder3.ivPicture3.setImageResource(article.getPicture3());holder3.tvTitle.setText(article.getTitle());holder3.tvAuthor.setText(article.getAuthor());break;case ITEM_TYPE_AD:AdViewHolder holderAd = (AdViewHolder) holder;holderAd.ivPicture.setImageResource(article.getAdPicture());break;case ITEM_TYPE_ARTICLE_1:default:MyViewHolder holderSimple = (MyViewHolder) holder;holderSimple.ivPicture.setImageResource(article.getPicture());holderSimple.tvTitle.setText(article.getTitle());holderSimple.tvAuthor.setText(article.getAuthor());break;}}@Overridepublic int getItemViewType(int position) {Article article = articles.get(position);if (article.isIdAd()) {return ITEM_TYPE_AD;} else if (article.getPicture2() == 0 && article.getPicture3() == 0) {return ITEM_TYPE_ARTICLE_1;} else if (article.getPicture3() == 0) {return ITEM_TYPE_ARTICLE_2;} else {return ITEM_TYPE_ARTICLE_3;}}/*** @return 告诉RecyclerView显示多少条数据*/@Overridepublic int getItemCount() {Log.i(TAG, "getItemCount: articles的数量为" + articles);return articles == null ? 0 : articles.size();}public class MyViewHolder extends RecyclerView.ViewHolder {private final ImageView ivPicture;private final TextView tvTitle;private final TextView tvAuthor;public MyViewHolder(@NonNull View itemView) {super(itemView);ivPicture = itemView.findViewById(R.id.iv_picture);tvTitle = itemView.findViewById(R.id.tv_title);tvAuthor = itemView.findViewById(R.id.tv_author);}}/*** 两张图片的ViewHolder*/public class MyViewHolder2 extends RecyclerView.ViewHolder {private final ImageView ivPicture1;private final ImageView ivPicture2;private final TextView tvTitle;private final TextView tvAuthor;public MyViewHolder2(@NonNull View itemView) {super(itemView);ivPicture1 = itemView.findViewById(R.id.iv_picture1);ivPicture2 = itemView.findViewById(R.id.iv_picture2);tvTitle = itemView.findViewById(R.id.tv_title);tvAuthor = itemView.findViewById(R.id.tv_author);}}public class MyViewHolder3 extends RecyclerView.ViewHolder {private final ImageView ivPicture1;private final ImageView ivPicture2;private final ImageView ivPicture3;private final TextView tvTitle;private final TextView tvAuthor;public MyViewHolder3(@NonNull View itemView) {super(itemView);ivPicture1 = itemView.findViewById(R.id.iv_picture1);ivPicture2 = itemView.findViewById(R.id.iv_picture2);ivPicture3 = itemView.findViewById(R.id.iv_picture3);tvTitle = itemView.findViewById(R.id.tv_title);tvAuthor = itemView.findViewById(R.id.tv_author);}}public class AdViewHolder extends RecyclerView.ViewHolder {private final ImageView ivPicture;private final View ivClose;public AdViewHolder(@NonNull View itemView) {super(itemView);ivPicture = itemView.findViewById(R.id.iv_picture);ivClose = itemView.findViewById(R.id.tv_close);}}}
package com.xlong.myapplication.recyclerview;import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import com.xlong.myapplication.R;import java.util.ArrayList;
/*** 实现不同的RecyclerView item样式*/
public class ArticleMoreListActivity extends AppCompatActivity{private static final String TAG = "ArticleMoreListActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_article_more_list);//1:找到循环视图RecyclerView recyclerView = findViewById(R.id.recycler_view);//2:一维列表(竖向)LinearLayoutManager layoutManager = new LinearLayoutManager(this);//3:为RecyclerView添加布局管理器recyclerView.setLayoutManager(layoutManager);//设置适配器ArticleMoreAdapter adapter = new ArticleMoreAdapter();//把数据先传给适配器ArrayList<Article> articles = createData();adapter.setArticles(articles);Log.i(TAG, "onCreate: article size =" + articles.size());//添加适配器recyclerView.setAdapter(adapter);}/*** 自己造一点数据给RecyclerView** @return 返回一个存放20条数据的新闻列表*/private ArrayList<Article> createData() {ArrayList<Article> articles = new ArrayList<>();articles.add(new Article("辽宁清原抽水蓄能电站最后一台定子吊装完成","人民网-图片频道,供稿:人民资讯", R.drawable.ic_article_1));articles.add(new Article("通行时间再缩短!广湛高铁佛山站建设正式启动","人民网-广东频道,供稿:人民资讯", R.drawable.ic_article_2));articles.add(new Article("19岁杭州帅小伙第一名,还有人抓到一只甲鱼!今早3000多人在钱塘江边,太欢乐了","杭州网", R.drawable.ic_article_3));//两张照片的Article article4 = new Article("今日起“两江小渡”航线恢复正常运营","上游新闻", R.drawable.ic_article_4);article4.setPicture2(R.drawable.ic_article_12);articles.add(article4);articles.add(new Article("中国太保小排球夏令营在漳州火热启航","国际时事讲解", R.drawable.ic_article_5));articles.add(new Article("奋力推动民族地区高质量发展和现代化建设迈出新步伐","当代先锋网", R.drawable.ic_article_6));//两张照片的Article article7 = new Article("相信青春的力量|写在我省2024年万名大学生志愿服务西部计划出征之际","人民网-图片频道,供稿:人民资讯", R.drawable.ic_article_7);article7.setPicture2(R.drawable.ic_article_5);articles.add(article7);articles.add(new Article("量身定制“海外订单” 贵州汽车制造开拓国际市场","当代先锋网", R.drawable.ic_article_8));articles.add(new Article("势不可挡……在这里,乡村振兴的新引擎正发出强劲动力","鲁网", R.drawable.ic_article_9));articles.add(new Article("菏泽职业学院召开2024届毕业生第二轮就业核查工作部署会","大众报业·齐鲁壹点", R.drawable.ic_article_10));articles.add(new Article("菏泽职业学院学前教育系(育中教育学院)斩获省级大赛一等奖","西安快线", R.drawable.ic_article_11));//三张照片的Article article12 = new Article("菏泽职业学院一类教学大赛,二等奖+2!","大众报业·齐鲁壹点", R.drawable.ic_article_12);article12.setPicture2(R.drawable.ic_article_3);article12.setPicture3(R.drawable.ic_article_16);articles.add(article12);articles.add(new Article("中央气象台停止对“格美”编号 湖南辽宁吉林等地仍有强降雨","国际时事讲解", R.drawable.ic_article_13));articles.add(new Article("习水仙源镇:清凉山水引客来 消夏避暑人气足","多彩贵州网", R.drawable.ic_article_14));articles.add(new Article("习水县寨坝镇:清凉避暑地 康养第二乡","多彩贵州网", R.drawable.ic_article_15));articles.add(new Article("赤水市元厚镇:新品荔枝出山记","人民网-图片频道,供稿:人民资讯", R.drawable.ic_article_16));articles.add(new Article("习水三岔河镇小小错车道 畅通出行暖民心","湖南日报", R.drawable.ic_article_17));articles.add(new Article("习水土城镇社会实践活动丰富学生假期生活","上海咨询", R.drawable.ic_article_18));articles.add(new Article("赤水建强“小站点”释放乡村振兴新动能","福建新闻网,供稿:人民资讯", R.drawable.ic_article_19));articles.add(new Article("习水同民镇:火龙果喜丰收 果农笑开颜","上游新闻,供稿:人民资讯", R.drawable.ic_article_20));//两张广告articles.add(5, new Article(true, R.drawable.bg_ad1));articles.add(10, new Article(true, R.drawable.bg_ad2));return articles;}}
总结:整体执行流程
-
ArticleMoreListActivity
启动 →onCreate
初始化页面和RecyclerView
。 -
调用
createData
生成 22 条模拟数据(含新闻和广告)。 -
适配器接收数据并通知
RecyclerView
更新。 -
RecyclerView
依次调用:-
getItemCount
→ 确定展示 22 条数据。 -
对每个位置(0-21):
getItemViewType
→ 判断布局类型。onCreateViewHolder
→ 创建对应布局的ViewHolder
。onBindViewHolder
→ 将数据绑定到控件,完成渲染。
-