How to draw gridlines for ECG graph plot using MPChart Android - android

I am developing an application to show ECG waveform in an android application. I tried using MPAndroidchart and XYPlotlibrary to plot the ECG data. I can able to see Major grid lines, but my requirement is to show both Major and minor gridlines So that it will resemble Ecg graph paper. Can anyone have any suggestion or suggest any library to achieve this functionality?

It's not possible to draw minor grid lines using a built-in chart capability with MPAndroidChart, but you can still add them manually to get the desired appearance.
For example:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val chart = findViewById<LineChart>(R.id.chart)
val lines = mutableListOf<LineDataSet>()
val x = (0..100).map { i -> 0.1f*i }
val y = x.map { i -> 5*sin(7*i)*sin(0.5f*i)* cos(3.25f*i) }
val primaryLine = LineDataSet(x.zip(y).map { Entry(it.first, it.second) }, "primary")
primaryLine.setDrawCircles(false)
primaryLine.lineWidth = 3f
primaryLine.color = Color.RED
primaryLine.setDrawValues(false)
primaryLine.isHighlightEnabled = false
val majorX = 2f
val majorY = 4f
val minorX = majorX/5
val minorY = majorY/5
val xmin = floor(x.min()/majorX)*majorX
val xmax = ceil(x.max()/majorX)*majorX
val ymin = floor(y.min()/majorY)*majorY
val ymax = ceil(y.max()/majorY)*majorY
xGridLines(lines, minorX, ymin, ymax, xmin, xmax, false)
yGridLines(lines, minorY, ymin, ymax, xmin, xmax, false)
xGridLines(lines, majorX, ymin, ymax, xmin, xmax, true)
yGridLines(lines, majorY, ymin, ymax, xmin, xmax, true)
lines.add(primaryLine)
chart.axisRight.isEnabled = false
val yAx = chart.axisLeft
yAx.setDrawLabels(false)
yAx.setDrawGridLines(false)
yAx.setDrawAxisLine(false)
yAx.axisMinimum = ymin
yAx.axisMaximum = ymax
val xAx = chart.xAxis
xAx.setDrawLabels(false)
xAx.position = XAxis.XAxisPosition.BOTTOM
xAx.setDrawGridLines(false)
xAx.setDrawAxisLine(false)
xAx.axisMinimum = xmin
xAx.axisMaximum = xmax+0.01f
chart.data = LineData(lines.toList())
chart.description.isEnabled = false
chart.legend.isEnabled = false
}
private fun yGridLines(lines: MutableList<LineDataSet>, spacing: Float, ymin: Float, ymax: Float, xmin: Float, xmax: Float, major: Boolean) {
val nY = ((ymax-ymin)/spacing).roundToInt()
for(i in 0..nY) {
val yl = ymin + i*spacing
val ep = listOf(Entry(xmin,yl), Entry(xmax,yl))
lines.add(makeGridLineDataSet(ep, major))
}
}
private fun xGridLines(lines: MutableList<LineDataSet>, spacing: Float, ymin: Float, ymax: Float, xmin: Float, xmax: Float, major: Boolean) {
val nX = ((xmax-xmin)/spacing).roundToInt()
for(i in 0..nX) {
val xl = xmin + i*spacing
val ep = listOf(Entry(xl, ymin), Entry(xl,ymax))
lines.add(makeGridLineDataSet(ep, major))
}
}
private fun makeGridLineDataSet(e: List<Entry>, major: Boolean): LineDataSet {
val ds = LineDataSet(e,"")
ds.setDrawCircles(false)
if( major ) {
ds.color = Color.BLACK
ds.lineWidth = 1f
}
else {
ds.color = Color.BLACK
ds.lineWidth = 0.5f
ds.enableDashedLine(10f,10f,0f)
}
ds.setDrawValues(false)
ds.isHighlightEnabled = false
return ds
}
Which produces

Related

I have a line chart that I want to use to show temperature, but I want to show icons and time in x axis

