资讯动态

ListView 与各种 Adapter 的使用:从 BaseAdapter 到 RecyclerView.Adapter 的配置骨架与验证

发布时间:2026/9/29 23:03:01 来源:尧图企业网站定制
1. ListView 与 Adapter 到底解决什么问题ListView 是 Android 里最经典的列表控件它本身只负责“滚动容器”这一件事真正决定每一行长什么样、数据从哪来、点击后干什么的是 Adapter。你可以把 ListView 想成一个空的书架Adapter 就是往书架上摆书的规则ArrayAdapter 摆的是纯字符串SimpleAdapter 摆的是“图片标题副标题”这种固定结构BaseAdapter 则允许你完全自定义每一行的布局和绑定逻辑。而 RecyclerView.Adapter 是后来者它把“复用 View”这件事做得更彻底强制用 ViewHolder 模式适合长列表和复杂 item。这篇面向的是刚接触 Android 列表、或者从老项目迁移到 RecyclerView 的开发者。我会把 BaseAdapter、ArrayAdapter、SimpleAdapter、RecyclerView.Adapter 四种写法的骨架代码、布局文件、数据绑定配置都摆出来每一步都能直接复制进 Android Studio 跑起来。过程中如果遇到编译报错、数据不刷新、点击无响应第 5 节有对应的排查清单。另外如果你在写这些 Adapter 时需要快速验证某段逻辑或让模型帮你补全模板可以顺手用 TaoToken 的模型对话能力做辅助后面第 2 节会讲怎么配。先明确一个判断标准item 结构固定且简单用 ArrayAdapter 或 SimpleAdapter 最省事item 里有多个控件、需要动态改状态用 BaseAdapter 或 RecyclerView.Adapter列表可能很长、要频繁刷新优先 RecyclerView。下面按这个顺序逐个给骨架。2. TaoToken 前置拿 Key 与接入文档在写 Adapter 的过程中经常需要让模型帮忙生成 ViewHolder 模板、检查 getView 里的复用逻辑或者把一段 Java 转成 Kotlin。TaoToken 提供统一的模型调用入口你只需要一个 API Key 就能在代码里发起对话请求。第一步打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册并登录。第二步进入控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建 API Key建议按项目命名比如 android-list-demo方便后续区分。第三步如果你打算在 IDE 里长期用模型辅助写 Adapter 和 Agent 逻辑可以看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合高频编码场景。拿到 Key 之后接口地址用 https://taotoken.net/api 不要带 UTM 参数。请求时把 Key 放在 Authorization 头里格式是 Bearer 你的Key。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的请求体和返回结构说明。如果你用的是 Claude Code 这类工具参考 https://taotoken.net/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentClaudeCodeAnthropicutm_campaignrewrite 里的配置方式。注意API Key 不要硬编码进 Adapter 或 Activity 源码里更不要提交到 Git。建议放在 local.properties 或环境变量中通过 BuildConfig 注入。3. 四种 Adapter 的可复制配置骨架3.1 ArrayAdapter纯字符串列表最快写法ArrayAdapter 适合 item 只有一个 TextView 的场景。布局文件 activity_main.xml 里放一个 ListView 和一个空视图?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:orientationvertical android:layout_widthmatch_parent android:layout_heightmatch_parent ListView android:idid/list_view android:layout_widthmatch_parent android:layout_heightwrap_content / TextView android:idid/empty_view android:layout_widthmatch_parent android:layout_heightwrap_content android:gravitycenter android:text暂无数据 / /LinearLayoutActivity 里绑定数据注意 setEmptyView 要在 setAdapter 之前或之后都可以但 ListView 必须已经 findViewById 完成public class MainActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView listView findViewById(R.id.list_view); listView.setEmptyView(findViewById(R.id.empty_view)); String[] data {Android, Java, Kotlin, RecyclerView}; ArrayAdapterString adapter new ArrayAdapter( this, android.R.layout.simple_list_item_1, data); listView.setAdapter(adapter); } }这里用的是系统自带的 simple_list_item_1省去自己写 item 布局。实测下来数据量在几百条以内、item 无复杂样式时这个写法最省事。3.2 SimpleAdapter图片加两行文字的固定结构SimpleAdapter 的数据源是 ListMapString, Object适合“左图右文”或“上图下文”的固定 item。先写 item 布局 row.xml?xml version1.0 encodingutf-8? RelativeLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightwrap_content android:padding12dp ImageView android:idid/item_image android:layout_width48dp android:layout_height48dp android:layout_alignParentRighttrue / TextView android:idid/item_title android:layout_widthmatch_parent android:layout_heightwrap_content android:textSize18sp / TextView android:idid/item_text android:layout_widthmatch_parent android:layout_heightwrap_content android:layout_belowid/item_title / /RelativeLayoutActivity 里构造数据并绑定from 数组和 to 数组的顺序必须一一对应ListView listView findViewById(R.id.list_view); ArrayListHashMapString, Object items new ArrayList(); int[] icons {android.R.drawable.ic_menu_add, android.R.drawable.ic_menu_delete}; for (int i 0; i icons.length; i) { HashMapString, Object map new HashMap(); map.put(image, icons[i]); map.put(title, 标题 i); map.put(desc, 描述内容 i); items.add(map); } SimpleAdapter adapter new SimpleAdapter( this, items, R.layout.row, new String[]{image, title, desc}, new int[]{R.id.item_image, R.id.item_title, R.id.item_text}); listView.setAdapter(adapter);踩过的坑from 里的 key 写错不会编译报错只会运行时该控件空白所以命名最好和 to 里的控件 id 保持语义一致。3.3 BaseAdapter完全自定义 item 与 ViewHolder 复用BaseAdapter 是 ListView 体系里最灵活的需要自己实现 getCount、getItem、getItemId、getView 四个方法。关键在 getView 里做 ViewHolder 复用否则滑动会卡。先定义 item 布局 item_custom.xml包含一个 ImageView 和两个 TextView这里省略重复的 XML结构同 3.2 的 row.xml。Adapter 骨架public class CustomAdapter extends BaseAdapter { private final Context context; private final ListItemBean data; public CustomAdapter(Context context, ListItemBean data) { this.context context; this.data data; } Override public int getCount() { return data.size(); } Override public Object getItem(int position) { return data.get(position); } Override public long getItemId(int position) { return position; } Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView null) { convertView LayoutInflater.from(context) .inflate(R.layout.item_custom, parent, false); holder new ViewHolder(); holder.image convertView.findViewById(R.id.item_image); holder.title convertView.findViewById(R.id.item_title); holder.desc convertView.findViewById(R.id.item_text); convertView.setTag(holder); } else { holder (ViewHolder) convertView.getTag(); } ItemBean bean data.get(position); holder.title.setText(bean.title); holder.desc.setText(bean.desc); holder.image.setImageResource(bean.iconRes); return convertView; } static class ViewHolder { ImageView image; TextView title; TextView desc; } }ItemBean 就是一个普通数据类含 title、desc、iconRes 三个字段。绑定到 ListView 时直接 new CustomAdapter(this, list) 再 setAdapter 即可。数据刷新调用 adapter.notifyDataSetChanged()。3.4 RecyclerView.Adapter强制 ViewHolder 的现代写法RecyclerView 需要先在 build.gradle 里加依赖implementation androidx.recyclerview:recyclerview:1.3.2布局文件把 ListView 换成 RecyclerViewandroidx.recyclerview.widget.RecyclerView android:idid/recycler_view android:layout_widthmatch_parent android:layout_heightmatch_parent /Adapter 骨架注意 onCreateViewHolder 里 inflate 时必须传 parent 和 falsepublic class MyRecyclerAdapter extends RecyclerView.AdapterMyRecyclerAdapter.VH { private final ListItemBean data; private OnItemClickListener listener; public interface OnItemClickListener { void onItemClick(int position); } public MyRecyclerAdapter(ListItemBean data) { this.data data; } public void setOnItemClickListener(OnItemClickListener l) { this.listener l; } NonNull Override public VH onCreateViewHolder(NonNull ViewGroup parent, int viewType) { View view LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_custom, parent, false); return new VH(view); } Override public void onBindViewHolder(NonNull VH holder, int position) { ItemBean bean data.get(position); holder.title.setText(bean.title); holder.desc.setText(bean.desc); holder.image.setImageResource(bean.iconRes); holder.itemView.setOnClickListener(v - { if (listener ! null) listener.onItemClick(holder.getAdapterPosition()); }); } Override public int getItemCount() { return data.size(); } static class VH extends RecyclerView.ViewHolder { ImageView image; TextView title, desc; VH(NonNull View itemView) { super(itemView); image itemView.findViewById(R.id.item_image); title itemView.findViewById(R.id.item_title); desc itemView.findViewById(R.id.item_text); } } }Activity 里设置 LayoutManager否则列表不会显示RecyclerView recyclerView findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); MyRecyclerAdapter adapter new MyRecyclerAdapter(dataList); adapter.setOnItemClickListener(position - { Toast.makeText(this, 点击 position, Toast.LENGTH_SHORT).show(); }); recyclerView.setAdapter(adapter);刷新数据时如果整个列表换了用 notifyDataSetChanged如果只是改某一行用 notifyItemChanged(position) 更高效。4. 验证请求与成功结果写完 Adapter 后怎么确认它真的在工作分三步验证。第一步编译运行。如果 item 布局里引用了不存在的 id编译期不会报错但运行时会抛 NullPointerException日志里会指出具体是哪个 findViewById 返回了 null。所以先看 Logcat 有没有 NPE。第二步检查列表是否显示。如果 ListView 一片空白先确认 setAdapter 被调用了再确认 getCount 返回值大于 0。可以在 getCount 里打一行 Log看它有没有被调用。第三步验证点击和刷新。点击 item 应该弹出 Toast 或跳转调用 notifyDataSetChanged 后列表内容应该变化。如果点击无反应检查 setOnItemClickListener 是否在 setAdapter 之后设置RecyclerView 则要确认点击事件是绑在 itemView 上而不是整个 RecyclerView 上。如果你在验证过程中需要让模型帮你分析一段报错日志可以用模型对话 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 把日志贴进去让它定位是哪一行 Adapter 代码的问题。接口地址统一用 https://taotoken.net/api 请求头带 Authorization: Bearer 你的Key。5. 本篇常见错排查报错一ListView 不显示数据。最常见原因是 setAdapter 之前没有 findViewById 到 ListView或者 getCount 返回 0。另一个隐蔽原因是 ListView 的 layout_height 设成了 wrap_content 且父容器没有约束导致高度为 0。改成 match_parent 或固定高度。报错二SimpleAdapter 某一行控件空白。检查 from 数组里的 key 和 map.put 的 key 是否完全一致大小写敏感。再检查 to 数组里的控件 id 是否存在于 item 布局中。报错三BaseAdapter 滑动卡顿。九成是 getView 里没有复用 convertView每次滑动都 inflate 新 View。确认 if (convertView null) 分支里才 inflateelse 分支直接取 holder。报错四RecyclerView 不显示。忘记 setLayoutManager 是最常见原因没有 LayoutManager 的 RecyclerView 不会布局任何 item。其次是 onCreateViewHolder 里 inflate 时传了 null 作为 parent导致 item 宽度异常。报错五notifyDataSetChanged 后列表没变。确认你修改的是 Adapter 持有的同一个 List 对象。如果重新赋值了一个新 ListAdapter 里还是旧引用需要调用 Adapter 的更新方法或重新 setAdapter。报错六点击事件错位。RecyclerView 里用 getAdapterPosition() 而不是把 position 存成 final 变量否则复用后 position 会错。如果返回 NO_POSITION说明 item 已被移除。6. 继续用模型辅助写 Adapter四种 Adapter 的骨架到这里就齐了。实际项目里更常见的是组合场景RecyclerView 里嵌套多种 viewType、BaseAdapter 配合 Cursor 做数据库列表、或者把老 ListView 代码迁移到 RecyclerView。这些场景下让模型帮你生成初始模板、检查 ViewHolder 复用逻辑比手写快很多。需要长期在 IDE 里做编码辅助的走 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 只是临时验证一段 Adapter 逻辑或让模型解释报错用模型对话即可。API Key 在控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 管理接入细节看文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。接口地址始终是 https://taotoken.net/api 请求时带上 Bearer 头就能调通。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