App Inventor 2 动态图表教程 - ECharts与Chart组件实现实时曲线图

« 返回首页

概述

在App Inventor 2中实现动态折线图/曲线图有两种主要方案:

方案 灵活度 性能 难度
Chart组件(内置) ⭐⭐
ECharts + WebView ⭐⭐⭐⭐⭐ ⭐⭐⭐

方案一:内置Chart组件

组件位置

组件面板 → ChartsChart + LineChart

基本使用

Chart组件需要配合数据源组件使用:

  • ChartData — 静态数据
  • FileData — 从文件读取数据
  • WebData — 从网络API获取数据
  • BluetoothData — 从蓝牙获取数据

添加Chart组件

  1. 在Designer中拖入 Chart 组件
  2. 设置Chart类型为 Line(折线图)
  3. 添加 ChartData 组件作为数据源

动态添加数据点

当 按钮_添加数据.被点击
  设 值 = 文本转数字(输入框_数值.文本)
  调用 ChartData1.添加数据项(值)

' 带标签的数据点
当 按钮_添加带标签.被点击
  设 标签 = "第" & 数据计数 & "个"
  设 值 = 文本转数字(输入框_数值.文本)
  调用 ChartData1.添加数据项(标签, 值)

清除图表数据

当 按钮_清除.被点击
  调用 ChartData1.清除数据()

实时更新图表(配合Clock)

设 数据计数 = 0

当 Screen1.初始化
  设 Clock1.计时器间隔 = 1000  ' 每秒更新
  设 Clock1.启用计时器 = true

当 Clock1.计时
  设 数据计数 = 数据计数 + 1
  设 随机值 = 数学.随机小数() * 100
  调用 ChartData1.添加数据项("T" & 数据计数, 随机值)
  
  ' 限制显示最近20个点
  如果 数据计数 > 20
    调用 ChartData1.移除数据项(0)

Chart组件属性

属性 说明
Type 图表类型(Line/Bar/Scatter/Area)
Description 图表描述
LabelsFromX 是否从X轴数据生成标签
GridEnabled 是否显示网格线
LegendEnabled 是否显示图例
XFromZero X轴是否从0开始

多条折线

添加多个ChartData组件,每个对应一条线:

当 Clock1.计时
  设 值1 = 传感器值1
  设 值2 = 传感器值2
  调用 ChartData1.添加数据项(值1)  ' 线1
  调用 ChartData2.添加数据项(值2)  ' 线2

方案二:ECharts + WebView

ECharts是百度开源的强大图表库,通过WebView可以在App Inventor中使用。