what I want to achieve
what I have
I want to show icons on the xAxis above time but line chart takes icon and places them on the value where temperature is shown. I have searched a lot but could not find the answer. Any help would be appreciated. I have tried a lot of things but all in vein.
private fun setTempChart(hour: ArrayList<Hour>, id: String) {
val entries: MutableList<Entry> = ArrayList()
for (i in hour.indices) {
val code = hour[i].condition.code
val icon =
if (hour[i].is_day == 1) requireActivity().setIconDay(code) else requireActivity().setIconNight(
code
)
entries.add(Entry(i.toFloat(), sharedPreference.temp?.let {
hour[i].temp_c.setCurrentTemperature(
it
).toFloat()
}!!))
}
val dataSet = LineDataSet(entries, "")
dataSet.apply {
lineWidth = 0f
setDrawCircles(false)
setDrawCircleHole(false)
isHighlightEnabled = false
valueTextColor = Color.WHITE
setColors(Color.WHITE)
valueTextSize = 12f
mode = LineDataSet.Mode.CUBIC_BEZIER
setDrawFilled(true)
fillColor = Color.WHITE
valueTypeface = typeface
isDrawIconsEnabled
setDrawIcons(true)
valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return String.format(Locale.getDefault(), "%.0f", value)
}
}
}
val lineData = LineData(dataSet)
chart.apply {
description.isEnabled = false
axisLeft.setDrawLabels(false)
axisRight.setDrawLabels(false)
legend.isEnabled = false
axisLeft.setDrawGridLines(false)
axisRight.setDrawGridLines(false)
axisLeft.setDrawAxisLine(false)
axisRight.setDrawAxisLine(false)
setScaleEnabled(false)
data = lineData
setVisibleXRange(8f, 8f)
animateY(1000)
xAxis.apply {
setDrawAxisLine(false)
textColor = Color.WHITE
setDrawGridLines(false)
setDrawLabels(true)
position = XAxis.XAxisPosition.BOTTOM
textSize = 12f
valueFormatter = MyAxisFormatter(hour, id)
isGranularityEnabled = true
granularity = 1f
labelCount = entries.size
}
}
}
I am using MPAndroidChart library
There isn't a nice built-in way to draw icons like that, but you can do it by making a custom extension of the LineChartRenderer and overriding drawExtras. Then you can get your icons from R.drawable.X and draw them on the canvas wherever you want. There is some work to figure out where to put them to line up with the data points, but you can copy the logic from drawCircles to find that.
Example Custom Renderer
inner class MyRenderer(private val context: Context,
private val iconY: Float,
private val iconSizeDp: Float,
chart: LineDataProvider,
animator: ChartAnimator,
viewPortHandler: ViewPortHandler)
: LineChartRenderer(chart, animator, viewPortHandler) {
private var buffer: FloatArray = listOf(0f,0f).toFloatArray()
override fun drawExtras(c: Canvas) {
super.drawExtras(c)
val iconSizePx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
iconSizeDp,
resources.displayMetrics
)
// get the icons you want to draw
val cloudy = ContextCompat.getDrawable(context, R.drawable.cloudy)
val sunny = ContextCompat.getDrawable(context, R.drawable.sunny)
if( cloudy == null || sunny == null ) {
throw RuntimeException("Missing drawables")
}
// Determine icon width in pixels
val w = iconSizePx
val h = iconSizePx
val dataSets = mChart.lineData.dataSets
val phaseY = mAnimator.phaseY
for(dataSet in dataSets) {
mXBounds.set(mChart, dataSet)
val boundsRange = mXBounds.range + mXBounds.min
val transformer = mChart.getTransformer(dataSet.axisDependency)
for(j in mXBounds.min .. boundsRange) {
val e = dataSet.getEntryForIndex(j) ?: break
buffer[0] = e.x
buffer[1] = iconY * phaseY
transformer.pointValuesToPixel(buffer)
if( !mViewPortHandler.isInBoundsRight(buffer[0])) {
break
}
if( !mViewPortHandler.isInBoundsLeft(buffer[0]) ||
!mViewPortHandler.isInBoundsY(buffer[1])) {
continue
}
// Draw the icon centered under the data point, but at a fixed
// vertical position. Here the icon "sits on top" of the
// specified iconY value
val left = (buffer[0]-w/2).roundToInt()
val right = (buffer[0]+w/2).roundToInt()
val top = (buffer[1]-h).roundToInt()
val bottom = (buffer[1]).roundToInt()
// Alternately, use this to center the icon at the
// "iconY" value
//val top = (buffer[1]-h/2).roundToInt()
//val bottom = (buffer[1]+h/2).roundToInt()
// Use whatever logic you want to select which icon
// to use at each position
val icon = if( e.y > 68f ) {
sunny
}
else {
cloudy
}
icon.setBounds(left, top, right, bottom)
icon.draw(c)
}
}
}
}
Using the Custom Renderer
val iconY = 41f
val iconSizeDp = 40f
chart.renderer = MyRenderer(this, iconY, iconSizeDp,
chart, chart.animator, chart.viewPortHandler)
(other formatting)
val chart = findViewById<LineChart>(R.id.chart)
chart.axisRight.isEnabled = false
val yAx = chart.axisLeft
yAx.setDrawLabels(false)
yAx.setDrawGridLines(false)
yAx.setDrawAxisLine(false)
yAx.axisMinimum = 40f
yAx.axisMaximum = 80f
val xAx = chart.xAxis
xAx.setDrawLabels(false)
xAx.position = XAxis.XAxisPosition.BOTTOM
xAx.setDrawGridLines(false)
xAx.setDrawAxisLine(false)
xAx.axisMinimum = 0.5f
xAx.axisMaximum = 5.5f
xAx.granularity = 0.5f
val x = listOf(0,1,2,3,4,5,6)
val y = listOf(60,65,66,70,65,50,55)
val e = x.zip(y).map { Entry(it.first.toFloat(), it.second.toFloat())}
val line = LineDataSet(e, "temp")
line.setDrawValues(true)
line.setDrawCircles(false)
line.circleRadius = 20f // makes the text offset up
line.valueTextSize = 20f
line.color = Color.BLACK
line.lineWidth = 2f
line.setDrawFilled(true)
line.fillColor = Color.BLACK
line.fillAlpha = 50
line.mode = LineDataSet.Mode.CUBIC_BEZIER
line.valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return "%.0f F".format(value)
}
}
chart.data = LineData(line)
chart.description.isEnabled = false
chart.legend.isEnabled = false
Gives the desired effect

