原文地址: Android图表开源库调研及使用示例 - Stars-One的杂货小窝

之前做的几个项目都是需要实现图表统计展示,于是做之前调研了下,做下记录

概述

AAChartCore-Kotlin 基于webview,本质上还是使用js

PhilJay/MPAndroidChart,老牌使用原生canvas绘制的图标,但似乎因为收费而不再更新了,文档不全

目前用的就是AAChartCore-Kotlin这个库

还有些其他的,没细看了,链接贴出来:

ECharts-Android:https://github.com/AnyChart/AnyChart-Android

AnyChart-Android:https://github.com/AnyChart/AnyChart-Android

HelloCharts:https://github.com/lecho/hellocharts-android

GraphView:https://github.com/jjoe64/GraphView

AAChartCore-Kotlin

基于hcharts这个js库整的图表库,所以如果有不能实现的效果,可以先去看下js库的实现

实质上Android库只是封装了对应的实体类,之后也是会将实体类转为json配置,从而让js库绘制出图表

折线图实现示例

效果:

android:id="@+id/aa_chart_view"

android:layout_width="match_parent"

android:layout_height="match_parent">

val arrayData = arrayOf(

AASeriesElement()

.name("Tokyo")

.color("#e5473d")

.data(arrayOf(86,125,112,168,123,131,119,95,112,86)),

AASeriesElement()

.name("NewYork")

.color("#40a0e2")

.data(arrayOf(42,89,76,126,87,91,73,67,80,110)),

AASeriesElement()

.name("London")

.color("#0bb142")

.data(arrayOf(40,78,62,83,58,67,31,53,68,74)),

)

val aaChartModel = AAChartModel()

.chartType(AAChartType.Spline)

.backgroundColor("#2f2f2f")

.dataLabelsEnabled(false)

.markerSymbol(AAChartSymbolType.Circle)

.zoomType(AAChartZoomType.X)

.series(arrayData)

.categories(arrayOf(

"5.19",

"5.20",

"5.21",

"5.22",

"5.23",

"5.24",

"5.25",

"5.26",

"5.27",

"5.28",

))

/*图表视图对象调用图表模型对象,绘制最终图形*/

binding.aaChartView.aa_drawChartWithChartModel(aaChartModel)

有两种绘制数据的方法:

aa_drawChartWithChartModel(AAChartModel)

aa_drawChartWithChartOptions(AAOptions)

AAChartModel可以转为AAOptions,通过aa_toAAOptions()方法

说明

AAOptions实际上就是官方对应的json数据格式,如果需要自定义设置,可以参考官方的API文档,对AAOptions里的数据进行修改

// 图表配置

var options = {

chart: {

type: 'bar' //指定图表的类型,默认是折线图(line)

},

title: {

text: '我的第一个图表' // 标题

},

xAxis: {

categories: ['苹果', '香蕉', '橙子'] // x 轴分类

},

yAxis: {

title: {

text: '吃水果个数' // y 轴标题

}

},

series: [{ // 数据列

name: '小明', // 数据列名

data: [1, 0, 4] // 数据

}, {

name: '小红',

data: [5, 7, 3]

}]

};

// 图表初始化函数

var chart = Highcharts.chart('container', options);

上面的options变量,就是对应的AAOptions这个实体类(转换json的操作不需要我们去做)

官方demo如何参考?

首先,我们到此网站Highcharts 演示 | Highcharts,找到一个符合我们需要的基本图表,比如说我找了个基础折线图 | Highcharts

之后可以看到,下面有个编辑源代码的功能,点击进去会进入到一个在线运行js代码的网站

此时,我们再打开Highcharts JS API 文档,对demo进行样式上的修改,最终得到我们需要的js代码

之后参考js代码,来进行我们AAChartCore-Kotlin的代码配置设置AAOptions即可

当然,建议还是先看一遍官方文档,比如这个文章图表主要组成 | Highcharts 使用教程,可以知道图表的基本组成及一些通用名称,这样后面去找api文档也比较方便

AAChartCore-Kotlin封装的时候,可能有些属性没有考虑到,所以这个时候我们没法通过官方的属性来进行设置,而且官方的很多配置类都是final类型,没有open关键字,导致我们需要去修改源码

于是我就是给开发者提了几个pull request,虽然都被接受合并了,不过开发者什么时候发个版本不确定,于是我就fork一份,自行改了并发布JitPack(各位有需要可以使用我的版本)

版本也没什么大改动,只是改了下某些类的开放性,方便继承扩展其他属性字段,依赖如下:

