百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

鸿蒙开源第三方组件——MPAndroidChart_ohos 图表绘制组件

bigegpt 2024-08-10 12:15 3 浏览

前言

本组件是基于安卓平台的图表绘制组件MPAndroidChart(https://github.com/PhilJay/MPAndroidChart),实现了其核心功能的鸿蒙化迁移和重构。目前代码已经开源到(https://gitee.com/isrc_ohos/mpandroid-chart_ohos),欢迎各位下载使用并提出宝贵意见!

背景

安卓版本的MPAndroidChart在GitHub上有超过3.3万个Star和8.3k个Fork,应该说是目前使用最广,体验最佳的开源图表库。它具绘制折线图、饼图、雷达图等图表的能力,用户只需要自己写一个数据接口,即可实现各种精美数据曲线的绘制,在一定程度上满足了大部分业务的需求。

本组件是MPAndroidChart的鸿蒙化版本,名为MPAndroidChart_ohos,实现了其核心功能。

组件效果展示

目前MPAndroidChart_ohos具有折线图和直方图两种图表绘制能力。下面将分别展示其折线图和直方图的绘制效果。

1、折线图

图1展示了一个由随机数据生成的折线图。MPAndroidChart_ohos继承了原版MPAndroidChart的优秀特性,提供了多种多样的用户自定义接口,例如:

(1) X、Y轴自定义。使用者可以自定义X、Y轴的位置,例如在这个sample里就绘制了左Y轴和上X轴。

(2)辅助线自定义。使用者可以选择是否显示辅助线(或格点线),也可以自由设定辅助线的位置。

(3)图表美化。使用者可以设置图表曲线的各种属性(颜色、粗细等),还可以对曲线包裹区域进行填充。

2、直方图

图2是基于假设场景“2020年1月1日 ~ 15日的小卖部收益情况”绘制的图表。基于这个背景,使用MPAndroidChart_ohos制作了一张直方图。


Sample解析

图1和图2主要依靠调用Library中的能力绘制,在Sample中的实现主要由图3中红框所示的两个文件来完成。

如果用户想要绘制图表,只需要完成以下几个步骤即可:

(1)选择图表种类。

(2)设置属性。

(3)导入数据

1、选择图表种类

MPAndroidChart_ohos提供了折线图和直方图的绘制能力,使用者只需要根据自身需求选择需要使用的能力即可。

LineChart chart = new LineChart(context);  //折线图的初始化
BarChart chart = new BarChart(context);    // 直方图的初始化

2、设置属性

MPAndroidChart_ohos提供了图表样式自定义的能力,使用者可以通过调用Library暴露的接口来给图表添加、修改、删除各项属性。例如使用者想要自定义轴线,可以通过实例化XAxis 类的对象,然后通过对象的各种方法实现修改X轴的颜色,设置最大值、最小值等:

XAxis xAxis = chart.getXAxis();  // 实例化
xAxis.setAxisMaximum(20f);   //属性设置
xAxis.setAxisMinimum(0f);
xAxis.setAxisLineColor(Color.BLACK.getValue());

除了轴线设置以外还可以在图表中加入各种辅助线,例如想要在x = 2处添加一条辅助线,可以通过实例化LimitLine 类的对象,然后通过对象的各种方法实现修改辅助线的宽度、标签位置、文本大小等:

LimitLine llXAxis = new LimitLine(2f, "辅助线:x=2");  // 实例化
llXAxis.setLineWidth(4f);                       //属性设置
llXAxis.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM);
llXAxis.setTextSize(10f);
llXAxis.setTypeface(Font.DEFAULT);


3、导入数据

在MPAndroidChart_ohos中,不同类型的图表有着不同的数据类,例如折线图的数据类为LineData,直方图的数据类为BarData,为什么不能仅仅通过一个简单int[]或者float[]作为数据类呢?这是因为在MPAndroidChart_ohos中数据类的作用不仅仅是承载数据,同时还需要承载一些图表相关的属性,例如曲线颜色、曲线粗细、数据点颜色、大小等,这样做的意图在后续Library分析时会讲到。

以折线图为例,导入数据的过程如下:

(1)创建LineDataSet类:

LineDataSet set1 = new LineDataSet(values, label);

其中values是使用者想要绘制的一类数据,一般是float[],label是这类数据的标签。

(2)将一类或者几类数据放置到一个ArrayList中

ArrayList<ILineDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);

(3)将ArrayList做成LineData数据类,并传递给chart

LineData data = new LineData(dataSets);
chart.setData(data);


Library解析

1、工程结构对比

