鸿蒙开发5.0【高级图表实现】 解决方案

2024-09-07 01:12

本文主要是介绍鸿蒙开发5.0【高级图表实现】 解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

方案描述

mpchart是一个包含各种类型图表的图表库,主要用于业务数据汇总,例如销售数据走势图,股价走势图等场景中使用,方便开发者快速实现图表UI,mpchart主要包括线形图、柱状图、饼状图、蜡烛图、气泡图、雷达图、瀑布图等自定义图表库。

使用准备

  1. 下载三方库控制台输入:ohpm install @ohos/mpchart。

  2. 初始化图表配置构建类。

    初始化三方库得构建类,图表数据,线性数据等。

    import { LineChart, LineChartModel } from '@ohos/mpchart'; // 初始化图表配置构建类
    import { XAxis, XAxisPosition } from '@ohos/mpchart'; // x轴
    import { YAxis, AxisDependency, YAxisLabelPosition } from '@ohos/mpchart'; // Y轴
    import { LineData } from '@ohos/mpchart'; // 生成图表数据
    import { LineDataSet, Mode } from '@ohos/mpchart'; //线形图数据集合
    import { EntryOhos } from '@ohos/mpchart'; //图表数据结构基础类
    
  3. 配置图表指定样式。

    注:此步骤是初始化底层的表框架,也是就后面的网格。

    图表生成分为x、y轴。

    setPosition设置:

    x轴(getXAxis)得显示范围:TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE,x轴只需要设置一次即可。

    y轴(getAxisLeft,getAxisRight)表内左右显示范围:OUTSIDE_CHART, INSIDE_CHART,如需只设置一侧,创建实例时,添加属性添加所需侧即可。

    let topAxis = this.model.getXAxis();//图表x轴
    if (topAxis) {topAxis.setLabelCount(5, false);//设置绘制标签个数topAxis.setPosition(XAxisPosition.TOP);//设置标签位置topAxis.setAxisMinimum(0); //设置最小值topAxis.setAxisMaximum(50); //设置最大值topAxis.setDrawAxisLine(false)// 启用绘制x轴轴线topAxis.setDrawLabels(false) // 不绘制x轴标签topAxis.setDrawGridLines(false)// 不绘制X轴网格线
    }
    ​
    let leftAxis = this.model.getAxisLeft();
    if (leftAxis) {leftAxis = new YAxis(AxisDependency.LEFT);leftAxis.setLabelCount(6, false);leftAxis.setDrawGridLines(true);//启用绘制左侧Y轴网格线leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART);leftAxis.setAxisMinimum(0);leftAxis.setAxisMaximum(50);leftAxis.setDrawLabels(true);leftAxis.setDrawGridLines(true);
    }
    ​
    let rightAxis = this.model.getAxisRight();
    if (rightAxis) {rightAxis = new YAxis(AxisDependency.RIGHT);rightAxis.setDrawGridLines(true);rightAxis.setLabelCount(6, false);rightAxis.setAxisMinimum(0);rightAxis.setAxisMaximum(50);rightAxis.setDrawAxisLine(false);rightAxis.setDrawLabels(true);rightAxis.setDrawGridLines(true);
    }
    ​
    this.model.setData(this.lineData);
    

应用场景

解决方案:mpchart

场景一:线性表虚实线交接【曲线】

实现效果

图 1
1
“zh-cn_image_0000001971495045”)

核心代码

我们需要写一个MyLineDataSet类,继承自LineDataSet,也就是线型图的数据类。为什么需要这个类呢?因为我们需要在初始化数据的时候定义这个虚实相接的线是从哪里开始由实线变为虚线的,这里MyLineDataSet类的构造方法比它的父类多了一个interval参数,也就是虚实分隔点。

import { EntryOhos, JArrayList, LineDataSet } from '@ohos/mpchart';
​
export class MyLineDataSet extends LineDataSet {interval: number = 0;constructor(yVals: JArrayList<EntryOhos> | null, label: string, interval: number) {super(yVals, label);this.interval = interval;}
}

