場景
產品需求,需要增加一個身高體重曲線的模組,設計圖如下
體重曲線也是這樣。準備
先來一張實現的粗略效果圖
曲線圖,折線圖等,大多使用流行強大的MPandroidChart
庫,這裡也是。
在網上找了一些類似的效果,都不是很符合設計圖,
實現
直接附上程式碼,註釋都很清楚。主要呼叫庫的方法
mLineChart = binding.lineChart
mLineChart.setDrawBorders(true)
var xAxis = mLineChart.xAxis
/*設定X軸的位置(預設在上方)*/
xAxis.position = XAxis.XAxisPosition.BOTTOM
/*設定X軸座標之間的最小間隔*/
xAxis.granularity = 1f
xAxis.mAxisMaximum = 72f
/*設定X軸值為字串*/
xAxis.setValueFormatter(object : IAxisValueFormatter {
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
return PinyinUtils.getFormatXLabel(value.toInt())
}
})
/* 右側Y軸不顯示*/
var axisRight = mLineChart.axisRight
axisRight.isEnabled = false
/*隱藏圖例*/
mLineChart.legend.isEnabled = false
/*隱藏描述*/
var description = Description()
description.isEnabled = false
mLineChart.description = description
/*X,Y軸同時縮放,false則X,Y軸單獨縮放,預設false*/
// mLineChart.setPinchZoom(true);
mLineChart.setScaleEnabled(false)
// 重置所有縮放與拖動,使圖示完全符合其邊界
// mLineChart.fitScreen();
/* y軸是否自動縮放;當縮放時,y軸的顯示會自動根據x軸範圍內資料的最大最小值而調整。財務報表比較有用,預設false*/
mLineChart.setAutoScaleMinMaxEnabled(true);
mLineChart.setExtraLeftOffset(10f); // 這個與上面的區別是不會忽略其自己計算的偏移。
// mLineChart.setDoubleTapToZoomEnabled(false);//雙擊螢幕縮放
// mLineChart.setScaleEnabled(false);
// mLineChart.setScaleXEnabled(true);
// mLineChart.setScaleYEnabled(false);
/* var axisLeft = mLineChart.getAxisLeft()
// axisLeft.axisMinimum = 45f
//如果設定為true那麼下面方法設定最小間隔生效,預設為false
axisLeft.setGranularityEnabled(true);
//設定Y軸的值之間的最小間隔。這可以用來避免價值複製當放大到一個地步,小數設定軸不再數允許區分兩軸線之間的值。
axisLeft.setGranularity(10f);
binding.viewModel?.requestGrowthData("1")*/
initHeightWidth(true)
//初始化
fun initHeightWidth(isHeight: Boolean) {
binding.tvTopTip.text = if (isHeight) "身高(cm)" else "體重(kg)"
mLineChart.setExtraLeftOffset(if (isHeight) 10f else 5f); // 這個與上面的區別是不會忽略其自己計算的偏移。
var axisLeft = mLineChart.getAxisLeft()
//如果設定為true那麼下面方法設定最小間隔生效,預設為false
axisLeft.setGranularityEnabled(true)
axisLeft.setGranularity(if (isHeight) 10f else 1f)
axisLeft.axisMinimum = if (isHeight) 45f else 1f
var xAxis = mLineChart.xAxis
/*設定X軸的位置(預設在上方)*/
xAxis.position = XAxis.XAxisPosition.BOTTOM
/*設定X軸座標之間的最小間隔*/
xAxis.granularity = 1f
xAxis.mAxisMaximum = 72f
/*設定X軸值為字串*/
xAxis.setValueFormatter(object : IAxisValueFormatter {
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
return PinyinUtils.getFormatXLabel(value.toInt())
}
})
binding.viewModel?.requestGrowthData(if (isHeight) "1" else "0")
}
//設定資料
fun setData(data: GrowthBean?) {
xList.clear()
//一個LineDataSet就是一條線
var entriesMax = mutableListOf<Entry>()
var entriesMin = mutableListOf<Entry>()
var entriesUser = mutableListOf<Entry>()
data?.max?.forEach {
entriesMax.add(Entry(it.x, it.y))
}
data?.min?.forEach {
xList.add("${it.x}")
entriesMin.add(Entry(it.x, it.y))
}
data?.user?.forEach {
entriesUser.add(Entry(it.x, it.y))
}
var set97 = LineDataSet(entriesMax, "97%")
set97.setColor(Color.parseColor("#FF7449"));
set97.setLineWidth(2f);
set97.setFillAlpha(65);
set97.setHighLightColor(Color.rgb(244, 117, 117));
set97.setDrawCircleHole(false);
set97.setDrawCircles(false)
set97.setDrawValues(false)
set97.setDrawFilled(true)
set97.setFillColor(Color.parseColor("#FFC9C3"));
set97.label = "97%"
set97.setDrawHighlightIndicators(false)
var set3 = LineDataSet(entriesMin, "3%")
set3.setColor(Color.parseColor("#FF7449"))
set3.setLineWidth(2f)
set3.setDrawCircleHole(false)
set3.setDrawCircles(false)
set3.setDrawValues(false)
set3.label = "3%"
set3.setDrawHighlightIndicators(false)
Collections.sort(entriesUser, EntryXComparator())
var setUser = LineDataSet(entriesUser, "寶寶")
setUser.setColor(Color.parseColor("#FD5A7B"))
setUser.setLineWidth(2f)
setUser.setDrawCircleHole(false)
setUser.setDrawCircles(true)
setUser.setDrawValues(true)
setUser.setCircleRadius(5f)
setUser.setCircleColor(Color.parseColor("#FD5A7B"))
setUser.setDrawHighlightIndicators(false)
var lineData: LineData
if (entriesUser.size > 0) {
lineData = LineData(set97, set3, setUser)
} else {
lineData = LineData(set97, set3)
}
//設定資料
mLineChart.setData(lineData)
//設定一頁最大顯示個數為6,超出部分就滑動
val ratio = xList.size.toFloat() / 6.toFloat()
//顯示的時候是按照多大的比率縮放顯示,1f表示不放大縮小
mLineChart.zoom(ratio, 1f, 0f, 1f)
mLineChart.isScaleYEnabled = false
mLineChart.setNoDataText("暫無資料")
//可以設定一條警戒線,如下:
val ll = LimitLine(data!!.monthAge.toFloat(), "今日")
ll.lineColor = Color.parseColor("#FF7449")
ll.lineWidth = 1f
ll.enableDashedLine(10f, 10f, 0f);
ll.textColor = Color.parseColor("#FF5A7D")
ll.textSize = 12f
ll.setLabelPosition(LimitLine.LimitLabelPosition.RIGHT_BOTTOM)
mLineChart.xAxis.addLimitLine(ll)
//移到某個位置
mLineChart.moveViewTo(data!!.monthAge.toFloat() - 3, if (isHeight) 160f else 80f, YAxis.AxisDependency.RIGHT)
/*渲染區間背景*/
set97.setFillFormatter(MyFillFormatter(set3))
mLineChart.renderer = MyLineLegendRenderer(mLineChart, mLineChart.animator, mLineChart.viewPortHandler)
}
複製程式碼
還有一個效果沒有實現,就是滑動懸浮的線上的數。
重點
渲染區間背景 是自定義的FillFormatter
public class MyFillFormatter implements IFillFormatter {
private ILineDataSet boundaryDataSet;
public MyFillFormatter() {
this(null);
}
//Pass the dataset of other line in the Constructor
public MyFillFormatter(ILineDataSet boundaryDataSet) {
this.boundaryDataSet = boundaryDataSet;
}
@Override
public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) {
return 0;
}
//Define a new method which is used in the LineChartRenderer
public List<Entry> getFillLineBoundary() {
if(boundaryDataSet != null) {
return ((LineDataSet) boundaryDataSet).getValues();
}
return null;
}}
複製程式碼
呼叫:
/*渲染區間背景*/
set97.setFillFormatter(MyFillFormatter(set3))
mLineChart.renderer = MyLineLegendRenderer(mLineChart, mLineChart.animator, mLineChart.viewPortHandler)
複製程式碼
這裡是在Stack Overflow裡看到的 參考連結1 參考連結2
#最後 附上參考的文章