implementation 'com.github.stars-one:AAChartCore-Kotlin:1.0'

左右滑动的实现

也是看了半天的js库的文档和尝试,才实现的效果

//这几个是数据

val arrayData = arrayOf(

AASeriesElement()

.color("#e5473d")

.data(sysArr),

AASeriesElement()

.color("#40a0e2")

.data(diaArr),

AASeriesElement()

.color("#0bb142")

.data(pulesArr),

)

//构造绘制的model

val aaChartModel = AAChartModel()

.chartType(AAChartType.Spline)

.backgroundColor("#2f2f2f")

.dataLabelsEnabled(false)

.markerSymbol(AAChartSymbolType.Circle)

.zoomType(AAChartZoomType.X)

.categories(categoriesArr)

.series(arrayData)

.yAxisVisible(true)

.touchEventEnabled(false)

.tooltipEnabled(false)

.legendEnabled(false)

.scrollablePlotArea(AAScrollablePlotArea().minWidth(categoriesArr.size*40).scrollPositionX(0))

核心的方法zoomType(AAChartZoomType.X)和.scrollablePlotArea(AAScrollablePlotArea().minWidth(categoriesArr.size*40).scrollPositionX(0))

这里要给图表设置个最小宽度才能实现左右滑动,我就以每个数据给了40px的宽度,这里各位可以看情况改

柱形图(范围)代码示例

实现效果如下下图:

private fun initChart() {

val jsArray = arrayOf(

arrayOf(60,130),

arrayOf(80,190),

arrayOf(60,130),

arrayOf(90,160),

arrayOf(68,145),

arrayOf(75,126),

arrayOf(67,139),

)

val categoriesArr = arrayOf(

"5.19",

"5.20",

"5.21",

"5.22",

"5.23",

"5.24",

"5.25"

)

val kotlinArray: Array =

jsArray.map { it.map { it.toInt() }.toTypedArray() }.toTypedArray()

val arrayData = arrayOf(

AASeriesElement()

.marker(AAMarker().enabled(false))

.data(kotlinArray)

)

val aaChartModel = AAChartModel()

.categories(categoriesArr)

.legendEnabled(false)

.touchEventEnabled(false)

.tooltipEnabled(false)

.chartType(AAChartType.Columnrange)

.xAxisVisible(true)

.yAxisVisible(true)

.yAxisLabelsEnabled(true)

.xAxisLabelsEnabled(true)

.backgroundColor("#04081a")

.dataLabelsEnabled(false)

.gradientColorEnable(true)

.borderRadius(25)

.markerRadius(25)

.series(arrayData)

val options = aaChartModel.aa_toAAOptions().apply {

val list = listOf("#d14664")

val colorList = (0..6).map { list.random() }

yAxis?.apply {

//改变y坐标轴的宽度(设置

//lineWidth = 0

//y坐标轴的颜色

//lineColor = "red"

//y坐标轴的左边文本隐藏

title(AATitle().text(""))

//y坐标轴的水平刻度的样式

gridLineColor = "#2b2745"

gridLineWidth= 1

gridLineDashStyle = AAChartLineDashStyleType.LongDash.value

//起始刻度

min=30

//刻度间隔

tickInterval = 30

}

xAxis?.apply {

lineWidth=0

}

plotOptions?.columnrange =

AAColumnRange(

SizeUtils.dp2px(2f),

SizeUtils.dp2px(8f),

0,

colorList,

AAChartLineDashStyleType.Dash

)

}

/*图表视图对象调用图表模型对象,绘制最终图形*/

binding.aaChartView.aa_drawChartWithChartOptions(options)

}

@Keep

data class AAColumnRange(

/**

* 柱形图的圆角

*/

var borderRadius: Number,

/**

* 柱形图的宽度

*/

var pointWidth: Number,

/**

* 柱形图的边框宽度

*/

var borderWidth: Number = 0,

/**

* 每个柱形图的颜色

*/

var colors: List,

/**

* 设置为true才会使用上面的颜色数组

*/

var dashStyle: AAChartLineDashStyleType,

var colorByPoint: Boolean = true,

)

柱形图渐变色实现

原仓库是没有封装渐变的属性的,这样我就是参考js代码及对应的文档,整了几个类来实现

效果图:

@Keep

data class AAColumnGradientColor(val linearGradient: LinearGradient, val stops: List>)

@Keep

data class LinearGradient(val x1: Float, val y1: Float, val x2: Float, val y2: Float)

@Keep