How to rotate an 3D model using DragGesture in Android SceneForm

I followed the solution here for rotating a TransformableNode on the X axis based on the user's DragGesture, using the Sceneform Android SDK. However, I would also like to rotate on the Y and Z axis as well, similar to how ARCore SceneViewer does it.
How can I achieve that?
What I have currently is on the left (rotates only on X axis), and what is desired is on the right (rotates on all axes, as in ARCore Scene Viewer).
class DragRotationController(transformableNode: BaseTransformableNode, gestureRecognizer: DragGestureRecognizer) :
BaseTransformationController<DragGesture>(transformableNode, gestureRecognizer) {
// Rate that the node rotates in degrees per degree of twisting.
var rotationRateDegrees = 0.5f
public override fun canStartTransformation(gesture: DragGesture): Boolean {
return transformableNode.isSelected
}
public override fun onContinueTransformation(gesture: DragGesture) {
var localRotation = transformableNode.localRotation
val rotationAmountX = gesture.delta.x * rotationRateDegrees
val rotationDeltaX = Quaternion(Vector3.up(), rotationAmountX)
localRotation = Quaternion.multiply(localRotation, rotationDeltaX)
// *** this only rotates on X axis. How do I rotate on all axes? ***
transformableNode.localRotation = localRotation
}
public override fun onEndTransformation(gesture: DragGesture) {}
}
I was able to find a working solution here: https://github.com/chnouman/SceneView
Here are the relevant snippets of code, with some of my adaptations to make it work for .glb files.
I have forked this repo and am working on keyboard input support if anyone's interested in that.
Rendering the object:
private fun renderLocalObject() {
skuProgressBar.setVisibility(View.VISIBLE)
ModelRenderable.builder()
.setSource(this,
RenderableSource.builder().setSource(
this,
Uri.parse(localModel),
RenderableSource.SourceType.GLB)/*RenderableSource.SourceType.GLTF2)*/
.setScale(0.25f)
.setRecenterMode(RenderableSource.RecenterMode.ROOT)
.build())
.setRegistryId(localModel)
.build()
.thenAccept { modelRenderable: ModelRenderable ->
skuProgressBar.setVisibility(View.GONE)
addNodeToScene(modelRenderable)
}
Adding the object to the SceneView:
private fun addNodeToScene(model: ModelRenderable) {
if (sceneView != null) {
val transformationSystem = makeTransformationSystem()
var dragTransformableNode = DragTransformableNode(1f, transformationSystem)
dragTransformableNode?.renderable = model
sceneView.getScene().addChild(dragTransformableNode)
dragTransformableNode?.select()
sceneView.getScene()
.addOnPeekTouchListener { hitTestResult: HitTestResult?, motionEvent: MotionEvent? ->
transformationSystem.onTouch(
hitTestResult,
motionEvent
)
}
}
}
Custom TransformableNode:
class DragTransformableNode(val radius: Float, transformationSystem: TransformationSystem) :
TransformableNode(transformationSystem) {
val dragRotationController = DragRotationController(
this,
transformationSystem.dragRecognizer
)
}
Custom TransformationController:
class DragRotationController(
private val transformableNode: DragTransformableNode,
gestureRecognizer: DragGestureRecognizer
) :
BaseTransformationController<DragGesture>(transformableNode, gestureRecognizer) {
companion object {
private const val initialLat = 26.15444376319647
private const val initialLong = 18.995950736105442
var lat: Double = initialLat
var long: Double = initialLong
}
// Rate that the node rotates in degrees per degree of twisting.
private var rotationRateDegrees = 0.5f
public override fun canStartTransformation(gesture: DragGesture): Boolean {
return transformableNode.isSelected
}
private fun getX(lat: Double, long: Double): Float {
return (transformableNode.radius * Math.cos(Math.toRadians(lat)) * Math.sin(Math.toRadians(long))).toFloat()
}
private fun getY(lat: Double, long: Double): Float {
return transformableNode.radius * Math.sin(Math.toRadians(lat)).toFloat()
}
private fun getZ(lat: Double, long: Double): Float {
return (transformableNode.radius * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(long))).toFloat()
}
override fun onActivated(node: Node?) {
super.onActivated(node)
Handler().postDelayed({
transformCamera(lat, long)
}, 0)
}
public override fun onContinueTransformation(gesture: DragGesture) {
val rotationAmountY = gesture.delta.y * rotationRateDegrees
val rotationAmountX = gesture.delta.x * rotationRateDegrees
val deltaAngleY = rotationAmountY.toDouble()
val deltaAngleX = rotationAmountX.toDouble()
long -= deltaAngleX
lat += deltaAngleY
//lat = Math.max(Math.min(lat, 90.0), 0.0)
transformCamera(lat, long)
}
private fun transformCamera(lat: Double, long: Double) {
val camera = transformableNode.scene?.camera
var rot = Quaternion.eulerAngles(Vector3(0F, 0F, 0F))
val pos = Vector3(getX(lat, long), getY(lat, long), getZ(lat, long))
rot = Quaternion.multiply(rot, Quaternion(Vector3.up(), (long).toFloat()))
rot = Quaternion.multiply(rot, Quaternion(Vector3.right(), (-lat).toFloat()))
camera?.localRotation = rot
camera?.localPosition = pos
}
fun resetInitialState() {
transformCamera(initialLat, initialLong)
}
public override fun onEndTransformation(gesture: DragGesture) {}
}

