之前做的幾個專案都是需要實現圖表統計展示,於是做之前調研了下,做下記錄
目前用的就是AAChartCore-Kotlin這個庫
還有些其他的,沒細看了,連結貼出來:
基於hcharts這個js庫整的圖表庫,所以如果有不能實現的效果,可以先去看下js庫的實現
實質上Android庫只是封裝了對應的實體類,之後也是會將實體類轉為json設定,從而讓js庫繪製出圖表
效果:
<com.github.aachartmodel.aainfographics.aachartcreator.AAChartView
android:id="@+id/aa_chart_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.github.aachartmodel.aainfographics.aachartcreator.AAChartView>
val arrayData = arrayOf<Any>(
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)
有兩種繪製資料的方法:
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的操作不需要我們去做)
首先,我們到此網站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<Any>(
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<Any> =
jsArray.map { it.map { it.toInt() }.toTypedArray() }.toTypedArray()
val arrayData = arrayOf<Any>(
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<String>,
/**
* 設定為true才會使用上面的顏色陣列
*/
var dashStyle: AAChartLineDashStyleType,
var colorByPoint: Boolean = true,
)
原倉庫是沒有封裝漸變的屬性的,這樣我就是參考js程式碼及對應的檔案,整了幾個類來實現
效果圖:
@Keep
data class AAColumnGradientColor(val linearGradient: LinearGradient, val stops: List<List<Any>>)
@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<AAColumnGradientColor>,
/**
* 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
註解來忽略
似乎是開發者要搞收費了,檔案都看不到,暫且記錄下之前寫的一個簡單圖表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()
}