ARTICLE DETAIL

资讯详情

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

ListView(2)SimpleAdapter与SimpleCursorAdapter详解:TaoToken统一Key接入Android配置骨架

ListView(2)SimpleAdapter与SimpleCursorAdapter详解:TaoToken统一Key接入Android配置骨架 1. 从一次真机崩溃说起SimpleAdapter 与 SimpleCursorAdapter 到底怎么选如果你正在写 Android 的列表页大概率绕不开ListView和它的几个经典 Adapter。ArrayAdapter只能塞一个字符串数组稍微复杂一点就顶不住自定义BaseAdapter又得自己写 ViewHolder、getView、复用逻辑代码量直接翻倍。这时候SimpleAdapter和SimpleCursorAdapter就是那个“刚刚好”的中间层——前者把ListMapString,Object映射到 XML 布局后者把数据库Cursor的列直接映射到TextView/ImageView。这篇聚焦两件事一是把这两种 Adapter 的配置化落地讲透给出可直接复制的布局 XML 和 Java/Kotlin 初始化代码二是把 AI 辅助开发工具的接入配置也一并搭好用统一的 Key 和 API 通道在settings.json与config.toml里完成配置让写 Adapter 样板代码这件事本身也能被工具加速。适合已经会写findViewById、但对 Adapter 参数含义还模模糊糊的 Android 初学者也适合想把老项目 ListView 逻辑重新梳理一遍的开发者。我试过在同一个页面里先跑 SimpleAdapter 再切 SimpleCursorAdapter最容易踩的坑不是代码本身而是from和to两个数组的顺序对不上——界面不报错但数据串位排查起来很费时间。所以下面每个参数我都会标清楚它到底在映射什么。2. 前置准备TaoToken 统一 Key 与 API 通道接入在动手写 Adapter 之前先把 AI 辅助开发工具的通道配好。这样后面写重复的getView、写from/to数组时可以让工具帮你补全和检查。TaoToken 提供统一的 Key 和 API 入口官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 这个不加 UTM。你需要先去控制台创建一个 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建完在 API Keys 页面复制出来地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。这个 Key 后面会同时写进settings.json和config.toml两个文件用同一个 Key不用分别申请。注意Key 只存在本地配置文件里不要提交到 Git 仓库。建议在.gitignore里把这两个配置文件加进去或者用环境变量占位。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 配置字段的含义如果和你的工具版本对不上以文档为准。如果你用的是 Claude Code 这类编码工具可以参考 https://taotoken.net/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentClaudeCodeAnthropicutm_campaignrewrite 里的接入方式。3. 可复制配置settings.json 与 config.toml 双文件落地先给settings.json的配置骨架。这个文件一般放在你的工具配置目录下字段名按你实际使用的工具调整核心是base_url指向 TaoToken 的 API 地址api_key填刚才复制的 Key{ ai: { provider: taotoken, base_url: https://taotoken.net/api, api_key: sk-你的TaoTokenKey, model: claude-sonnet, timeout_ms: 60000 }, editor: { inline_completion: true, adapter_snippet: true } }再给config.toml的等价配置。有些工具读 TOML 而不是 JSON两个文件保持同一套 Key 和 base_url 即可[ai] provider taotoken base_url https://taotoken.net/api api_key sk-你的TaoTokenKey model claude-sonnet timeout_ms 60000 [editor] inline_completion true adapter_snippet true配置完成后工具在补全SimpleAdapter构造函数时就能给出参数提示。如果你更想直接在对话里问 Adapter 参数含义可以用模型对话入口 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 把报错日志贴进去让它帮你定位。长期做 Android 编码、需要 Agent 连续改多个文件的可以看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。4. SimpleAdapter 配置化落地Map 数据绑定图文列表4.1 构造函数五个参数逐个拆SimpleAdapter的构造函数签名是SimpleAdapter(Context context, List? extends MapString, ? data, int resource, String[] from, int[] to)。五个参数的含义如下表参数类型作用contextContext上下文通常传 Activity 的 thisdataListMapString,Object数据源每个 Map 对应一个 ItemresourceintItem 的布局文件资源 IDfromString[]Map 中的 key 数组决定取哪些字段toint[]布局中 View 的 ID 数组与 from 一一对应关键点在于from和to是按下标一一对应的from[0]的值会填到to[0]指向的 Viewfrom[1]填到to[1]。顺序错一位数据就串位。4.2 Item 布局 XML先写一个图文横向布局item_simple.xml左边 ImageView右边 TextView?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:orientationhorizontal android:layout_widthmatch_parent android:layout_heightwrap_content android:padding8dp ImageView android:idid/imv_icon android:layout_width56dp android:layout_height56dp android:scaleTypecenterCrop android:srcmipmap/ic_launcher / TextView android:idid/tv_content android:layout_width0dp android:layout_weight1 android:layout_height56dp android:gravitycenter_vertical android:paddingStart12dp android:textSize16sp / /LinearLayout4.3 Java 初始化代码ListView listView findViewById(R.id.list_view); ListMapString, Object data new ArrayList(); for (int i 0; i 20; i) { MapString, Object item new HashMap(); item.put(icon, R.mipmap.ic_launcher); item.put(content, 列表项 i); data.add(item); } String[] from new String[]{icon, content}; int[] to new int[]{R.id.imv_icon, R.id.tv_content}; SimpleAdapter adapter new SimpleAdapter( this, data, R.layout.item_simple, from, to ); listView.setAdapter(adapter);4.4 Kotlin 初始化代码val listView findViewByIdListView(R.id.list_view) val data mutableListOfMapString, Any() repeat(20) { i - data.add( mapOf( icon to R.mipmap.ic_launcher, content to 列表项 $i ) ) } val from arrayOf(icon, content) val to intArrayOf(R.id.imv_icon, R.id.tv_content) val adapter SimpleAdapter( this, data, R.layout.item_simple, from, to ) listView.adapter adapterSimpleAdapter内部对 ImageView 做了判断如果to指向的是 ImageView它会尝试把值当作资源 ID 或 Drawable 处理指向 TextView 则调用setText。所以icon这个 key 放R.mipmap.ic_launcher能直接显示放字符串就会出问题。5. SimpleCursorAdapter 配置化落地Cursor 数据绑定5.1 构造函数六个参数与 flagsSimpleCursorAdapter的签名是SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags)。比 SimpleAdapter 多了一个flags常用值是FLAG_REGISTER_CONTENT_OBSERVER注册内容观察者后Cursor 数据变化时列表会自动刷新。参数类型作用contextContext上下文layoutintItem 布局资源 IDcCursor数据游标不可用时可为 nullfromString[]Cursor 中的列名数组toint[]布局中 View 的 ID 数组flagsint行为标志常用 FLAG_REGISTER_CONTENT_OBSERVER5.2 使用系统布局绑定联系人系统自带的android.R.layout.simple_expandable_list_item_2里有两个 TextViewID 分别是android.R.id.text1和android.R.id.text2正好对应两列数据Cursor cursor getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, null); String[] from new String[]{ ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER }; int[] to new int[]{ android.R.id.text1, android.R.id.text2 }; SimpleCursorAdapter adapter new SimpleCursorAdapter( this, android.R.layout.simple_expandable_list_item_2, cursor, from, to, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER ); listView.setAdapter(adapter);别忘了在AndroidManifest.xml里声明权限uses-permission android:nameandroid.permission.READ_CONTACTS /5.3 绑定联系人头像用 Uri 而不是资源 ID想把头像也显示出来from里换成ContactsContract.Contacts.PHOTO_THUMBNAIL_URIto指向 ImageViewString[] from new String[]{ ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.PHOTO_THUMBNAIL_URI }; int[] to new int[]{ R.id.tv_content, R.id.imv_icon };这里有个实测结论PHOTO_THUMBNAIL_URI返回的是 Uri 字符串SimpleCursorAdapter能识别并加载但如果换成PHOTO_ID程序会抛android.content.res.Resources$NotFoundException。原因是数据库里存的是 Uri不是mipmap/drawable那种资源 ID两者不能混用。没设置过头像的联系人该字段为 nullImageView 自然空白这是正常现象。6. 验证请求与渲染结果日志加界面双重确认配置和代码都写完后怎么确认 Adapter 真的绑上了两个手段。第一在setAdapter之后打日志确认数据条数和 Adapter 实例Log.d(AdapterCheck, count adapter.getCount()); Log.d(AdapterCheck, adapter listView.getAdapter());getCount()返回 20 说明 Map 数据源进了 20 条返回 0 说明 data 是空的。getAdapter()不为 null 说明视图已经加载了适配器。第二用 TaoToken 的模型对话入口把日志贴进去让它帮你判断from/to是否对齐。比如你贴count20但界面只显示空白工具会提示你检查to数组里的 ID 是否真的存在于resource布局中——这是最常见的“有数据但渲染不出来”的原因。界面渲染的预期结果SimpleAdapter 显示 20 行“列表项 0”到“列表项 19”每行左侧一个图标SimpleCursorAdapter 显示联系人姓名和是否有电话号码有头像的联系人左侧显示缩略图。如果日志 count 正确但界面空白优先查布局里 View 的 ID 拼写。7. 本篇常见错排查Resources$NotFoundException把PHOTO_ID当资源 ID 传给了 ImageView。改用PHOTO_THUMBNAIL_URI或PHOTO_URI。数据串位from和to顺序不一致。比如from {content,icon}但to {R.id.imv_icon, R.id.tv_content}文字会跑到图片位置。把两个数组按同一顺序排列。界面空白但 count 正常to里的 View ID 不在resource布局中或者布局里根本没有这个 ID。检查 XML 和R.id.xxx是否一致。Cursor 为 null 导致崩溃查询联系人时权限没给或者query返回 null。加权限声明并在使用前判空。SimpleCursorAdapter 不刷新没传FLAG_REGISTER_CONTENT_OBSERVER或者 Cursor 查询后没调用setNotificationUri。加上 flag 即可。配置文件不生效settings.json和config.toml里的base_url写成了带路径的完整地址。base_url 只写到https://taotoken.net/api具体端点由工具拼接。8. 接入与排障入口Adapter 代码跑通之后如果配置环节还有疑问按场景分流接入配置和报错排查走 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 配合接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 想验证模型返回是否符合预期用模型对话 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 长期做 Android 编码、需要 Agent 连续处理多个 Adapter 文件的走 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后留一个实用技巧SimpleAdapter的data里 Map 的 value 类型是Object如果你放的是int资源 ID它会走 ImageView 分支放String走 TextView 分支。所以同一个 key 在不同 Item 里最好保持类型一致否则会出现某几行显示异常、某几行正常的情况这种问题用日志很难直接看出来只能靠统一数据类型规避。
返回列表