HTML文件(echarts_dynamic.html)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<style>
body { margin: 0; background: #fff; }
#chart { width: 100vw; height: 100vh; }
</style>
</head>
<body>
<div id="chart"></div>
<script>
var myChart = echarts.init(document.getElementById('chart'));
var data = [];
var labels = [];
var maxPoints = 30;

var option = {
  title: { text: '实时数据监控', left: 'center', textStyle: { fontSize: 16 } },
  tooltip: { trigger: 'axis' },
  grid: { left: '10%', right: '5%', bottom: '15%', top: '20%' },
  xAxis: { type: 'category', data: labels, axisLabel: { fontSize: 10 } },
  yAxis: { type: 'value', axisLabel: { fontSize: 10 } },
  series: [{
    type: 'line',
    data: data,
    smooth: true,
    lineStyle: { color: '#4CAF50', width: 2 },
    areaStyle: { color: 'rgba(76,175,80,0.2)' },
    itemStyle: { color: '#4CAF50' }
  }],
  dataZoom: [{ type: 'inside' }]
};

myChart.setOption(option);

setInterval(function() {
  var msg = window.AppInventor.getWebViewString();
  if (msg && msg !== "") {
    try {
      var parsed = JSON.parse(msg);
      if (parsed.type === "add") {
        data.push(parsed.value);
        labels.push(parsed.label || "");
        if (data.length > maxPoints) {
          data.shift();
          labels.shift();
        }
        myChart.setOption({ xAxis: { data: labels }, series: [{ data: data }] });
      } else if (parsed.type === "multi") {
        // 多条线
        option.series = parsed.series;
        option.xAxis.data = parsed.labels;
        myChart.setOption(option, true);
      } else if (parsed.type === "clear") {
        data = [];
        labels = [];
        myChart.setOption({ xAxis: { data: [] }, series: [{ data: [] }] });
      }
    } catch(e) {}
    window.AppInventor.setWebViewString("");
  }
}, 100);
</script>
</body>
</html>

App Inventor积木块

设 数据计数 = 0

当 Screen1.初始化
  设 WebView1.主页地址 = "file:///android_asset/echarts_dynamic.html"
  设 Clock1.计时器间隔 = 500
  设 Clock1.启用计时器 = true

当 Clock1.计时
  设 数据计数 = 数据计数 + 1
  设 值 = 数学.随机小数() * 100
  设 消息 = "{""type"":""add"",""label"":""" & 数据计数 & """,""value"":" & 值 & "}"
  设 WebView1.WebView字符串 = 消息

当 按钮_清除图表.被点击
  设 WebView1.WebView字符串 = "{""type"":""clear""}"
  设 数据计数 = 0

多条折线(ECharts)

当 按钮_显示多条线.被点击
  设 JSON数据 = "{
    ""type"":""multi"",
    ""labels"":[""1月"",""2月"",""3月"",""4月"",""5月""],
    ""series"":[
      {""type"":""line"",""name"":""温度"",""data"":[5,8,15,22,28],""smooth"":true},
      {""type"":""line"",""name"":""湿度"",""data"":[80,70,60,55,50],""smooth"":true}
    ]
  }"
  设 WebView1.WebView字符串 = JSON数据

实战案例:温度监测折线图

功能需求

  • 每2秒采集一次温度(模拟)
  • 实时更新折线图
  • 显示最高/最低/平均温度
  • 可以暂停/继续

积木块代码

设 温度列表 = 创建空列表
设 时间列表 = 创建空列表
设 是否运行 = false
设 采集计数 = 0

当 Screen1.初始化
  设 WebView1.主页地址 = "file:///android_asset/echarts_dynamic.html"

当 按钮_开始.被点击
  设 是否运行 = true
  设 Clock1.计时器间隔 = 2000
  设 Clock1.启用计时器 = true
  设 按钮_开始.文本 = "暂停"

当 Clock1.计时
  如果 是否运行
    设 采集计数 = 采集计数 + 1
    ' 模拟温度数据(20-30度随机波动)
    设 温度 = 25 + (数学.随机小数() - 0.5) * 10
    设 温度 = 数学.保留小数(温度, 1)
    设 时间标签 = Clock1.格式化日期时间(Clock1.当前时间(), "HH:mm:ss")
    
    设 温度列表 = 添加列表项(温度列表, 温度)
    设 时间列表 = 添加列表项(时间列表, 时间标签)
    
    ' 更新图表
    设 消息 = "{""type"":""add"",""label"":""" & 时间标签 & """,""value"":" & 温度 & "}"
    设 WebView1.WebView字符串 = 消息
    
    ' 更新统计
    设 最高 = 列表最大值(温度列表)
    设 最低 = 列表最小值(温度列表)
    设 平均 = 列表求和(温度列表) / 列表长度(温度列表)
    标签_统计.文本 = "最高:" & 最高 & "°C | 最低:" & 最低 & "°C | 平均:" & 数学.保留小数(平均, 1) & "°C"

常见问题

Q1: Chart组件数据多了会卡顿?

内置Chart组件在100+数据点时可能变慢。解决方案:

  • 定期清除旧数据
  • 使用ECharts方案(性能更好)
  • 限制显示的数据点数量

Q2: ECharts图表在WebView中显示不全?

确保HTML中容器尺寸正确:

#chart { width: 100vw; height: 100vh; }

WebView的宽高也需要正确设置。

Q3: 如何实现饼图/柱状图?

ECharts方案中修改series的type:

  • 折线图:type: 'line'
  • 柱状图:type: 'bar'
  • 饼图:type: 'pie'
  • 散点图:type: 'scatter'

Q4: 图表可以导出为图片吗?

ECharts支持导出图片:

var imgData = myChart.getDataURL({ type: 'png' });
window.AppInventor.setWebViewString("IMG:" + imgData);

总结

需求 推荐方案
简单折线图 内置Chart组件
实时动态图 ECharts + WebView
多种图表类型 ECharts
自定义样式 ECharts
离线使用 内置Chart组件

版权声明:MIT App Inventor 官方文档采用 CC BY-SA 4.0 授权,本文档由 ai2claw 🐝 整理。

文档反馈