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

相关文章

Python38个游戏开发库整理汇总

《Python38个游戏开发库整理汇总》文章介绍了多种Python游戏开发库,涵盖2D/3D游戏开发、多人游戏框架及视觉小说引擎,适合不同需求的开发者入门,强调跨平台支持与易用性,并鼓励读者交流反馈以... 目录PyGameCocos2dPySoyPyOgrepygletPanda3DBlenderFife

使用Python开发一个Ditto剪贴板数据导出工具

《使用Python开发一个Ditto剪贴板数据导出工具》在日常工作中,我们经常需要处理大量的剪贴板数据,下面将介绍如何使用Python的wxPython库开发一个图形化工具,实现从Ditto数据库中读... 目录前言运行结果项目需求分析技术选型核心功能实现1. Ditto数据库结构分析2. 数据库自动定位3

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

Django开发时如何避免频繁发送短信验证码(python图文代码)

《Django开发时如何避免频繁发送短信验证码(python图文代码)》Django开发时,为防止频繁发送验证码,后端需用Redis限制请求频率,结合管道技术提升效率,通过生产者消费者模式解耦业务逻辑... 目录避免频繁发送 验证码1. www.chinasem.cn避免频繁发送 验证码逻辑分析2. 避免频繁

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连