拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Android 基础知识4-3.11 Adapter(适配器)详解:BaseAdapter/ArrayAdapter/SimpleAdapter 配置骨架与验证

Android 基础知识4-3.11 Adapter(适配器)详解:BaseAdapter/ArrayAdapter/SimpleAdapter 配置骨架与验证 1. 为什么 Adapter 是 Android 列表控件的“翻译官”如果你刚开始写 AndroidListView 或 GridView 里那一串数据是怎么“贴”到界面上的大概率是你第一个卡住的地方。Adapter 就是干这个的它把后端数据数组、List、Cursor翻译成前端能显示的 View。没有 AdapterListView 就是一个空壳子你往里塞再多数据它也渲染不出来。这篇聚焦三种最常被问到的适配器ArrayAdapter、SimpleAdapter、BaseAdapter。我会给出可以直接复制到 Android Studio 里跑的骨架代码包括布局绑定、数据映射、运行验证的完整动作。适合已经会建 Activity、能看懂 XML 布局但对“数据怎么变成列表项”还没打通的人。看完你至少能分清什么时候三行代码用 ArrayAdapter 就够了什么时候必须自己继承 BaseAdapter 重写 getView。先说结论选型其实不复杂。纯文字一行、数据是 String 数组或 List用 ArrayAdapter每行有图片加两三行文字、数据是 List用 SimpleAdapter每行要放按钮、要处理点击、要做 ViewHolder 复用优化老老实实继承 BaseAdapter。下面按这个顺序逐个拆。2. 前置准备TaoToken 与 Android Studio 环境代码本身不依赖任何第三方 SDK但如果你想在写 Adapter 卡住时快速验证一段模型生成的适配器代码逻辑或者让模型帮你解释 getView 里 convertView 的复用机制可以先把 TaoToken 的 API Key 配好。它在这里的角色是“随叫随到的代码解释器”不是替代 Android Studio。官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 地址不带 UTMhttps://taotoken.net/api拿 Key 的动作进 console 页面创建 API Keys复制出来存到本地环境变量里别硬编码进 Android 项目。模型对话入口可以用来问“BaseAdapter 的 getItem 返回 null 会怎样”这类具体问题如果你在长期做 Android 编码或 Agent 类项目Coding Plan 更适合持续调用。Android Studio 这边你需要确认三件事新建一个 Empty Views Activity 项目res/layout 下能正常建 XML模拟器或真机能跑起来。版本上本文代码用 AppCompatActivity 和 ListView不涉及 Compose所以从 API 21 到最新都能跑。下面所有代码我都实测过直接贴进对应文件即可。3. 三种 Adapter 的可复制配置骨架3.1 ArrayAdapter一行文字的最小闭环ArrayAdapter 是最省事的。它的构造需要三个参数Context、每一行的布局资源 ID、数据源。系统自带的android.R.layout.simple_list_item_1就是“只显示一行文字”的布局直接拿来用。先写布局 activity_array.xml?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical ListView android:idid/list_array android:layout_widthmatch_parent android:layout_heightmatch_parent / /LinearLayout再写 Activitypublic class ArrayAdapterActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_array); ListView listView findViewById(R.id.list_array); String[] strs {第一项, 第二项, 第三项, 第四项, 第五项}; ArrayAdapterString adapter new ArrayAdapter( this, android.R.layout.simple_list_item_1, strs); listView.setAdapter(adapter); } }关键点第三个参数传数组或 List 都行泛型写 String。如果你传的是 List记得用new ArrayList()初始化别传 null否则 getCount 直接崩。运行后你会看到五行文字点一下有系统默认的按压反馈。3.2 SimpleAdapter图片加多行文字的映射SimpleAdapter 的核心是“键值对映射”。数据是ListMapString, Object每个 Map 代表一行然后你告诉它Map 里哪个 key 对应布局里哪个 View 的 id。先写行布局 list_item.xml?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationhorizontal android:padding8dp ImageView android:idid/img_tou android:layout_width64dp android:layout_height64dp / LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationvertical android:paddingLeft8dp TextView android:idid/tv_name android:layout_widthwrap_content android:layout_heightwrap_content android:textSize20sp android:textColor#1D1D1C / TextView android:idid/tv_says android:layout_widthwrap_content android:layout_heightwrap_content android:textSize14sp android:textColor#B4B4B9 / /LinearLayout /LinearLayoutActivity 里组装数据并绑定public class SimpleAdapterActivity extends AppCompatActivity { private String[] names {B神, 基神, 曹神}; private String[] says {无形被黑最为致命, 大神好厉害~, 我将带头日狗~}; private int[] imgIds {R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher}; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_simple); ListMapString, Object listItem new ArrayList(); for (int i 0; i names.length; i) { MapString, Object item new HashMap(); item.put(touxiang, imgIds[i]); item.put(name, names[i]); item.put(says, says[i]); listItem.add(item); } SimpleAdapter adapter new SimpleAdapter( this, listItem, R.layout.list_item, new String[]{touxiang, name, says}, new int[]{R.id.img_tou, R.id.tv_name, R.id.tv_says}); ListView listView findViewById(R.id.list_simple); listView.setAdapter(adapter); } }注意 from 数组和 to 数组必须一一对应、长度一致。图片这里我用的是 mipmap 里的启动图标你换成自己的 drawable 资源即可。SimpleAdapter 对 ImageView 的处理是 setImageResource所以传 int 资源 ID别传 Bitmap。3.3 BaseAdapter带按钮和 ViewHolder 的完整骨架当每行需要放按钮、要单独响应点击时SimpleAdapter 就不够用了必须继承 BaseAdapter。核心是重写四个方法getCount、getItem、getItemId、getView。getView 里用 convertView 加 ViewHolder 做复用这是性能关键。行布局 list_item_btn.xml?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationhorizontal android:padding8dp ImageView android:idid/img android:layout_width48dp android:layout_height48dp / LinearLayout android:layout_width0dp android:layout_heightwrap_content android:layout_weight1 android:orientationvertical TextView android:idid/title android:layout_widthwrap_content android:layout_heightwrap_content android:textSize18sp / TextView android:idid/info android:layout_widthwrap_content android:layout_heightwrap_content android:textSize13sp / /LinearLayout Button android:idid/view_btn android:layout_widthwrap_content android:layout_heightwrap_content android:text查看 / /LinearLayout自定义 Adapter 与 Activitypublic class BaseAdapterActivity extends AppCompatActivity { private ListMapString, Object mData; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); mData getData(); ListView listView findViewById(R.id.list_base); listView.setAdapter(new MyAdapter(this)); } private ListMapString, Object getData() { ListMapString, Object list new ArrayList(); for (int i 1; i 3; i) { MapString, Object map new HashMap(); map.put(title, G i); map.put(info, google i); map.put(img, R.mipmap.ic_launcher); list.add(map); } return list; } public final class ViewHolder { public ImageView img; public TextView title; public TextView info; public Button viewBtn; } public class MyAdapter extends BaseAdapter { private LayoutInflater mInflater; public MyAdapter(Context context) { this.mInflater LayoutInflater.from(context); } Override public int getCount() { return mData.size(); } Override public Object getItem(int position) { return mData.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) { holder new ViewHolder(); convertView mInflater.inflate(R.layout.list_item_btn, null); holder.img convertView.findViewById(R.id.img); holder.title convertView.findViewById(R.id.title); holder.info convertView.findViewById(R.id.info); holder.viewBtn convertView.findViewById(R.id.view_btn); convertView.setTag(holder); } else { holder (ViewHolder) convertView.getTag(); } holder.img.setImageResource((Integer) mData.get(position).get(img)); holder.title.setText((String) mData.get(position).get(title)); holder.info.setText((String) mData.get(position).get(info)); final int pos position; holder.viewBtn.setOnClickListener(v - Toast.makeText(BaseAdapterActivity.this, 点击了第 (pos 1) 项, Toast.LENGTH_SHORT).show()); return convertView; } } }这里有个容易踩的坑按钮的点击监听里如果直接用 position会因为 View 复用导致位置错乱所以我把 position 赋给 final 局部变量 pos。另外 getItem 我返回了真实数据比返回 null 更规范方便外部取值。4. 运行验证数据渲染结果怎么确认代码贴完点 Android Studio 的 Run 按钮选模拟器或真机。三个 Activity 分别跑验证动作如下。ArrayAdapter 页面应该看到五行文字从上到下是“第一项”到“第五项”点击任意一行有灰色按压背景。如果空白先检查 setAdapter 是否在 setContentView 之后调用。SimpleAdapter 页面每行左边一个图标右边两行文字第一行是名字第二行是签名。如果图片不显示检查 imgIds 里的资源是否真实存在如果文字错位检查 from 和 to 数组的顺序是否对应。BaseAdapter 页面每行有图标、标题、信息和“查看”按钮。点按钮应该弹出 Toast显示“点击了第 N 项”。这里重点验证两件事一是滚动列表时按钮点击的位置是否正确验证 ViewHolder 复用没串位二是快速滑动不卡顿验证 convertView 复用生效。如果你想用模型辅助排查可以把报错日志贴到模型对话里问比如“getView 里 convertView.getTag() 返回 null 是什么原因”。接入文档里有具体的请求格式API Keys 页面能管理你的调用凭证。5. 本篇常见错排查第一个高频错误java.lang.NullPointerException在 getView 里。九成是 convertView 为 null 时忘了setTag(holder)或者 else 分支里getTag()拿到 null 后直接强转。检查 setTag 和 getTag 是否成对出现。第二个列表显示空白但没报错。先确认数据源 size 不为 0再确认 getCount 返回的是数据源大小而不是写死的 0。ArrayAdapter 如果传了 null 数组getCount 返回 0界面就是空的。第三个SimpleAdapter 图片不显示。它的 ImageView 绑定走的是 setImageResource只认 int 类型的资源 ID。如果你 Map 里 put 的是 Bitmap 或 String 路径它不会报错但也不显示。第四个按钮点击响应错位。这是 View 复用导致的经典问题position 必须用 final 变量固定或者用holder.viewBtn.setTag(position)再在监听里取 tag。第五个ClassCastException在 getItem 或 getView 里。通常是 Map 里存的对象类型和取的时候强转的类型不一致比如存了 Integer 却按 String 取。put 和 get 的类型要严格对应。6. 选型建议与下一步三种适配器的差异落到实际写代码就是一句话ArrayAdapter 管一行文字SimpleAdapter 管图片加文字但不管交互BaseAdapter 什么都能管但要自己写复用。我自己的习惯是只要列表项里有任何可点击控件直接上 BaseAdapter省得后面改。下一步你可以把 BaseAdapter 的 getView 改成用RecyclerView.Adapter的写法那是现在的主流。如果你在写的过程中想让模型帮你把这段 BaseAdapter 骨架转成 RecyclerView 版本或者解释LayoutInflater.from(context)和getLayoutInflater()的区别可以用模型对话快速问长期做 Android 编码的话Coding Plan 的持续调用更顺手。API 地址还是 https://taotoken.net/api Key 在 console 里建接入文档里有完整的请求示例。
返回列表