鸿蒙原生实战:不引图表库 —— 油耗分析页手写的油耗/油费双柱状图

App 57「加油记录」分析页(Func2Tab),主题色 #FF6B35 活力橙。本页是油耗与油费的"分析页"——白色单行 Header("油耗分析"20 号加粗) + 双指标汇总行(7.2 平均油耗橙 / ¥4668 半年油费浅橙) + 油耗趋势柱状图(3月 7.5 / 4月 7.2 / 5月 7.8 / 6月 7.1 / 7月 7.4 / 8月 7.2 L/100km,橙色柱 + 柱顶数值 + 月份标签,柱高 = 数值 × 14) + 油费趋势柱状图(3月 680 / 4月 720 / 5月 850 / 6月 690 / 7月 693 / 8月 1035 元,浅橙柱 + 柱顶金额,柱高 = 数值 × 110 ÷ 最大值)。本篇基于 57-fuel-log/entry/src/main/ets/pages/Func2Tab.ets(共 99 行)逐段拆解,附 4 张实机截图。

一、整体结构:三区"Header + 汇总 + 双图"布局

分析页是"指标总览 + 双趋势图"的分析布局——3 个 @Builder 块:

build() {
  Column() {
    this.Header()
    Scroll() {
      Column({ space: 14 }) {
        this.SummaryRow()
        this.FuelTrend()
        this.CostChart()
      }
      .width('100%')
      .padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 })
    }
    .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
  }
  .width('100%').height('100%').backgroundColor(C.bg)
}

3 块结构:

  1. SummaryRow — 双指标汇总行(平均油耗 7.2 / 半年油费 ¥4668)
  2. FuelTrend — 油耗趋势柱状图(3-8 月 L/100km)
  3. CostChart — 油费趋势柱状图(3-8 月元)

分析页是"油耗 + 油费"的双维度分析——先看两个核心指标(平均油耗 7.2、半年油费 4668),再看油耗趋势(3-8 月 7.1-7.8 波动、5 月最高 7.8),最后看油费趋势(3-8 月 680-1035、8 月最高 1035)。指标 + 性能趋势 + 费用趋势三层递进,是系列"数据可视化页"的又一代表(App 54 订阅、App 56 停车同属此类)。

项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub

二、Header:单行品牌头

Header() {
  Row() { Text('油耗分析').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) }
  .width('100%').height(this.safeTop + 56).padding({ top: this.safeTop, left: D.pad, right: D.pad })
  .backgroundColor(C.card).alignItems(VerticalAlign.Bottom)
}

功能页 Header 标准范式(20 号、固定高、文字贴底),与加油页、我的页同款。"油耗分析"点明本页职能——对油耗与油费做分析。

分析页整体布局

三、SummaryRow:双指标汇总行

SummaryRow() {
  Row({ space: 10 }) {
    Column({ space: 4 }) {
      Text('7.2').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.primary)
      Text('平均油耗').fontSize(11).fontColor(C.textDim)
    }.layoutWeight(1).padding({ top: 16, bottom: 16 })
    .backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
    Column({ space: 4 }) {
      Text('¥4668').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.accent)
      Text('半年油费').fontSize(11).fontColor(C.textDim)
    }.layoutWeight(1).padding({ top: 16, bottom: 16 })
    .backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
  }.width('100%')
}

技术拆解:

  1. 双列均分:Row({ space: 10 }) + 两个 layoutWeight(1) Column——**平均油耗(7.2)/ 半年油费(¥4668)**两列 1:1,每列白卡圆角 14 描边、上下 16 内边距。
  2. 双色数值:油耗橙(C.primary)、半年油费浅橙(C.accent)——数值用主题色系区分(性能指标橙、费用指标浅橙)。
  3. 数据自洽:半年油费 4668 = 680+720+850+690+693+1035(3-8 月逐月之和)——月度累计 → 半年汇总完全对账;平均油耗 7.2 与首页油耗一致。
  4. 双指标定位:两个指标回答"车费不费油"(7.2)与"半年花了多少"(4668)——性能 + 费用两个维度的半年快览。

四、FuelTrend:油耗趋势柱状图

