鸿蒙图表MarkerView遮挡解决方案

鸿蒙项目接入了@ohos/mpchart图表框架,在实现MarkerView时出现了MarkerView被遮挡问题,体验感不佳,查找官方文档没有一个有效的设置方式,以下是被遮挡截图

解决方案使用ArkTS中的Stack包裹图表控件,在包裹一个Stack组件用于Popup来实现气泡效果

复制代码
Stack
    |--- LineChart
    |--- Stack().bindPopup用于实现MarkerView效果

前置依赖准备

  • 先通过鸿蒙ohpm命令安装@ohos/mpchart三方库,确保版本为最新版,我这里用的是V3.0.30,该版本已适配HarmonyOS NEXT的API接口规范。
  • 导入所需核心类:AxisDependency、EntryOhos、LineChart、LineChartModel、OnChartValueSelectedListener等图表相关API
TypeScript 复制代码
ohpm i @ohos/mpchart

状态变量定义

通过OnChartValueSelectedListener回调,在用户点击/长按图表数据点时,同步获取数据信息和屏幕像素坐标

TypeScript 复制代码
@Entry
@ComponentV2
struct LineChartDemo {
  // 线形图配置构建类
  @Local lineChartModel: LineChartModel | undefined = undefined;
  // Popup气泡位置
  @Local positionX: number = 0;
  @Local positionY: number = 0;
  // 选中点的Popup气泡文字描述
  @Local popupMessage: string = '';
  // 气泡显示控制变量
  @Local showUI: boolean = false;
  @Local handlePopup: boolean = false;
  // 图表数据选择监听事件
  private valueSelectedListener: OnChartValueSelectedListener = {
    onValueSelected: (e: EntryOhos, h: Highlight) => {
      // 触发气泡显示
      this.showUI = true;
      // 格式化获取选中点的Popup气泡文字描述
      this.popupMessage = `${e.getData()?.toString()}\n${e.getY().toFixed(2)}元`;
      // 转换数据点为屏幕像素坐标
      this.positionX = this.lineChartModel?.getTransformer(AxisDependency.LEFT)?.getPixelForValues(e.getX(), e.getY()).x ?? 0;
      this.positionY = this.lineChartModel?.getTransformer(AxisDependency.LEFT)?.getPixelForValues(e.getX(), e.getY()).y ?? 0;
      this.handlePopup = true;
    },
    onNothingSelected: () => {
      // 取消选中时隐藏气泡
      this.showUI = false;
      this.handlePopup = false;
      this.popupMessage = '';
    }
  }
}

图表基础初始化

在aboutToAppear生命周期中完成图表数据和基础样式配置

TypeScript 复制代码
aboutToAppear(): void {
    this.lineChartModel = new LineChartModel();
    this.lineChartModel.setOnChartValueSelectedListener(this.valueSelectedListener);
    this.lineChartModel.setHitTestMode(HitTestMode.Block);
    this.lineChartModel.setExtraOffsets(10, 10, 10, 10);
    this.lineChartModel.setNoDataText('没有可查看数据');
    //不显示图表描述信息
    this.lineChartModel.getDescription()?.setEnabled(false);
    //不支持图表缩放
    this.lineChartModel.setScaleEnabled(false);
    this.lineChartModel.animateXY(500, 500);
    // 改变y标签的位置
    let leftAxis = this.lineChartModel.getAxisLeft();
    if (leftAxis) {
      leftAxis.setAxisMinimum(0);
      const color = ColorUtil.parseColor('#e5e5e5');
      leftAxis.setAxisLineColor(color);
      leftAxis.setGridColor(color);
      leftAxis.setValueFormatter(new leftAxisValueFormatter());
    }
    //右y轴不显示
    this.lineChartModel.getAxisRight()?.setEnabled(false);
    //x坐标轴
    let xAxis = this.lineChartModel.getXAxis();
    if (xAxis) {
      const color = ColorUtil.parseColor('#e5e5e5');
      xAxis.setPosition(XAxisPosition.BOTTOM);
      xAxis.setAxisLineColor(color);
      xAxis.setGridColor(color);
    }
    //获取图表的图例
    let legend = this.lineChartModel.getLegend();
    if (legend) {
      legend.setForm(LegendForm.LINE);
      // 图例换行
      legend.setWordWrapEnabled(true);
      //图例设置在下方
      legend.setVerticalAlignment(LegendVerticalAlignment.BOTTOM);
      legend.setDrawInside(false);
      legend.setFormSize(8);
      legend.setFormToTextSpace(4);
      legend.setXEntrySpace(6);
    }
    const days = 30;
    // 测试数据
    let dataSetList = new JArrayList<ILineDataSet>();
    let entryList = new JArrayList<EntryOhos>();
    for (let day = 1; day < (days + 1); day++) {
      const date = `9月${day}日`;
      const entry = new EntryOhos(day, Math.floor(Math.random() * 200), undefined, date);
      everyDayEntryList.add(entry);
    }
    let label = `标签`;
    let lineDataSet = new LineDataSet(entryList, label);
    lineDataSet.setMode(LineDataSetMode.HORIZONTAL_BEZIER);
    lineDataSet.setCubicIntensity(0.2);
    lineDataSet.setDrawFilled(true);
    lineDataSet.setDrawCircles(true);
    lineDataSet.setLineWidth(1);
    lineDataSet.setCircleRadius(3);
    lineDataSet.setCircleHoleRadius(2);
    lineDataSet.setHighlightEnabled(true);
    lineDataSet.setCircleColor(ColorUtil.parseColor('#f2ad0b'));
    lineDataSet.setCircleHoleColor(Color.White);
    lineDataSet.setColorByColor(ColorUtil.parseColor('#f2ad0b'));
    lineDataSet.setFillColor(ColorUtil.parseColor('#3bf2ad0b'));
    dataSetList.add(lineDataSet);

    let lineData = new LineData(dataSetList);
    lineData.setDrawValues(false);
    this.lineChartModel.setData(lineData);
    this.lineChartModel.invalidate();
}