data class AAColumnRange(

/**

* 柱形图的圆角

*/

var borderRadius: Number,

/**

* 柱形图的宽度

*/

var pointWidth: Number,

/**

* 柱形图的边框宽度

*/

var borderWidth: Number = 0,

/**

* 每个柱形图的颜色

*/

var colors: List,

/**

* y轴水平刻度线条

*/

var dashStyle: AAChartLineDashStyleType,

/**

* 设置为true才会使用上面的颜色colors数组

*/

var colorByPoint: Boolean = true,

)

val gradient = AAColumnGradientColor(

LinearGradient(0f, 0f, 0f, 1f),

listOf(

listOf(0, "#fceed3"), // 颜色的起始位置

listOf(1, "#ce395a") // 颜色的结束位置

)

)

val list = listOf(gradient)

plotOptions?.columnrange =AAColumnRange(

SizeUtils.dp2px(2f),

SizeUtils.dp2px(8f),

0,

colorList,

AAChartLineDashStyleType.Dash

)

混淆规则

# 图表库

-keep class com.github.aachartmodel.** {*;}

//这个是官方文档上写的混淆规则,如果出现问题可以使用上面这个

-keep class com.github.aachartmodel.aainfographics.** { *; }

注意:如果使用上面自定义了些字段,对应的类也要添加忽略混淆,不然图表的样式会出现问题,我上面是加了个@Keep注解来忽略

MPChart

似乎是开发者要搞收费了,文档都看不到,暂且记录下之前写的一个简单图表demo代码示例

private fun testDrawChart2() {

val chart = binding.mpChart

// 设置 X 轴坐标值

val labels = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")

val xAxis = chart.xAxis

xAxis.valueFormatter = IndexAxisValueFormatter(labels)

// 将数据集添加到数据对象中

val lineData = LineData(listOf(

createDataSet(),

createDataSet()

))

chart.data = lineData

chart.invalidate()

val yAxisLeft = chart.axisLeft

yAxisLeft.removeAllLimitLines()

chart.invalidate()

}

private fun createDataSet(): LineDataSet {

// 创建数据点 (日期, 温度)

val entries =(0..6).map{

Entry(it.toFloat(),Random.nextInt(0..30).toFloat())

}

//val entries = listOf(

//

// Entry(1f, 27.4f),

// Entry(2f, 28.9f),

// Entry(3f, 29.6f),

// Entry(4f, 25.8f),

// Entry(5f, 26.2f),

// Entry(6f, 30.2f),

// Entry(7f, 31.1f)

//)

// 将数据点添加到数据集中

val dataSet = LineDataSet(entries, "")

dataSet.color = Color.GREEN

dataSet.lineWidth = 2f

dataSet.setDrawCircles(true)

//设置每个坐标点的颜色

dataSet.setDrawCircleHole(true)

dataSet.circleHoleColor = Color.GREEN

dataSet.circleRadius = 4f

dataSet.valueTextSize = 12f

//需要曲线

dataSet.mode = LineDataSet.Mode.HORIZONTAL_BEZIER

return dataSet

}

/**

* 图表组件的一些配置

*/

private fun configChartView() {

val chart = binding.mpChart

// 显示动画

chart.animateXY(1000, 1000)

// 设置属性

chart.description.isEnabled = false

chart.setTouchEnabled(true)

chart.isDragEnabled = true

chart.setScaleEnabled(true)

chart.setPinchZoom(true)

//不显示底部的图例样式

chart.legend.form = Legend.LegendForm.NONE

val xAxis = chart.xAxis

//设置x坐标在下面(默认是在上面的)

xAxis.position = XAxis.XAxisPosition.BOTTOM

xAxis.granularity = 1f

xAxis.textColor = Color.parseColor("#898989")

xAxis.textSize = SizeUtils.dp2px(10f).toFloat()

// 设置 Y 轴坐标值

val yAxisLeft = chart.axisLeft

yAxisLeft.setStartAtZero(false)

yAxisLeft.setTextColor(Color.parseColor("#898989"))

yAxisLeft.setTextSize(SizeUtils.dp2px(10f).toFloat())

// 创建 LimitLine 对象,并设置相关属性

val limitLine = LimitLine(30f, "Threshold")

limitLine.lineColor = Color.RED // 设置线条颜色为红色

limitLine.lineWidth = 2f // 设置线条宽度为 2 像素

limitLine.enableDashedLine(10f,5f,0f)

// 添加 LimitLine 对象到 YAxis 对象中,并刷新图表

yAxisLeft.addLimitLine(limitLine)

chart.invalidate()

}