How to set xAxis display certain value independent from entry using mpchart

For example, I have a data set like following:
val dataSetX = {101, 205, 210, 445, 505}
val dataSetY = {50, 100, 150, 200, 250}
The labels on the xAxis of the chart will be 101, 205, 295, 445, 505.
But I want them to be 100,200,300,400,500, without changing the data.
What do I do?
To be able to render custom labels independent from the Data Entry List you need to implement a custom XAxisRenderer and override the function fun drawLabels(c: Canvas?, pos: Float, anchor: MPPointF?) which is called when the labels are ready to be drawn on the Canvas. The default implementation uses the mXAxis.mEntryCount and mXAxis.mEntries to render the labels in the xAxis which are calculated internally by the library. What you can do is to modify the superclass implementation by overriding it without calling the super.drawLabels(c, pos, anchor) method and make the adjustments needed to handle your custom labels. Below i will describe how you can achieve this.
1.) First declare your CustomXAxisRenderer which extends from XAxisRenderer like the below example:
class CustomXAxisRenderer(viewPortHandler: ViewPortHandler?, xAxis: XAxis?, trans: Transformer?, renderXArray : Array<Float>, formatter: ValueFormatter)
: XAxisRenderer(viewPortHandler, xAxis, trans) {
private val xArray : Array<Float>
private val xValueFormatter: ValueFormatter
init {
xArray = renderXArray
xValueFormatter = formatter
}
override fun drawLabels(c: Canvas?, pos: Float, anchor: MPPointF?) {
//don't call the super class to draw the default x points
//super.drawLabels(c, pos, anchor)
val labelRotationAngleDegrees = mXAxis.labelRotationAngle
val centeringEnabled = false//mXAxis.isCenterAxisLabelsEnabled
val positions = FloatArray(xArray.size * 2)
for(i in positions.indices step 2){
// only fill x values
if (centeringEnabled) {
positions[i.toInt()] = mXAxis.mCenteredEntries[(i / 2).toInt()]
} else {
positions[i.toInt()] = xArray[(i / 2).toInt()]
}
}
mTrans.pointValuesToPixel(positions)
for(i in positions.indices step 2){
var x = positions[i]
if (mViewPortHandler.isInBoundsX(x)) {
val label = xValueFormatter.getAxisLabel(xArray[i / 2], mXAxis)
if (mXAxis.isAvoidFirstLastClippingEnabled) {
// avoid clipping of the last
if (i / 2 == xArray.size - 1 && xArray.size > 1) {
val width = Utils.calcTextWidth(mAxisLabelPaint, label).toFloat()
if (width > mViewPortHandler.offsetRight() * 2 && x + width > mViewPortHandler.chartWidth)
x -= width / 2
// avoid clipping of the first
} else if (i == 0) {
val width = Utils.calcTextWidth(mAxisLabelPaint, label).toFloat()
x += width / 2
}
}
drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees)
}
}
}
}
2.)Then you can use it like the below:
//prepare an Array of the x points to render independent of the x points in the Data Entry List
//below values must be between AxisMinimum and AxisMaximum
val renderXArray = arrayOf<Float>(0f, 150f, 250f, 350f, 400f, 433f, 505f)
//and set a custom XAxisRenderer
chart.setXAxisRenderer(CustomXAxisRenderer(chart.getViewPortHandler(), chart.getXAxis(), chart.getTransformer(YAxis.AxisDependency.LEFT), renderXArray, object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return value.toInt().toString()
}
}))
In case you need to return a specific Label-String based on the value you can use the ValueFormatter like the below:
chart.setXAxisRenderer(CustomXAxisRenderer(chart.getViewPortHandler(), chart.getXAxis(), chart.getTransformer(YAxis.AxisDependency.LEFT), renderXArray, object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return when (value) {
0f -> "A"
150f -> "B"
250f -> "C"
350f -> "D"
400f -> "E"
433f -> "F"
505f -> "G"
else -> ""
}
}
}))
Full Example:
chart = findViewById<LineChart>(R.id.chart)
//set xAxis properties
val xAxis = chart.xAxis
xAxis.setDrawLabels(true)
xAxis.setDrawAxisLine(true)
xAxis.setDrawGridLines(true)
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.axisLineWidth = 1.5f
//set xAxis Min/Max and Granularity
xAxis.setAxisMinimum(0f)
xAxis.setAxisMaximum(505f)
xAxis.setGranularity(1f)
xAxis.setGranularityEnabled(true)
//set axisLeft properties
val axisLeft = chart.axisLeft
axisLeft.setDrawLabels(true)
axisLeft.setDrawGridLines(true)
axisLeft.setDrawZeroLine(true)
axisLeft.axisLineWidth = 1.5f
axisLeft.setDrawTopYLabelEntry(true)
//set axisRight properties
val axisRight = chart.axisRight
axisRight.setDrawLabels(false)
axisRight.setDrawGridLines(false)
axisRight.setDrawZeroLine(false)
axisRight.setDrawAxisLine(false)
//prepare the Entry points
val values: ArrayList<Entry> = ArrayList()
values.add(Entry(101f, 50f))
values.add(Entry(205f, 100f))
values.add(Entry(210f, 150f))
values.add(Entry(445f, 200f))
values.add(Entry(505f, 250f))
//set the LineDataSet
val set1 = LineDataSet(values, "")
set1.setDrawCircles(true);
set1.setDrawValues(true);
//prepare LineData and set them to LineChart
val dataSets: ArrayList<ILineDataSet> = ArrayList()
dataSets.add(set1)
val data = LineData(dataSets)
chart.setData(data)
chart.getDescription().setEnabled(false);
chart.legend.isEnabled = false
//prepare an Array of the x points to render independent of the x points in the Data Entry List
//below values must be between AxisMinimum and AxisMaximum
val renderXArray = arrayOf<Float>(0f, 150f, 250f, 350f, 400f, 433f, 505f)
//and set a custom XAxisRenderer
chart.setXAxisRenderer(CustomXAxisRenderer(chart.getViewPortHandler(), chart.getXAxis(), chart.getTransformer(YAxis.AxisDependency.LEFT), renderXArray, object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return value.toInt().toString()
}
}))
Value Result:
Label Result:
Note: This was tested with version 'com.github.PhilJay:MPAndroidChart:v3.1.0'

