原文地址: 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库绘制出图表

折线图实现示例

效果:

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),)//构造绘制的modelval 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= 1gridLineDashStyle = 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)}@Keepdata class AAColumnRange(    /**     * 柱形图的圆角     */    var borderRadius: Number,    /**     * 柱形图的宽度     */    var pointWidth: Number,    /**     * 柱形图的边框宽度     */    var borderWidth: Number = 0,    /**     * 每个柱形图的颜色     */    var colors: List,    /**     * 设置为true才会使用上面的颜色数组     */    var dashStyle: AAChartLineDashStyleType,    var colorByPoint: Boolean = true,)

柱形图渐变色实现

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

效果图:

@Keepdata class AAColumnGradientColor(val linearGradient: LinearGradient, val stops: List<List>)@Keepdata class LinearGradient(val x1: Float, val y1: Float, val x2: Float, val y2: Float)@Keepdata 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.xAxisxAxis.valueFormatter = IndexAxisValueFormatter(labels)// 将数据集添加到数据对象中val lineData = LineData(listOf(createDataSet(),createDataSet()))chart.data = lineDatachart.invalidate()val yAxisLeft = chart.axisLeftyAxisLeft.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.GREENdataSet.lineWidth = 2fdataSet.setDrawCircles(true)//设置每个坐标点的颜色dataSet.setDrawCircleHole(true)dataSet.circleHoleColor = Color.GREENdataSet.circleRadius = 4fdataSet.valueTextSize = 12f//需要曲线dataSet.mode = LineDataSet.Mode.HORIZONTAL_BEZIERreturn dataSet}/** * 图表组件的一些配置 */private fun configChartView() {val chart = binding.mpChart// 显示动画chart.animateXY(1000, 1000)// 设置属性chart.description.isEnabled = falsechart.setTouchEnabled(true)chart.isDragEnabled = truechart.setScaleEnabled(true)chart.setPinchZoom(true)//不显示底部的图例样式chart.legend.form = Legend.LegendForm.NONEval xAxis = chart.xAxis//设置x坐标在下面(默认是在上面的)xAxis.position = XAxis.XAxisPosition.BOTTOMxAxis.granularity = 1fxAxis.textColor = Color.parseColor("#898989")xAxis.textSize = SizeUtils.dp2px(10f).toFloat()// 设置 Y 轴坐标值val yAxisLeft = chart.axisLeftyAxisLeft.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()}

提问之前,请先看提问须知点击右侧图标发起提问或者加入QQ群一起学习TornadoFx学习交流群:1071184701<!––>