FuelTrend() {
  Column({ space: 14 }) {
    Text('油耗趋势(L/100km)').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
    Row() {
      ForEach(this.months, (m: string, idx: number) => {
        Column({ space: 6 }) {
          Text(this.fuelVals[idx].toFixed(1)).fontSize(9).fontColor(C.textDim)
          Column()
            .width(24).height(this.fuelVals[idx] * 14)
            .backgroundColor(C.primary).borderRadius({ topLeft: 4, topRight: 4 })
          Text(m).fontSize(10).fontColor(C.textSub)
        }.layoutWeight(1).justifyContent(FlexAlign.End)
      }, (m: string) => m)
    }.width('100%').height(150).alignItems(VerticalAlign.Bottom)
  }
  .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
}

6 个月油耗数据(12-13 行):

月份3月4月5月6月7月8月
油耗7.57.27.87.17.47.2

技术拆解:

  1. 线性高度公式:height(this.fuelVals[idx] * 14)——柱高 = 数值 × 固定系数 14(非比例缩放!7.8 × 14 ≈ 109 最高、7.1 × 14 ≈ 99 最低),因为油耗数值区间窄(7.1-7.8),用线性系数而非最大比例(若用比例缩放,7.1 与 7.8 的柱高差异会过度放大/缩小),直接乘系数让微小差异可视化。
  2. 柱顶数值:Text(this.fuelVals[idx].toFixed(1))——油耗保留 1 位小数(7.5/7.2/7.8),9 号淡灰。
  3. 橙色柱体:24 宽橙柱(C.primary)+ 顶部两角圆角 4——与主题统一。
  4. 趋势解读:油耗在 7.1-7.8 之间小幅波动(5 月最高 7.8、6 月最低 7.1、8 月 7.2)——油耗相对稳定(同一辆车油耗不会剧烈变化),5 月可能空调开得多(油耗略高)、6 月路况好(油耗低)。

五、CostChart:油费趋势柱状图

CostChart() {
  Column({ space: 14 }) {
    Text('油费趋势').fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width('100%')
    Row() {
      ForEach(this.months, (m: string, idx: number) => {
        Column({ space: 6 }) {
          Text('¥' + this.costVals[idx]).fontSize(8).fontColor(C.textDim)
          Column()
            .width(24).height(this.costVals[idx] * 110 / this.maxCost())
            .backgroundColor(C.accent).borderRadius({ topLeft: 4, topRight: 4 })
          Text(m).fontSize(10).fontColor(C.textSub)
        }.layoutWeight(1).justifyContent(FlexAlign.End)
      }, (m: string) => m)
    }.width('100%').height(150).alignItems(VerticalAlign.Bottom)
  }
  .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
}

6 个月油费数据(14 行):

月份3月4月5月6月7月8月
油费6807208506906931035

技术拆解:

  1. 比例高度公式:height(val * 110 / maxCost())——柱高 = 数值 × 比例系数 ÷ 最大值(8 月 1035 最高占满 110、3 月 680 最低),与油耗图的线性系数不同——油费数值跨度大(680-1035)用比例缩放(相对最高值),油耗数值区间窄用线性系数,两种公式按数据分布选用。
  2. 浅橙柱体:24 宽浅橙柱(C.accent)+ 顶部圆角——与油耗图的橙色柱区分(油耗深橙、油费浅橙),两张图用同色系深浅区分。
  3. 柱顶金额:8 号淡灰"¥680"——比油耗图的 9 号更小(金额三位数占位宽),8 号紧凑。
  4. 趋势解读:油费 680-1035 波动、8 月最高(1035)——油费与油价、里程相关(8 月油价上涨或跑得多),5 月 850 与油耗 7.8 呼应(油耗高峰月油费也偏高),两张趋势图数据互证。

六、跨页数据自洽:油耗分析的全局闭环