How to set mpandoroidchart's color in kotlin?

I want to set my chart's data color
So, I found that MPAndroidchart, but example was made Java, not Kotlin.
This is my Kotlin source. How can I change colors?
In Addition, I want to make Label in Bar chart below the bar (Like first picture), And Piechart
Please help me.. T_T
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_result)
val tabHost = findViewById<TabHost>(R.id.TabHost)
tabHost.setup()
//종합 탭 소스
val tabSpecTotal = tabHost.newTabSpec("Total").setIndicator("종합")
tabSpecTotal.setContent(R.id.total)
//남녀 성비 그래프
sexRateChart.setUsePercentValues(true)
sexRateChart.description.setEnabled(false)
sexRateChart.setExtraOffsets(5f, 10f, 5f, 5f)
sexRateChart.dragDecelerationFrictionCoef = 0.95f
sexRateChart.isDrawHoleEnabled = false
sexRateChart.setHoleColor(Color.BLACK)
sexRateChart.transparentCircleRadius = 61f
val sexValues = ArrayList<PieEntry>() // 데이터 삽입
sexValues.add(PieEntry(63f,"남성"))
sexValues.add(PieEntry(37f,"여성"))
sexRateChart.animateY(2000, Easing.EaseInOutCubic) //애니메이션 효과 설정
val sexDataSet = PieDataSet(sexValues, "성별")
sexDataSet.sliceSpace = 3f
sexDataSet.selectionShift = 2f
val sexData = PieData((sexDataSet))
sexData.setValueTextSize(10f)
sexData.setValueTextColor(Color.BLACK)
sexRateChart.setData(sexData)
sexRateChart.invalidate()
//남녀 성비 끝
//연령대 막대그래프
ageRateChart.setExtraOffsets(5f, 10f, 5f, 5f)
val ageValues = ArrayList<BarEntry>()
ageValues.add(BarEntry(0f, 10f, "10대"))
ageValues.add(BarEntry(1f, 30f, "20대"))
ageValues.add(BarEntry(2f, 50f, "30대"))
ageValues.add(BarEntry(3f, 30f, "40대"))
ageValues.add(BarEntry(4f, 40f, "50대"))
ageValues.add(BarEntry(5f, 5f, "60대 이상"))
ageRateChart.animateY(4000, Easing.EaseInOutCubic)
val ageDataSet = BarDataSet(ageValues, "연령대")
ageDataSet.setColors(intArrayOf(R.color.red1, R.color.red2, R.color.red3, R.color.red4), Context)
val ageData = BarData(ageDataSet)
ageData.barWidth = 1f
ageRateChart.data = ageData
ageRateChart.setFitBars(true)
ageRateChart.invalidate()
//연령대 막대그래프 끝
Result of code :
You can set colors with fills
val startColor1 = ContextCompat.getColor(this, android.R.color.holo_orange_light)
val startColor2 = ContextCompat.getColor(this, android.R.color.holo_blue_light)
val startColor3 = ContextCompat.getColor(this, android.R.color.holo_orange_light)
val startColor4 = ContextCompat.getColor(this, android.R.color.holo_green_light)
val startColor5 = ContextCompat.getColor(this, android.R.color.holo_red_light)
val gradientFills: MutableList<Fill> = ArrayList()
with(gradientFills) {
add(Fill(startColor1))
add(Fill(startColor2))
add(Fill(startColor3))
add(Fill(startColor4))
add(Fill(startColor5))
}
val ageDataSet = BarDataSet(ageValues, "연령대")
ageDataSet.fills = gradientFills
And Label with ValueFormatter
ageRateChart.xAxis.apply {
position = XAxisPosition.BOTTOM
setDrawGridLines(false)
granularity = 1f
valueFormatter = object : ValueFormatter() {
override fun getAxisLabel(value: Float, axis: AxisBase?): String {
return (value * 10 + 10).toString() + "대"
}
}
}