气泡绑定实现

在build方法中通过bindPopup方法将气泡与选中点位置绑定,实现跟随数据点展示的效果

TypeScript 复制代码
build() {
  Column() {
    Stack() {
      LineChart({ model: this.lineChartModel })
        .width('100%')
        .height('100%')
      if (this.showUI) {
        // 占位元素用于锚定Popup气泡位置
        Stack()
          .width(1)
          .height(1)
          .position({ x: this.positionX, y: this.positionY })
          .bindPopup(this.handlePopup, {
            message: this.popupMessage,
            messageOptions: {
              font: { size: 10 },
              textColor: '#FFFFFF'
            },
            radius: 5,
            placementOnTop: true,
            popupColor: '#8E8E8E',
            backgroundBlurStyle: BlurStyle.NONE,
            onStateChange: (e) => {
              // 气泡关闭时同步取消图表选中状态
              if (!e.isVisible) {
                this.handlePopup = false;
                this.lineChartModel?.highlightValueForObject(null);
              }
            }
          });
      }
    }
    .width('100%')
    .height('311')
  }.padding(15)
   .width('100%')
   .height('100%')
}

自定义样式扩展

可以通过修改Popup配置项实现更多自定义效果:

  • 调整 placement 参数控制气泡在数据点的上下方位
  • 自定义气泡的背景色、文字大小、圆角等样式
相关推荐
SoFarAway6 小时前
拆解华为一碰传
华为·逆向
贾伟康7 小时前
【共创稿事节】HarmonyOS 7.0 Progress进度体系:从接入到生产验证
性能优化·实战·harmonyos·arkts·arkui
贾伟康14 小时前
HarmonyOS API 26 HMAF Skill 端侧 A2A 消息去重与上下文最小化
harmonyos·arkts·skill·a2a·hmaf
贾伟康14 小时前
HarmonyOS API 26 HMAF Skill 多 Skill 冲突仲裁与可恢复执行
harmonyos·arkts·智能体·skill·hmaf
钉先生贾14 小时前
毕业项目:从上游更新到回归发布的完整维护闭环
华为·harmonyos
行者-全栈开发15 小时前
【鸿蒙心迹】权限申请全流程——从声明到用户授权的完整链路(HarmonyOS 7.x)
华为·harmonyos
KevinChaos1 天前
阳台小花园开发手记 01|HarmonyOS 7.0 互动卡片:小花园为何要两套界面
华为·harmonyos
Ranger09291 天前
下一代鸿蒙打包工具链:arkdown
harmonyos
程序猿追2 天前
一个工程面向手机与二合一:HarmonyOS 6 部署配置和本地手记
华为·智能手机·harmonyos
行者-全栈开发2 天前
【鸿蒙心迹】深色模式适配全链路——从配置到自定义(HarmonyOS 7.x)
华为·harmonyos