从图4中的两张图的对比可以看出,MPAndroidChart_ohos是按照MPAndroidChart工程的结构开发的,实现了其主要功能。相较于MPAndroidChart,虽然MPAndroidChart_ohos缺少exception、highlight、jobs这几个文件夹,但并不影响其主要功能的使用。

2、多设备适配

为了增加多设备适配性,MPAndroidChart内部以dp(density independent pixels)为单位来计算图表中各个部件的相对位置,在绘制图表时,统一将dp数据转化为pixel数据,在这个过程中就需要系统提供一些显示信息。在安卓中,这些信息由DisplayMetrics来提供,如下代码可以通过上下文获取到DisplayMetrics:

Resources res = context.getResources();
mMetrics = res.getDisplayMetrics();

接下来通过DisplayMetrics可以获取到屏幕的DPI,dp * DPI即为屏幕的pixel:

public static float convertDpToPixel(float dp) {
        return dp * mMetrics.density;
}


在鸿蒙系统中,显示信息通过DisplayAttribute类来获取,以下代码可以获取到DisplayAttribute:

Display display = DisplayManager.getInstance().getDefaultDisplay(this.getContext()).get();
DisplayAttribute displayAttribute = display. getAttributes()

可以看出与安卓还是有些许不同的。得到DisplayAttribute后即可得到屏幕DPI,需要注意的是代表DPI的接口与安卓不同:

public static float convertDpToPixel(float dp) {
        return dp * mMetrics.densityPixels;
}


3、轴线绘制

轴线是一张图的基准,在MPAndroidChart中,轴线甚至作为了图表种类的分类基准!看似MPAndroidChart提供了十余种图表绘制的能力,其实这十余种图表是依托于两种轴线制作的,这两种轴线分别是平面直角坐标系和极坐标系。

在直角坐标系下,MPAndroidChart实现了折线图、散点图、直方图、气泡图、蜡烛图等。

在极坐标系下,MPAndroidChart实现了饼图、雷达图。

在MPAndroidChart_ohos中,和轴线相关的类主要分布在components文件夹和renderer文件夹中:

其中AxisBase类主要定义了轴应具备的属性,例如颜色、粗细、位置、刻度、标签、最值等。XAxis和YAxis继承自AxisBase,并分别定义了X、Y轴所应具备的属性,例如:X轴的位置属性应是“Top”、“BOTTOM”、“TOP_INSIDE”、“BOTTOM_INSIDE”或“BOTH_SIDED”中的一种;而Y轴与X轴不同,其位置属性应为“LEFT”或“RIGHT”。

AxisRenderer类是绘制轴线的基类,其定义了绘制轴线所必备的属性和方法,例如用于绘制轴线、标签、辅助线、格点的几种画笔(Paint)和对应的方法接口。XAxisRenderer和YAxisRenderer继承自AxisRenderer,实现了其中用于绘制的接口,真正实现了轴线的绘制。其他的诸如XAxisRenderHorizontalBarChart类从名字上看也容易得知是在一些特殊图表上绘制轴线用的。

4、数据绘制

在Sample解析中提到对于不同类型的图表,需要不同的数据类去承载数据和属性。数据类的继承关系是MPAndroidChart中比较复杂的一部分内容,举一个例子来说,我们绘制折线图所需的LineData类,它继承自:

