用 GridLayoutManager 构建 RecyclerView 网格列表:cw-omnibus Grid 示例源码全解析 示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载Grid是 cw-omnibus 仓库《The Busy Coders Guide to Android Development》合订版配套示例代码中专门演示RecyclerView与GridLayoutManager组合用法的完整示例工程。本文以 RecyclerView/Grid/README.markdown 为核心主线结合其源码逐层剖析如何用 2 列网格展示列表数据覆盖布局管理器配置、程序化构建RecyclerView的基类设计、ViewHolder与点击事件、Material 主题适配等完整链路。读完本文你将掌握GridLayoutManager的核心配置方法、网格项复用与点击处理的标准写法并能顺藤摸瓜看懂仓库中更复杂的网格变体如不等宽网格。示例定位仓库中的最小网格演示该示例的官方描述非常凝练——This sample Android app demonstrates aRecyclerViewusing aGridLayoutManager.即一个用GridLayoutManager驱动RecyclerView的最小可运行示例被收录在书中 RecyclerView 章节 之下。在仓库目录 RecyclerView/ 的 20 余个相关示例中Grid是与 SimpleListLinearLayoutManager线性列表互为对照的网格形态其余如VideoTable则是在此基础上的高级变体。整个工程仅有一个app模块源码结构如下文件职责MainActivity.java配置GridLayoutManager并装配数据适配器RecyclerViewActivity.java程序化创建RecyclerView的基类RowController.java网格行的ViewHolder负责数据绑定与点击row.xml单行网格项的CardView布局AndroidManifest.xml应用清单与主题声明GridLayoutManager一行代码把列表变成网格GridLayoutManager是RecyclerView.LayoutManager的标准子类与LinearLayoutManager的差异仅在于它把可滚动方向默认垂直上的空间按列或按行切分让每个条目占据固定的span。本示例只用一个构造参数就完成了网格化public class MainActivity extends RecyclerViewActivity { private static final String[] items{lorem, ipsum, dolor, sit, amet, consectetuer, adipiscing, elit, morbi, vel, ligula, vitae, arcu, aliquet, mollis, etiam, vel, erat, placerat, ante, porttitor, sodales, pellentesque, augue, purus}; Override public void onCreate(Bundle state) { super.onCreate(state); setLayoutManager(new GridLayoutManager(this, 2)); setAdapter(new IconicAdapter()); } // ... }关键点位于 MainActivity.java 的两行调用new GridLayoutManager(this, 2)第二参数spanCount 2即水平方向固定切分为 2 列条目按从左到右、从上到下的顺序填充。这是最常见的使用形态——只需替换LinearLayoutManager即可获得网格适配器与ViewHolder完全无需改动数据源是一个 25 个拉丁单词组成的String[]恰好构成非整行的条目数25 无法被 2 整除可在运行效果中直观看到最后一行的不满列排布验证网格自动换行与空白留位逻辑。GridLayoutManager还支持方向参数如new GridLayoutManager(this, 2, LinearLayoutManager.HORIZONTAL, false)实现横向网格、setReverseLayout()反向排布以及通过setSpanSizeLookup()让条目跨越多列——后者的实战用法会在本文末尾结合仓库中的VideoTable示例展开。基类 RecyclerViewActivity代码级构建 RecyclerView示例刻意没有使用 XML 布局文件来承载RecyclerView而是在基类 RecyclerViewActivity.java 中完全以代码构建控件这正是理解RecyclerView三个核心协作对象Adapter、LayoutManager、ViewHolder的最小骨架public class RecyclerViewActivity extends Activity { private RecyclerView rvnull; public void setAdapter(RecyclerView.Adapter adapter) { getRecyclerView().setAdapter(adapter); } public RecyclerView.Adapter getAdapter() { return(getRecyclerView().getAdapter()); } public void setLayoutManager(RecyclerView.LayoutManager mgr) { getRecyclerView().setLayoutManager(mgr); } public RecyclerView getRecyclerView() { if (rvnull) { rvnew RecyclerView(this); rv.setHasFixedSize(true); setContentView(rv); } return(rv); } }值得注意的实现细节懒加载 单例getRecyclerView()首次调用时才创建RecyclerView实例并setContentView(rv)之后复用。子类MainActivity只需调用setLayoutManager()/setAdapter()即可完成装配无需关心控件创建顺序setHasFixedSize(true)向RecyclerView声明条目尺寸不随内容变化或已知是固定的从而跳过不必要的重新测量是提升滚动性能的常规优化手段。该标志的语义是只要数据条目的宽高恒定网格布局的测量结果即可被安全缓存复用基类复用同样结构的RecyclerViewActivity也出现在 SimpleList 示例 中说明该基类是仓库内多组RecyclerView示例共用的惯用封装。网格行布局CardView 卡片 水波纹反馈单行条目布局 row.xml 决定了网格中每个格子呈现的视觉形态android.support.v7.widget.CardView xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:cardviewhttp://schemas.android.com/apk/res-auto android:layout_widthmatch_parent android:layout_heightwrap_content android:layout_margin4dp cardview:cardCornerRadius4dp LinearLayout android:idid/row_content android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationhorizontal android:background?android:attr/selectableItemBackground ImageView android:idid/icon android:layout_widthwrap_content android:layout_heightwrap_content android:layout_gravitycenter_vertical android:padding2dip android:srcdrawable/ok android:contentDescriptionstring/icon/ LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationvertical TextView android:idid/label android:layout_widthwrap_content android:layout_heightwrap_content android:textSize25sp android:textStylebold/ TextView android:idid/size android:layout_widthwrap_content android:layout_heightwrap_content android:textSize15sp/ /LinearLayout /LinearLayout /android.support.v7.widget.CardView布局要点解读根节点使用android.support.v7.widget.CardViewv7 兼容包中的卡片控件cardview:cardCornerRadius4dp设置 4dp 圆角android:layout_margin4dp为格子之间留出 4dp 间距——在网格场景中间距直接构成了格子与格子之间的视觉分割线内部LinearLayoutid 为row_content背景设为?android:attr/selectableItemBackground这是 Material 规范中可点击区域的标准水波纹Ripple背景资源让整行在点击时呈现按压反馈内容区由一个图标ImageViewdrawable/ok与垂直排布的label25sp 加粗、size15sp两个TextView组成构成图标 标题 副标题的经典列表项结构。RowControllerViewHolder 的数据绑定与点击处理RowController.java 继承RecyclerView.ViewHolder并实现View.OnClickListener负责两件事把数据绑定到row.xml中的视图以及处理整行的点击事件class RowController extends RecyclerView.ViewHolder implements View.OnClickListener { TextView labelnull; TextView sizenull; ImageView iconnull; String templatenull; RowController(View row) { super(row); label(TextView)row.findViewById(R.id.label); size(TextView)row.findViewById(R.id.size); icon(ImageView)row.findViewById(R.id.icon); templatesize.getContext().getString(R.string.size_template); row.setOnClickListener(this); if (Build.VERSION.SDK_INT Build.VERSION_CODES.LOLLIPOP) { row.setOnTouchListener(new View.OnTouchListener() { TargetApi(Build.VERSION_CODES.LOLLIPOP) Override public boolean onTouch(View v, MotionEvent event) { v .findViewById(R.id.row_content) .getBackground() .setHotspot(event.getX(), event.getY()); return(false); } }); } } Override public void onClick(View v) { Toast.makeText(v.getContext(), String.format(Clicked on position %d, getAdapterPosition()), Toast.LENGTH_SHORT).show(); } void bindModel(String item) { label.setText(item); size.setText(String.format(template, item.length())); if (item.length()4) { icon.setImageResource(R.drawable.delete); } else { icon.setImageResource(R.drawable.ok); } } }核心逻辑解析构造时一次性地 findViewByIdlabel、size、icon三个视图引用在构造器内一次性解析并缓存这正是ViewHolder模式降低findViewById开销、提升滚动流畅度的核心思想同时从strings.xml读取size_templateSize: %d见 strings.xml作为格式化模板点击事件整行row.setOnClickListener(this)注册监听回调中使用getAdapterPosition()获取当前条目在适配器中的位置并弹出 Toast——注意必须使用getAdapterPosition()而非缓存位置因为网格滚动复用ViewHolder后位置可能已发生变化Lollipop 水波纹热点跟随Android 5.0 及以上时通过setOnTouchListener在触摸事件中把row_content背景selectableItemBackground即 RippleDrawable的hotspot设置为手指触点坐标实现波纹从按下处扩散的 Material 动效。setHotspot(x, y)的坐标是基于该视图自身的坐标系统因此直接用event.getX()/getY()即可bindModel()数据绑定主标题显示单词本身副标题按模板显示单词长度并根据长度是否超过 4 个字符切换图标delete.png或ok.png位于 drawable-mdpi让网格内容随数据产生直观的视觉差异。适配器网格数据从数组到 ViewHolder 的流动MainActivity内部类IconicAdapter实现了RecyclerView.AdapterRowController完整覆盖了适配器的三个抽象方法class IconicAdapter extends RecyclerView.AdapterRowController { Override public RowController onCreateViewHolder(ViewGroup parent, int viewType) { return(new RowController(getLayoutInflater() .inflate(R.layout.row, parent, false))); } Override public void onBindViewHolder(RowController holder, int position) { holder.bindModel(items[position]); } Override public int getItemCount() { return(items.length); } }onCreateViewHolder在需要新条目时创建RowController通过inflate(R.layout.row, parent, false)把row.xml实例化false表示不立即挂载到 parent由LayoutManager负责后续附加并将parent作为布局参数来源——这保证了网格项能正确继承CardView的宽度约束onBindViewHolder复用场景下把position对应的数据items[position]交给holder.bindModel()填充getItemCount返回数据总量25RecyclerView据此与LayoutManager协同完成滚动计算。值得注意的是示例没有重写getItemViewType()意味着所有条目共用同一种布局——这也是 2 列网格最简形态。若需要表头、首列、正文等多种格子类型可重写getItemViewType()配合onCreateViewHolder分支创建仓库中的VideoTable示例正是这种多类型网格的进阶参照。主题与 Material 色彩values-v21 的分版本适配应用主题采用基础版本 v21 覆盖的双层结构体现了 Android 多版本资源分发的标准做法values/styles.xml 中Theme.Apptheme继承Theme.Holo.Light.DarkActionBar作为低版本API 14的兜底主题values-v21/styles.xml 中同一主题名切换为继承android:Theme.Material.Light.DarkActionBar并显式指定 Material 配色style nameTheme.Apptheme parentandroid:Theme.Material.Light.DarkActionBar item nameandroid:colorPrimarycolor/primary/item item nameandroid:colorPrimaryDarkcolor/primary_dark/item item nameandroid:colorAccentcolor/accent/item /style对应的 colors.xml 中定义了 Material Design 主色调primary #3f51b5靛蓝、primary_dark #1a237e深靛蓝用于状态栏、accent #ffee58黄用于强调色。这样同一个主题名在不同系统版本上自动解析为不同的父主题与配色而清单文件 AndroidManifest.xml 中android:themestyle/Theme.Apptheme只需声明一次。构建配置与运行方式工程采用 Gradle 构建settings.gradle 声明include :appGradle Wrapper 由 gradle-wrapper.properties 指向 Gradle 4.4。运行前需注意导入方式可直接将整个RecyclerView/Grid目录作为工程导入 Android Studio。根据仓库根目录 README.markdown 的说明若本机 SDK 与project.properties中声明的sdk-folder/opt/android-sdk-linux_x86-1.1_r1、targetandroid-19不符需在导入时调整 SDK 路径与编译目标安全提示仓库根 README 特别强调——导入任何项目前务必检查gradle-wrapper.properties以防第三方发布者捆绑被篡改的 Gradle 安装命令行构建仓库出于安全原因不包含gradlew脚本及对应 JAR命令行构建需自行安装与distributionUrl匹配的 Gradle 版本当前为 4.4后执行gradle assembleDebug代码混淆proguard.cfg 提供了标准的 Activity/Service/BroadcastReceiver 等四大组件及 Parcelable 的 keep 规则可在 release 构建时启用。进阶对照SpanSizeLookup 与不等宽网格GridLayoutManager的威力不止于等宽网格。仓库中的 VideoTable 示例 展示了权重列玩法它定义了COLUMN_WEIGHTS{1, 4, 1}并通过自实现的 ColumnWeightSpanSizeLookup.java 让不同位置的条目占据不同数量的 span缩略图占 1/6 宽、标题占 4/6 宽、时长占 1/6 宽Override public int getSpanSize(int position) { return(columnWeights[position % columnWeights.length]); }配合getTotalSpans()返回权重总和6作为GridLayoutManager的总列数以及适配器中getItemViewType()按position % 3区分三类布局即可实现类似表格的多栏网格。这正是从Grid这个最小示例出发、在同一仓库内继续深造GridLayoutManager高级用法的理想路线。小结RecyclerView/Grid示例以极少的代码量完整呈现了RecyclerView GridLayoutManager的标准实现范式程序化构建的基类RecyclerViewActivity、两行核心装配调用、ViewHolder模式的RowController、CardView卡片布局与 Material 水波纹反馈以及按系统版本分层的主题适配。它既是书中RecyclerView章节的配套练习也是 cw-omnibus 仓库中二十余个RecyclerView变体示例的共同起点——掌握了它再去看VideoTable的不等宽网格、ChoiceList的选择模式乃至SortedList的数据变更机制都会更加顺理成章。赞分享示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载相关推荐Microsoft Graph 集合子集建模模式Modeling Collection Subsets实战指南Microsoft Graph 集合子集建模模式Modeling Collection Subsets实战指南 本文导读 本文讲解 Microsoft G示例工程教程cw-omnibus 示例精读用 RecyclerView 实现单选项列表SingleActivatedListcw omnibus 示例精读用 RecyclerView 实现单选项列表SingleActivatedList cw omnibus 是《The Bus示例工程教程ERPNext Project 文档类型深度解析预算跟踪、物料消耗与任务甘特图实战指南ERPNext Project 文档类型深度解析预算跟踪、物料消耗与任务甘特图实战指南 本指南以 ERPNext 开源仓库中 Project 帮助文档 htt示例工程教程上一篇PRQL 编译器测试体系全解析从单元快照测试到真实数据库集成测试下一篇Apache Pulsar 连接器调试实战localrun 与集群模式下的日志与 admin CLI 排查指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考