分析页与首页、加油、我的三页形成闭环:

  • 分析页"平均油耗 7.2" ↔ 首页"平均油耗 7.2"——数值完全一致,分析页给趋势、首页给当前值。
  • 分析页"8月油费 ¥1035" ↔ 首页"本月油费 ¥1035"——金额完全一致(1035 元),柱状图最后一根(8 月)就是首页的"本月"。
  • 分析页"半年油费 ¥4668" ↔ 月度柱状图之和——4668 = 680+720+850+690+693+1035,汇总 = 明细之和。
  • 分析页"油耗趋势 8月 7.2" ↔ 首页"平均油耗 7.2"——8 月油耗 7.2 即当前平均油耗(最近月份数据),趋势末值 = 当前指标。
  • 分析页油耗/油费趋势 ↔ 加油页录入——每月的油耗与油费由加油页记录计算(油量 ÷ 里程差、油量 × 单价),录入数据 → 月度聚合 → 趋势图。
  • 我的页"¥28k 累计油费" ↔ 分析页"半年 ¥4668"——半年 4668 是累计 28000 元的一部分(6 个月),半年统计 → 累计总量。

七、系列横向对比:双柱状图页面的三种形态

系列中"双图统计"型页面各有侧重:

  • App 57 油耗分析(橙 #FF6B35):油耗趋势 + 油费趋势双柱状图——"性能 + 费用"双图(两个不同量纲的指标)。
  • App 56 费用统计(深灰 #2D3436):月度柱状图 + 地点列表——"趋势 + 结构"组合。
  • App 54 订阅统计(紫 #9B51E0):年度柱状图 + 分类占比——"趋势 + 占比"组合。

57 的差异化在于**"双柱状图 + 两种高度公式"**——油耗图用线性系数(val × 14,数值区间窄 7.1-7.8)、油费图用比例缩放(val × 110 / max,数值区间宽 680-1035),同一页两张柱状图按数据分布选用不同高度算法,这是系列中最精细的图表设计之一(App 54/56 只用比例缩放一种)。同时"油耗(性能)+ 油费(费用)"的双量纲分析是车辆维护场景独有的(其他统计页只有单一金额维度)。

八、开发者避坑:双柱状图的四个细节

从 Func2Tab 的实现提炼四个易踩坑点:

  1. 两种高度公式的取舍:油耗用 val * 14、油费用 val * 110 / maxCost()——线性系数适合数值区间窄的数据(7.1-7.8,直接乘系数展示微差)、比例缩放适合区间宽的数据(680-1035,相对最高值),若用错(油耗用比例缩放会放大噪声、油费用线性系数会溢出或过矮),图表会失真,选公式要按数据分布。
  2. 线性系数的溢出风险:val * 14 假设最大值不超过 150/14 ≈ 十点七(容器高 150)——若油耗升到 11 以上柱高 154 溢出容器,产品化应加"最高柱不超过容器高"的约束(或改用比例缩放兜底)。
  3. 双图色系区分:油耗深橙(primary)、油费浅橙(accent)——同色系深浅区分两个指标,若都用同色用户分不清哪张是油耗哪张是油费,产品化应保持色差并配合标题(油耗趋势/油费趋势)双重区分。
  4. 数值格式统一:油耗 toFixed(1)(7.2)、油费直接拼字符串(¥680)——两种数值格式(小数 vs 整数),产品化按数据类型格式化(油耗保留 1 位、金额取整或两位),避免格式混乱。

九、常见问题 FAQ

问:为什么油耗趋势用线性高度、油费趋势用比例高度?答:因为两组数据的数值分布不同——油耗在 7.1-7.8 之间(区间很窄),若用比例缩放(相对最大值),7.1 和 7.8 的柱高差异只有约 10%,视觉上几乎看不出波动;用线性系数(× 14)把 7.1-7.8 放大到 99-109 的高度区间,微小的油耗变化也能看清。油费在 680-1035 之间(区间宽),比例缩放(相对 1035 最高值)已经能清晰显示差异,无需线性放大。图表高度算法的选择取决于数据分布,这是本页最有技术含量的设计。

问:半年油费 4668 元多不多?答:对一辆家用车(大众朗逸、油耗 7.2)来说,半年 4668 元(月均 778 元)属于正常水平——按每月行驶 1000 公里、油耗 7.2、油价 7.5 元估算,每月油费约 540 元,加上油价波动与里程增加(8 月 1035 元可能跑了长途),月均 778 元在合理区间。产品化后可对比历史半年数据判断"今年油费偏高还是偏低"。

问:8 月油费为什么突然到 1035 元?答:演示数据设计 8 月为油费高峰——可能的原因:8 月油价上涨(95# 涨到 7.58 元/L)、8 月出行增多(暑假自驾游、里程增加 1020km)、或单月加油次数多。产品化后柱状图直接反映油价与里程的实际变化,8 月为什么高可以从当月加油记录中找到答案(次数、单价、油量)。

十、验收与总结

本篇对 57-fuel-log 分析页(Func2Tab,99 行)做了逐块拆解:

  • 三区结构:Header(油耗分析)+ SummaryRow(双指标汇总)+ FuelTrend(油耗趋势柱)+ CostChart(油费趋势柱)
  • 核心交互:无点击(纯数据展示),双柱状图两种高度公式
  • 主题色:#FF6B35 活力橙(油耗柱)+ #FFA078 浅橙(油费柱)
  • 数据闭环:分析页 7.2/4668/8月1035 → 首页 7.2/1035 → 加油页录入,四页同源

分析页是加油记录的"油耗油费分析"——双指标给结论(7.2 油耗/4668 半年)、油耗趋势给性能画像(7.1-7.8 稳定)、油费趋势给费用画像(680-1035 波动),两图一页完成油耗与油费的全面分析。

油费趋势柱状图

十、扩展思考:从油耗分析到驾驶建议

分析页的演进方向是"驾驶建议":第一是油耗归因分析——油耗波动(7.1-7.8)的原因分析(夏季空调导致 5 月 7.8、高速路段导致 6 月 7.1),演进方向是结合加油记录的时间/里程密度推测路况(两次加油间隔短 = 市区短途多 = 油耗高),让油耗趋势"看得懂为什么";第二是驾驶行为建议——油耗偏高的月份建议(胎压检查、减少急加速、定期保养),演进方向是根据油耗数据生成个性化建议("近三月油耗上升,建议检查胎压与空滤"),分析页的趋势图是建议的数据依据;第三是油费预算与预警——月度油费趋势(680-1035)演进方向是设油费预算(每月 800 元),超预算月份柱状图变红(8 月 1035 超预算),结合预算提醒"控制油费";第四是车辆横向对比——平均油耗 7.2 与同车型均值对比(大众朗逸车主平均 7.0),演进方向是展示"我的油耗 vs 同款车型均值",让用户知道车况是否正常。这四个方向都从分析页已有的双柱状图长出来,让"看数据"变成"得建议"。

油耗趋势柱状图

十一、从设计稿到代码的映射

把分析页设计稿翻译成代码,有几组典型映射:第一,"双指标汇总"映射为"两个等权重白卡"——设计稿的平均油耗与半年油费两列,代码里用两个等权重列容器各放 20 号数值与 11 号标签,数值用橙与浅橙;第二,"油耗柱状图"映射为"等分行内的高度计算容器"——设计稿的六根橙色柱,代码里用六个等权重列,柱高按油耗数值乘固定系数 14,柱顶数值保留一位小数;第三,"油费柱状图"映射为"比例缩放的柱体"——设计稿的六根浅橙柱,代码里柱高按油费数值乘比例系数除以最大值,柱顶金额;第四,"双图卡片"映射为"两个白卡加标题"——设计稿的油耗趋势与油费趋势两张图,代码里用两个白底描边卡片各放标题与柱状图行。这四组映射展示了"双图分析页"的完整翻译,其中两种高度公式的选用是最有技术含量的设计决策。

十二、常见问题 FAQ

问:为什么同一页要用两种柱高公式?答:因为油耗(7.1-7.8)与油费(680-1035)的数据分布不同——油耗区间窄(差异约 10%),用比例缩放会"看不出波动",用线性系数(乘 14)放大微差;油费区间宽(最大为最小的 1.5 倍),用比例缩放(相对最大值)已经清晰。柱状图高度公式的选择要匹配数据分布:窄区间用线性系数、宽区间用比例缩放,这是本页图表设计的核心技巧,也是系列其他柱状图(App 54/56 只有比例缩放)的补充。

问:半年油费 4668 元是怎么算出来的?答:4668 = 680 + 720 + 850 + 690 + 693 + 1035(3 月到 8 月逐月油费之和)——汇总行的"半年油费"就是柱状图六根柱的数值相加,汇总 = 明细之和完全对账。产品化后由每月加油记录自动聚合,新增记录后半年总额动态更新。

问:油耗趋势和油费趋势两张图有什么关系?答:两张图是"性能与费用"的对照——油耗趋势反映"车本身费不费油"(性能维度),油费趋势反映"花了多少钱"(费用维度)。两者相关但不相同:油耗低但跑得多,油费也可能高(6 月油耗最低 7.1 但油费 690 元);油耗高油费通常也高(5 月油耗 7.8、油费 850 元)。对照看才能理解"油费 = 油耗 × 里程 × 油价"的完整关系。

十三、补充要点:分析页三个细节再思考

第一,关于数值精度。油耗柱顶用 toFixed(1)(7.5 一位小数)而油费用整数(¥680)——精度按数据特征选择:油耗 7.1-7.8 必须一位小数(否则 7 和 8 分不清)、油费整十到百位足够(680 与 690 差距明显),图表数值的精度要匹配数据的区分度。第二,关于双图的色彩区分。油耗深橙(primary)与油费浅橙(accent)同色系深浅区分——同色系保证"同一主题"(都是橙=能源),深浅区分"两个指标"(深=性能、浅=费用),配合标题(油耗趋势/油费趋势)双重区分,若两图同色用户会混淆。第三,关于趋势的收尾。两张图都以 8 月收尾(油耗 7.2、油费 1035)——与首页"平均油耗 7.2、本月油费 1035"完全一致,趋势图最后一个月即当前月,首页给当前值、分析页给历史趋势,两个页面"当前 + 历史"互补。

分析页双指标汇总

十四、最后总结

分析页作为加油记录的"分析页",用三个区块完成了油耗与油费的完整分析:双指标给结论(7.2 油耗/4668 半年)、油耗趋势给性能画像(7.1-7.8 稳定波动)、油费趋势给费用画像(680-1035 有高有低)。整页没有用户卡与菜单,是系列"纯数据页"的又一代表——第 3 Tab 完全让给数据分析,体现加油记录"数据驱动"的产品重心。手绘双柱状图展示了 ArkUI 基础组件完成数据可视化的能力,两种高度公式(线性系数 + 比例缩放)是系列图表设计中最精细的实现。从演示到产品,分析页的进化方向是动态聚合、油耗归因、预算预警、车型对比,从"看数据"走向"得建议"。作为系列"双图统计"页的代表,它与首页(油耗总览)、加油页(录入入口)、我的页(车辆档案)共同构成加油记录的完整链路:录入 → 总览 → 分析 → 管理。

十五、补充要点:双图的对照阅读与工程细节再思考

第一,关于双图的对照阅读。油耗趋势(性能)与油费趋势(费用)并排阅读可以发现数据关系——5 月油耗最高(7.8)油费也偏高(850 元)、8 月油耗不高(7.2)但油费最高(1035 元,说明 8 月油价涨或跑得多)——两张图"横着看趋势、竖着看相关",油费 = 油耗 × 里程 × 油价的公式在两图对照中可见(油耗低但油费高 → 里程或油价因素),这是双图并置的价值(单张图看不到这种关系)。第二,关于图表容器的一致性。两张图容器同规格(白卡、16 内边距、标题 14 号、柱宽 24、底部月份 10 号)——同一页的双图统一容器参数(只有柱色和高度公式不同),视觉上"两张图是一套"(同规格不同指标),若容器参数不一致(柱宽不同、月份字号不同)会显得杂乱。第三,关于高度公式的可维护性。两种公式(油耗 × 14、油费 × 110 ÷ max)都是"魔法数字"写死——产品化应抽象为统一的图表组件(传入数值数组 + 公式类型:线性/比例,组件内部计算高度),避免每张图手写公式、也便于后续图表(里程趋势等)复用,这是"手绘图表 → 图表组件"的演进方向。这三个细节共同保证分析页"看得懂关系、看得顺视觉、改得动代码"。

十六、最后补充

分析页解析至此完整收尾:双指标汇总、油耗趋势柱、油费趋势柱全部覆盖。读者可重点回顾两种高度公式的选用(线性系数用于窄区间油耗、比例缩放用于宽区间油费)——这是系列图表设计中最精细的实现,也是"图表高度算法匹配数据分布"的典型示范。油耗与油费的双图对照让车主看清"车费不费油(7.2)与油钱花多少(4668)"的完整关系,这是加油记录数据分析的核心价值。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