- 概述
- 内置ListView组件
- 内置ListView的局限性
- 方案一:使用ListView扩展
- 方案二:Spinner下拉列表
- 方案三:WebView + HTML自定义列表
- 方案四:动态创建组件
- 实战案例:联系人列表
- 方案对比
- 常见问题
- 总结
概述
ListView是App Inventor 2中最常用的列表展示组件。内置的ListView支持简单的文本列表,但自定义能力有限。本文介绍从基础到高级的ListView自定义方案:
- 内置ListView — 简单文本/详情列表
- ListView扩展 — AdvancedListView等第三方扩展
- WebView方案 — 使用HTML/CSS打造完全自定义的列表
内置ListView组件
组件位置
组件面板 → User Interface → ListView
核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
| ElementsFromString | text | 逗号分隔的列表项 |
| Selection | text | 当前选中项 |
| SelectionIndex | number | 当前选中项索引 |
| ShowFilterBar | boolean | 是否显示搜索过滤栏 |
| TextSize | number | 文字大小 |
| TextColor | color | 文字颜色 |
| BackgroundColor | color | 背景颜色 |
| Height | number | 列表高度 |
基本用法
当 Screen1.初始化
设 列表视图1.元素来自字符串 = "苹果,香蕉,橙子,葡萄,西瓜"
' 或使用列表
设 列表视图1.元素 = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]
当 列表视图1.选择完成()
设 标签_选中.文本 = "你选择了:" & 列表视图1.选中项
带过滤的列表
设置 ShowFilterBar = true,用户可以在顶部搜索框中输入关键字过滤列表项。
动态添加/删除列表项
// 添加
当 按钮_添加.被点击
设 当前列表 = 列表视图1.元素
设 当前列表 = 添加列表项(当前列表, 输入框_新项目.文本)
设 列表视图1.元素 = 当前列表
// 删除
当 按钮_删除.被点击
设 当前列表 = 列表视图1.元素
设 选中索引 = 列表视图1.选中索引
如果 选中索引 > 0
设 当前列表 = 移除列表项(当前列表, 选中索引)
设 列表视图1.元素 = 当前列表
内置ListView的局限性
内置ListView只能显示:
- 单行文本
- 统一的颜色和字体大小
- 无法显示图标/图片
- 无法自定义每项布局
如果需要更丰富的列表效果,需要使用扩展或WebView方案。
方案一:使用ListView扩展
常用扩展
| 扩展名 | 来源 | 功能 |
|---|---|---|
| AdvancedListView | Kodular社区 | 多列、图标、自定义布局 |
| RecyclerView | DeepHost | 高性能、自定义Item |
| ListViewPlus | AppyBuilder | 图片+文本+副文本 |
示例:AdvancedListView
AdvancedListView支持在列表项中显示图片、主文本、副文本。
添加扩展
- 下载AdvancedListView扩展(.aix文件)
- 在App Inventor中:Designer → Extension → Import Extension
- 选择下载的.aix文件
使用方法
当 Screen1.初始化
设 数据列表 = 创建空列表
' 创建列表项(每项是一个字典)
设 数据列表 = 添加列表项(数据列表, 创建字典("标题" → "张三", "副标题" → "18888888888", "图片" → "contact_icon.png"))
设 数据列表 = 添加列表项(数据列表, 创建字典("标题" → "李四", "副标题" → "19999999999", "图片" → "contact_icon.png"))
设 数据列表 = 添加列表项(数据列表, 创建字典("标题" → "王五", "副标题" → "17777777777", "图片" → "contact_icon.png"))
调用 AdvancedListView1.设置数据(数据列表)
当 AdvancedListView1.项目被点击(位置, 项目数据)
设 名称 = 从字典 项目数据 获取 "标题"
设 电话 = 从字典 项目数据 获取 "副标题"
标签_详情.文本 = 名称 & ":" & 电话
方案二:Spinner下拉列表
Spinner是另一种列表选择组件,显示为下拉菜单:
当 Screen1.初始化
设 下拉框1.元素 = ["选项1", "选项2", "选项3"]
当 下拉框1.选择后()
标签_选择.文本 = "选择了:" & 下拉框1.选中项
方案三:WebView + HTML自定义列表
最灵活的方案,可以完全控制列表的外观。
HTML文件(custom_list.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, sans-serif; background: #f5f5f5; }
.list-item {
display: flex; align-items: center; padding: 12px 16px;
background: white; border-bottom: 1px solid #eee;
cursor: pointer;
}
.list-item:active { background: #e8e8e8; }
.avatar {
width: 40px; height: 40px; border-radius: 50%;
background: #4CAF50; color: white; display: flex;
align-items: center; justify-content: center;
font-size: 18px; font-weight: bold; margin-right: 12px;
flex-shrink: 0;
}
.info { flex: 1; }
.title { font-size: 16px; color: #333; }
.subtitle { font-size: 13px; color: #999; margin-top: 2px; }
.badge {
background: #ff4444; color: white; border-radius: 50%;
min-width: 20px; height: 20px; display: flex;
align-items: center; justify-content: center;
font-size: 12px; padding: 0 6px;
}
.search-bar {
padding: 8px 16px; background: white; border-bottom: 1px solid #eee;
}
.search-bar input {
width: 100%; padding: 8px 12px; border: 1px solid #ddd;
border-radius: 20px; font-size: 14px; outline: none;
}
</style>
</head>
<body>
<div class="search-bar">
<input type="text" id="search" placeholder="搜索...">
</div>
<div id="list"></div>
<script>
var items = [];
function renderList(data) {
var html = '';
data.forEach(function(item, index) {
html += '<div class="list-item" onclick="onItemClick(' + index + ')">';
html += '<div class="avatar">' + item.title.charAt(0) + '</div>';
html += '<div class="info">';
html += '<div class="title">' + item.title + '</div>';
html += '<div class="subtitle">' + item.subtitle + '</div>';
html += '</div>';
if (item.badge) {
html += '<div class="badge">' + item.badge + '</div>';
}
html += '</div>';
});
document.getElementById('list').innerHTML = html;
}
function onItemClick(index) {
window.AppInventor.setWebViewString("CLICK:" + index);
}
// 搜索过滤
document.getElementById('search').oninput = function() {
var keyword = this.value.toLowerCase();
var filtered = items.filter(function(item) {
return item.title.toLowerCase().includes(keyword) ||
item.subtitle.toLowerCase().includes(keyword);
});
renderList(filtered);
};
// 监听App端数据
setInterval(function() {
var data = window.AppInventor.getWebViewString();
if (data && data !== "") {
try {
items = JSON.parse(data);
renderList(items);
} catch(e) {}
window.AppInventor.setWebViewString("");
}
}, 200);
</script>
</body>
</html>
App Inventor积木块
当 Screen1.初始化
设 WebView1.主页地址 = "file:///android_asset/custom_list.html"
当 Screen1.初始化 完成
设 联系人数据 = "["
设 联系人数据 = 联系人数据 & "{""title"":""张三"",""subtitle"":""18888888888"",""badge"":""3""},"
设 联系人数据 = 联系人数据 & "{""title"":""李四"",""subtitle"":""19999999999"",""badge"":""""},"
设 联系人数据 = 联系人数据 & "{""title"":""王五"",""subtitle"":""17777777777"",""badge"":""1""}"
设 联系人数据 = 联系人数据 & "]"
当 WebView1.页面加载完成(网址)
设 WebView1.WebView字符串 = 联系人数据
当 WebView1.WebView字符串改变(值)
如果 值 以 "CLICK:" 开头
设 选中索引 = 文本转数字(替换文本(值, "CLICK:", ""))
标签_详情.文本 = "点击了第" & (选中索引+1) & "项"
方案四:动态创建组件
使用Dynamic Components扩展,可以在HorizontalArrangement中动态创建Label、Image等组件,模拟自定义列表。
当 Screen1.初始化
设 数据列表 = ["项目1", "项目2", "项目3"]
设 i = 1
当 i ≤ 列表长度(数据列表)
' 创建水平布局容器
调用 动态组件.创建组件(垂直布局1, "HorizontalArrangement", "row_" & i)
' 在容器中创建标签
调用 动态组件.创建组件("row_" & i, "Label", "label_" & i)
调用 动态组件.设置属性("label_" & i, "文本", 列表第i项(数据列表))
调用 动态组件.设置属性("label_" & i, "字体大小", 18)
设 i = i + 1
实战案例:联系人列表
功能需求
- 显示联系人头像、姓名、电话
- 点击拨打电话
- 搜索过滤
- 添加/删除联系人
使用WebView方案
设 联系人列表 = 创建空列表
当 Screen1.初始化
' 初始化示例数据
设 联系人列表 = [
{name: "张三", phone: "13800138001", dept: "技术部"},
{name: "李四", phone: "13900139002", dept: "市场部"},
{name: "王五", phone: "13700137003", dept: "财务部"}
]
设 WebView1.主页地址 = "file:///android_asset/custom_list.html"
当 WebView1.页面加载完成(网址)
设 JSON数据 = 调用 文本.转JSON(联系人列表)
设 WebView1.WebView字符串 = JSON数据
当 WebView1.WebView字符串改变(值)
如果 值 以 "CLICK:" 开头
设 索引 = 文本转数字(替换文本(值, "CLICK:", "")) + 1
设 联系人 = 列表第索引项(联系人列表)
设 电话 = 从字典 联系人 获取 "phone"
调用 电话拨号器.拨打电话(电话)
当 按钮_添加联系人.被点击
设 新联系人 = 创建字典("name" → 输入框_姓名.文本, "phone" → 输入框_电话.文本, "dept" → 输入框_部门.文本)
设 联系人列表 = 添加列表项(联系人列表, 新联系人)
设 WebView1.WebView字符串 = 调用 文本.转JSON(联系人列表)
方案对比
| 方案 | 灵活度 | 性能 | 难度 | 适用场景 |
|---|---|---|---|---|
| 内置ListView | ⭐ | 高 | ⭐ | 简单文本列表 |
| ListView扩展 | ⭐⭐⭐ | 高 | ⭐⭐ | 图文列表、多列 |
| WebView+HTML | ⭐⭐⭐⭐⭐ | 中 | ⭐⭐⭐ | 完全自定义UI |
| 动态组件 | ⭐⭐⭐ | 中低 | ⭐⭐⭐ | 需要原生组件 |
推荐:
- 简单列表 → 内置ListView
- 需要图片/多行 → ListView扩展
- 完全自定义UI → WebView + HTML
常见问题
Q1: ListView数据量很大时卡顿?
- 内置ListView在100+项时可能卡顿
- 使用WebView方案,HTML的虚拟滚动效果更好
- 或使用RecyclerView扩展(支持懒加载)
Q2: 如何实现列表项滑动删除?
内置组件不支持滑动操作。解决方案:
- 使用支持滑动的扩展(如SwipeListView)
- 或使用WebView + touch事件实现
Q3: 如何在列表项中显示不同颜色的标签?
内置ListView不支持。使用WebView方案:
<span class="tag" style="background: #4CAF50">已完成</span>
<span class="tag" style="background: #FF9800">进行中</span>
总结
ListView自定义是App Inventor 2进阶开发的重要技能。根据需求复杂度选择合适方案:
- 简单需求 → 内置组件
- 中等需求 → 扩展组件
- 高级需求 → WebView + HTML
版权声明:MIT App Inventor 官方文档采用 CC BY-SA 4.0 授权,本文档由 ai2claw 🐝 整理。
扫码添加客服咨询