Custom label indicators on xAxis in MpAndroidChart

I am using a chart library called MPAndroidChart and it responds to most of my needs. However, I need to customize some parts.
I want to draw some indicator lines for labels on xAxis like this :
As I dug in, I could write a CustomAxisRenderer but it seems I need to copy most of the super class codes.
I want the min value to be drawn exactly on xAxis. This min value could be 0 or any other number as well.
How can this be done? Is it even possible to do it?
Any help or hint would be appreciated.
I solved the first issue:
internal class IndicatorAxisRenderer(
viewPortHandler: ViewPortHandler,
xAxis: XAxis,
trans: Transformer
) : XAxisRenderer(viewPortHandler, xAxis, trans) {
private var indicatorWidth = 1f
private var indicatorHeight = 1f
private fun getXLabelPositions(): FloatArray {
var i = 0
val positions = FloatArray(mXAxis.mEntryCount * 2)
val centeringEnabled = mXAxis.isCenterAxisLabelsEnabled
while (i < positions.size) {
if (centeringEnabled) {
positions[i] = mXAxis.mCenteredEntries[i / 2]
} else {
positions[i] = mXAxis.mEntries[i / 2]
}
positions[i + 1] = 0f
i += 2
}
mTrans.pointValuesToPixel(positions)
return positions
}
override fun renderAxisLine(c: Canvas?) {
super.renderAxisLine(c)
val positions = getXLabelPositions()
var i = 0
while (i < positions.size) {
val x = positions[i]
if (mViewPortHandler.isInBoundsX(x)) {
val y = mViewPortHandler.contentBottom()
c?.drawLine(
x, y,
x, y + indicatorHeight,
mAxisLinePaint
)
}
i += 2
}
}
fun setIndicatorSize(width: Float, height: Float) {
this.indicatorWidth = width
this.indicatorHeight = height
}
}
This code renders indicator lines on top of the xAxis.

Categories

Resources