public class LineData extends BarLineScatterCandleBubbleData<ILineDataSet> {

类名有点长,不过没关系,继续向下寻找:

public abstract class BarLineScatterCandleBubbleData<T extends IBarLineScatterCandleBubbleDataSet<? extends Entry>> extends ChartData<T> {

ChartData类应该就是根了:

public abstract class ChartData<T extends IDataSet<? extends Entry>> {


看似三级继承关系并不算多,但是值得注意的是期间需要实现的接口和泛型参数是非常多的,这些接口和泛型往往还都能继续向下嵌套好多层,这着实给移植工作带来了一些困难。下面来看看这些数据类是做什么的。

ChartData类是数据类的基类,在其中首先定义了数据的上界和下界分别是浮点数所能代表的最大和最小值,同时该类提供了一些数据处理方法,例如如果发现任何数超过了上、下界,都将这些数强制赋值为上、下界,避免溢出带来的数据错误。同时这个类还提供了诸如查询数据点个数、查询数据X、Y值、查询标签、查询最大、最小值等数据查询方法。

BarLineScatterCandleBubbleData和LineData分别是对ChartData的一次和二次封装,本身并没有添加任何方法,只是通过实现接口与各种泛型参数对存入其中的数据格式加以限制。

那么数据点和曲线是如何绘制到图表中的?DataRenderer是数据绘制的基类,其中写出了绘制数据、曲线、标签等的抽象方法。继续以折线图为例,这些抽象方法将在DataRendereràBarLineScatterCandleBubbleRendereràLineScatterCandleRadarRendereràLineRadarRendereràLineChartRenderer这个继承路径中被逐步实现,最终LineChartRenderer实现了绘制折线图的全部能力。

项目贡献人

吴圣垚 郑森文 朱伟 陈美汝 张馨心

相关推荐

ActiveAndroid使用(对象化数据库)

配置模块的build.gradlerepositories{mavenCentral()mavenLocal()maven{url"https://oss.sonatype.org/conte...

AndroidStudio下的依赖管理(android app依赖外部jar包)

在开发中用第三方库是很常见的事,如何在AndroidStudio下管理这些依赖呢?这就是这篇文章的目的。目录Maven/Ivy仓库依赖Module依赖aar文件依赖jar文件依赖例子完整代码一、Mav...

Android Studio之gradle的配置与介绍

1、gradle的简单介绍Gradle是可以用于Android开发的新一代的BuildSystem,也是AndroidStudio默认的build工具。其实Gradle脚本是基于一种JVM语言—...

Android中的run-as命令带来的安全问题

一、前言最近一周比较忙,没时间写东西了,今天继续开始我们今天的话题:run-as命令,在上周的开发中,遇到一个问题,就是在使用run-as命令的时候出现了一个错误,不过当时因为工作进度的问题,这问题就...

Android系统级深入开发——input驱动程序

1、Input驱动程序是Linux输入设备的驱动程序,分成游戏杆(joystick)、鼠标(mouse和mice)和事件设备(Eventqueue)3种驱动程序。其中事件驱动程序是目前通用的驱动程序...

Android项目中如何用好构建神器Gradle?

CSDN移动将持续为您优选移动开发的精华内容,共同探讨移动开发的技术热点话题,涵盖移动应用、开发工具、移动游戏及引擎、智能硬件、物联网等方方面面。如果您想投稿、参与内容翻译工作,或寻求近匠报道,请发送...

Android Studio自定义文件类头(android studio自定义标题栏)

--简书作者谢恩铭转载请注明出处今天给大家介绍一个很简单的"小"技巧。平时,我们在AndroidStudio中开发Android时,总免不了要创建新的文件,也许是Java文件,也许是C...

C语言#include头文件真的是插入代码吗?

若文章对您有帮助,欢迎关注程序员小迷。助您在编程路上越走越好!编译器理论和实作既是又不是。从编译器理论理解,#include头文件"相当于"插入了头文件的代码,以供源代码引用(宏定...

Android 系统核心机制binder(03)binder C++层实现

本章关键点总结&说明:这里主要关注BinderC++部分即可,看到,也是本章节的核心内容,主要就是以C++封装的框架为主来解读binder。之前主要针对于底层驱动binder的数据交互以及...

Java对象序列化与反序列化的那些事

Java对象序列化与反序列化的那些事在Java的世界里,对象序列化和反序列化就像一对孪生兄弟,它们共同构成了Java对象存储和传输的基础。如果你曾经尝试将对象保存到文件中,或者在网络中传输对象,那么你...

Java对象序列化剖析(java 对象序列化)

对象序列化的目的1)希望将Java对象持久化在文件中2)将Java对象用于网络传输实现方式如果希望一个类的对象可以被序列化/反序列化,那该类必须实现java.io.Serializable接口或jav...

C++模板 - 16(SFINAE)(c++模板编程)

C++支持函数重载,同一个函数名,只要它的签名不一样,可以声明若干个版本(这个特性也是必须的,不然构造函数就只能有一个了)。现在函数的重载集合中又加入了新的成员-函数模板,事情就变得越发有趣起来,...

NewtoSoft.Json相关使用技巧(newtosoft.json相关使用技巧有哪些)

  本篇将为大家介绍Newtonsoft.Json的一些高级用法,可以修改很少的代码解决上述问题。Newtonsoft.Json介绍  在做开发的时候,很多数据交换都是以json格式传输的。而使用Js...

C#调用DeepSeek API(c#调用deepseek api 流式输出)

一、官方网站二、DeepSeek测试DeepSeek三大适用模式:基础模型(V3)、深度思考(R1)、联网搜索。基础模型(V3)深度思考(R1)联网搜索三、C#调用DeepSeekAPI核心代码//...

.NET性能系列文章二:Newtonsoft.Json vs System.Text.Json

微软终于追上了?图片来自GlennCarstens-Peters[1]Unsplash[2]欢迎来到.NET性能系列的另一章。这个系列的特点是对.NET世界中许多不同的主题进行研究、基准和比较...