App Inventor 2 ListView自定义Item教程 - 打造精美列表

« 返回首页

概述

ListView是App Inventor 2中最常用的列表展示组件。内置的ListView支持简单的文本列表,但自定义能力有限。本文介绍从基础到高级的ListView自定义方案:

  1. 内置ListView — 简单文本/详情列表
  2. ListView扩展 — AdvancedListView等第三方扩展
  3. WebView方案 — 使用HTML/CSS打造完全自定义的列表

内置ListView组件

组件位置

组件面板 → User InterfaceListView

核心属性

属性 类型 说明
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支持在列表项中显示图片、主文本、副文本。

添加扩展

  1. 下载AdvancedListView扩展(.aix文件)
  2. 在App Inventor中:Designer → Extension → Import Extension
  3. 选择下载的.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 🐝 整理。

文档反馈