定义好自己的数据类之后,就要定义MyRender类了,实线具体的绘制功能,MyRender类继承自LineChartRenderer,因为是要绘制曲线,所以重写的是drawCubicBezier方法,MyRender类的代码。

import { EntryOhos, JArrayList, LineDataSet } from '@ohos/mpchart';export default class MyRender extends LineChartRenderer {protected drawCubicBezier(c: CanvasRenderingContext2D, dataSet: MyLineDataSet) {if (!this.mChart || !this.mXBounds) {return;}const PHASE_Y: number = this.mAnimator ? this.mAnimator.getPhaseY() : 1;const TRANS: Transformer | null = this.mChart.getTransformer(dataSet.getAxisDependency());this.mXBounds.set(this.mChart, dataSet);const intensity: number = dataSet.getCubicIntensity();let lineCubicPath = new Path2D();let dotsCubicPath = new Path2D();if (this.mXBounds.range >= 1) {let prevDx: number = 0;let prevDy: number = 0;let curDx: number = 0;let curDy: number = 0;const FIRST_INDEX: number = this.mXBounds.min + 1;const INTERVAL: number = dataSet.interval;let prevPrev: EntryOhos | null;let prev: EntryOhos | null = dataSet.getEntryForIndex(Math.max(FIRST_INDEX - 2, 0));let cur: EntryOhos | null = dataSet.getEntryForIndex(Math.max(FIRST_INDEX - 1, 0));let next: EntryOhos | null = cur;let nextIndex: number = -1;if (cur === null) return;Utils.resetContext2DWithoutFont(c, this.mRenderPaint);// let the spline startlineCubicPath.moveTo(cur.getX(), cur.getY() * PHASE_Y);for (let j: number = this.mXBounds.min + 1; j <= this.mXBounds.range + this.mXBounds.min; j++) {prevPrev = prev;prev = cur;cur = nextIndex === j ? next : dataSet.getEntryForIndex(j);nextIndex = j + 1 < dataSet.getEntryCount() ? j + 1 : j;next = dataSet.getEntryForIndex(nextIndex);prevDx = (cur.getX() - prevPrev.getX()) * intensity;prevDy = (cur.getY() - prevPrev.getY()) * intensity;curDx = (next.getX() - prev.getX()) * intensity;curDy = (next.getY() - prev.getY()) * intensity;if (j < INTERVAL) {lineCubicPath.bezierCurveTo(prev.getX() + prevDx,(prev.getY() + prevDy) * PHASE_Y,cur.getX() - curDx,(cur.getY() - curDy) * PHASE_Y,cur.getX(),cur.getY() * PHASE_Y);if (j === INTERVAL - 1) {dotsCubicPath.moveTo(cur.getX(), cur.getY());}} else {dotsCubicPath.bezierCurveTo(prev.getX() + prevDx,(prev.getY() + prevDy) * PHASE_Y,cur.getX() - curDx,(cur.getY() - curDy) * PHASE_Y,cur.getX(),cur.getY() * PHASE_Y);}}}// if filled is enabled, close the pathif (dataSet.isDrawFilledEnabled()) {let cubicFillPath: Path2D = new Path2D();cubicFillPath.addPath(lineCubicPath);if (c && TRANS) {this.drawCubicFill(c, dataSet, cubicFillPath, TRANS, this.mXBounds);}}this.mRenderPaint.setColor(dataSet.getColor());this.mRenderPaint.setStyle(Style.STROKE);if (TRANS && TRANS.pathValueToPixel(lineCubicPath)) {lineCubicPath = TRANS.pathValueToPixel(lineCubicPath);}if (TRANS && TRANS.pathValueToPixel(dotsCubicPath)) {dotsCubicPath = TRANS.pathValueToPixel(dotsCubicPath);}Utils.resetContext2DWithoutFont(c, this.mRenderPaint);c.beginPath();c.stroke(lineCubicPath);c.closePath();Utils.resetContext2DWithoutFont(c, this.mRenderPaint);c.beginPath();c.setLineDash([5, 5, 0])c.stroke(dotsCubicPath);c.closePath();this.mRenderPaint.setDashPathEffect(null);}
}

这个方法主要内容就是定义了两条path2D,也就是线段来绘制实线和虚线。

//实线
let solidLinePath = new Path2D();
//虚线
let dashedLinePath = new Path2D();
复制

绘制方法如下:

solidLinePath.bezierCurveTo(prev.getX() + prevDx,(prev.getY() + prevDy) * phaseY,cur.getX() - curDx,(cur.getY() - curDy) * phaseY,cur.getX(),cur.getY() * phaseY
);

就是调用path2D的方法bezierCurveTo方法,这个方法有6个参数,分别是控制点1的(x值,y值 )和 控制点2的(x值,y值)以及目标点的(x值,y值)。直接把父类的方法抄过来即可。

我们需要有一个if判断,if(j <= dataSet.interval)就是当j小于dataSet.interval时,写绘制实线的方法,当j等于dataSet.interval时,虚线要moveTo当前位置;当j大于dataSet.interval时,就调用dashedLinePath.bezierCurveTo方法绘制虚线了。

最后绘制方法是调用c.stroke方法绘制的。c.setLineDash([5,5,0]);是设置虚线效果。

Utils.resetContext2DWithoutFont(c, this.mRenderPaint);
c.beginPath();
c.stroke(solidLinePath);
c.closePath();
​
Utils.resetContext2DWithoutFont(c, this.mRenderPaint);
c.beginPath();
c.setLineDash([5,5,0]);
c.stroke(dashedLinePath);
c.closePath();

最后就是使用,代码如下:

// 创建一个 JArrayList 对象,用于存储 EntryOhos 类型的数据
let values: JArrayList<EntryOhos> = new JArrayList<EntryOhos>();
// 循环生成 1 到 6 的随机数据,并添加到 values 中
//i为图表内数据线的节点数
for (let i = 1; i <= 6; i++) {values.add(new EntryOhos(i, Math.random() * 100));
}
// 创建 LineDataSet 对象,使用 values 数据,并设置数据集的名称为 'DataSet'
//3是图表内数据线实线的数量(按节点来算,如果2个节点就为一条线性数据)
let dataSet = new MyLineDataSet(values, 'DataSet', 3);
//设置线条模式
dataSet.setMode(Mode.CUBIC_BEZIER);
​
let dataSetList: JArrayList<ILineDataSet> = new JArrayList<ILineDataSet>();
dataSetList.add(dataSet);
// 创建 LineData 对象,使用 dataSetList数据,并将其传递给model
let lineData: LineData = new LineData(dataSetList);
this.model?.setData(lineData);
//canvas绘制this.model.setRenderer(new MyRender(this.model, this.model.getAnimator()!, this.model.getViewPortHandler()))、
//动画播放时长
this.model.animateX(5000);

场景二:线性表模拟数据图表实时变化

实现效果

  • 绘制图表:添加底层图表数据。
  • 绘制线标:添加实时线性数据。
  • 清空:清空线性数据。

2

核心代码

初始化底层表格后,需要给表格上绑定数据。

首先判定data是否有数据,不为空的话获取这个DataSet这个容器,如果容器DataSet为空,添加样式即可。

调用接口addEntry填充数据,将之前的生成的数据用set.getEntryCount一起添加,再利用随机数填充新数据,从0索引开始。

notifyDataChanged计算数据的最大值和最小值,notifyDataSetChanged触发数据更新,以x轴移动。

注:每次通过调用addEntry()获取set容器不断更新里面的数据做渲染。

addEntry() {let data = this.model.getData();//获取数据if (data != null) {let set = data.getDataSetByIndex(0);if (set == null) {set = this.createSet();//填充样式data.addDataSet(set);}data.addEntry(new EntryOhos(set.getEntryCount(), (Math.random() * 40) + 30), 0);//填充数据data.notifyDataChanged();this.model.notifyDataSetChanged();//触发坐标轴数据更新this.model.setVisibleXRangeMaximum(50);// 设置图表最大的X轴显示范围,如不设置,则默认显示全部数据this.model.moveViewToX(data.getEntryCount());//以X轴移动}
}

场景三:线性表自定义表节点和线性节点

实现效果

上方slider:x轴传值监听,下方slider:y轴传值监听。

3

注册监听事件"seekBarXValueWatch"和"seekBarYValueWatch",滑动组件Slider,当值变化时触发监听,传递给setData。

核心代码

@State @Watch("seekBarXValueWatch") seekBarX: SeekBarModel = new SeekBarModel()//x轴传值监听
@State @Watch("seekBarYValueWatch") seekBarY: SeekBarModel = new SeekBarModel()//Y轴传值监听
​
seekBarXValueWatch(): void {this.setData(this.seekBarX.getValue(), this.seekBarY.getValue());}seekBarYValueWatch(): void {this.setData(this.seekBarX.getValue(), this.seekBarY.getValue());}

将监听后数据传入setData,经过计算得出X,Y轴的数量,传入数据结构基础类JArrayList,添加样式,绑定图表。

注:每次滑动slider改变的时候,触发监听将值传递给setData重新计算数据,达到变化效果。

private async setData(count: number, range: number): Promise<void> {let start: number = 1;let values: JArrayList<EntryOhos> = new JArrayList<EntryOhos>();for (let i = start; i < count; i++) { //X轴坐标个数// 设置数据点Y值let val = Math.random() * range; //Y轴值values.add(new EntryOhos(i, val));}//添加样式this.dataSet = new LineDataSet(values, "DataSet 1");this.dataSet.setDrawIcons(false)this.dataSet.setDrawFilled(true);//是否显示阴影this.dataSet.setDrawValues(true);this.dataSet.setMode(Mode.LINEAR);//直线模式this.dataSet.setColorByColor(ChartColor.rgb(140, 234, 255));//设置折线颜色this.dataSet.setLineWidth(1.5)this.dataSet.setDrawCircles(true);//折线点画圆圈this.dataSet.setDrawIcons(false) // 设置是否绘制数据项图标this.dataSet.setCircleColor(ChartColor.rgb(140, 234, 255));//设置圆环颜色// 设置数据点的半径this.dataSet.setCircleRadius(4);//设置内径this.dataSet.setCircleHoleRadius(2)// 设置半径大小
​this.dataSet.setCircleHoleColor(Color.White)this.dataSet.setDrawCircleHole(true)//设置内部孔let gradientFillColor = new JArrayList<ChartColorStop>();//渐变颜色类 设置一种颜色即可折现内部gradientFillColor.add(["#FFB6C1", 0.2]);gradientFillColor.add(["#DA70D6", 0.4]);gradientFillColor.add(["#0000FF", 0.6]);gradientFillColor.add(["#FFD700", 0.8]);gradientFillColor.add(["#90EE90", 1.0]);this.dataSet.setGradientFillColor(gradientFillColor);//塞入渐变数据this.dataSet.setDrawFilled(true);//填充绘制
​let dataSetList: JArrayList<ILineDataSet> = new JArrayList<ILineDataSet>();dataSetList.add(this.dataSet); 线形图数据集合的操作类添加dataset设置好的数据
​let lineData: LineData = new LineData(dataSetList);//生成图表数据lineData.setValueTextSize(10);//设置文本大小
​if (this.model) {this.model.setData(lineData);// 将数据与图表配置类绑定}
}

注意事项

aboutToAppear定义x/y轴的外层图表时,x轴只用定义一次,Y轴根据业务来。

设置渐变颜色线性内的范围不是所有渐变,而只是最表层,如需设置渐变,只推荐两种相邻颜色过度。

以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

2

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!
3

这篇关于鸿蒙开发5.0【高级图表实现】 解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143636

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo