鸿蒙开发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

相关文章

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

使用Python实现图像LBP特征提取的操作方法

《使用Python实现图像LBP特征提取的操作方法》LBP特征叫做局部二值模式,常用于纹理特征提取,并在纹理分类中具有较强的区分能力,本文给大家介绍了如何使用Python实现图像LBP特征提取的操作方... 目录一、LBP特征介绍二、LBP特征描述三、一些改进版本的LBP1.圆形LBP算子2.旋转不变的LB

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

C# Where 泛型约束的实现

《C#Where泛型约束的实现》本文主要介绍了C#Where泛型约束的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用的对象约束分类where T : structwhere T : classwhere T : ne

将Java程序打包成EXE文件的实现方式

《将Java程序打包成EXE文件的实现方式》:本文主要介绍将Java程序打包成EXE文件的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录如何将Java程序编程打包成EXE文件1.准备Java程序2.生成JAR包3.选择并安装打包工具4.配置Launch4

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Python实现特殊字符判断并去掉非字母和数字的特殊字符

《Python实现特殊字符判断并去掉非字母和数字的特殊字符》在Python中,可以通过多种方法来判断字符串中是否包含非字母、数字的特殊字符,并将这些特殊字符去掉,本文为大家整理了一些常用的,希望对大家... 目录1. 使用正则表达式判断字符串中是否包含特殊字符去掉字符串中的特殊字符2. 使用 str.isa

Spring Boot 集成 Quartz并使用Cron 表达式实现定时任务

《SpringBoot集成Quartz并使用Cron表达式实现定时任务》本篇文章介绍了如何在SpringBoot中集成Quartz进行定时任务调度,并通过Cron表达式控制任务... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启动 Sprin