1
0
mirror of https://github.com/dzeiocom/OpenHealth.git synced 2025-07-09 21:09:18 +00:00

feat: Add Custom Chart Library

This commit is contained in:
2022-07-20 00:21:02 +02:00
parent 902c24bf89
commit 1f72b68a13
10 changed files with 312 additions and 0 deletions

View File

@ -0,0 +1,24 @@
package com.dzeio.charts
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.dzeio.charts.test", appContext.packageName)
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dzeio.charts">
</manifest>

View File

@ -0,0 +1,199 @@
package com.dzeio.charts
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
import android.view.View
import kotlin.math.abs
class BarView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
View(context, attrs) {
companion object {
const val TAG = "DzeioCharts/BarView"
}
/**
* Nunber of entries displayed at the same time
*/
val numberOfEntries = 5
/**
* Number of labels displayed at the same time
*/
val numberOfLabels = 3
/**
* Spacing between entries
*/
val spacing = 22
/**
* top margin from the canvas
*/
@Deprecated("Not needed anymore, Use the parent Padding/Margin")
private val topMargin: Int = 5
private val textTopMargin = 5
private var barWidth: Int = 0
private val textColor = Color.parseColor("#9B9A9B")
private val foregroundColor = Color.parseColor("#FC496D")
private val percentList: ArrayList<Float> = ArrayList()
private var targetPercentList: ArrayList<Float> = ArrayList()
private val textPaint: Paint = Paint().also {
it.isAntiAlias = true
it.color = textColor
it.textSize = 25f
it.textAlign = Paint.Align.CENTER
}
private val fgPaint: Paint = Paint().also {
it.isAntiAlias = true
it.color = foregroundColor
}
private val rect: Rect = Rect()
private var bottomTextDescent = 0
private var bottomTextHeight = 0
private var bottomTextList: ArrayList<String>? = ArrayList()
private val animator: Runnable = object : Runnable {
override fun run() {
var needNewFrame = false
for (i in targetPercentList.indices) {
if (percentList[i] < targetPercentList[i]) {
percentList[i] = percentList[i] + 0.02f
needNewFrame = true
} else if (percentList[i] > targetPercentList[i]) {
percentList[i] = percentList[i] - 0.02f
needNewFrame = true
}
if (abs(targetPercentList[i] - percentList[i]) < 0.02f) {
percentList[i] = targetPercentList[i]
}
}
if (needNewFrame) {
postDelayed(this, 20)
}
invalidate()
}
}
/**
* dataList will be reset when called is method.
*
* @param bottomStringList The String ArrayList in the bottom.
*/
fun setBottomTextList(bottomStringList: ArrayList<String>?) {
barWidth = measuredWidth / numberOfEntries - spacing
bottomTextList = bottomStringList
val r = Rect()
bottomTextDescent = 0
for (s in bottomTextList!!) {
textPaint.getTextBounds(s, 0, s.length, r)
if (bottomTextHeight < r.height()) {
bottomTextHeight = r.height()
}
Log.d(TAG, measuredWidth.toString())
// if (autoSetWidth && barWidth < r.width()) {
// barWidth = r.width()
// }
if (bottomTextDescent < abs(r.bottom)) {
bottomTextDescent = abs(r.bottom)
}
}
postInvalidate()
}
/**
* @param list The ArrayList of Integer with the range of [0-max].
*/
fun setDataList(list: ArrayList<Int>) {
barWidth = measuredWidth / numberOfEntries - spacing
// Calculate max
val max = list.reduce { acc, i -> if (acc > i) return@reduce acc else return@reduce i }
targetPercentList = ArrayList()
for (integer in list) {
targetPercentList.add(1 - integer.toFloat() / max.toFloat())
}
// Make sure percentList.size() == targetPercentList.size()
if (percentList.isEmpty() || percentList.size < targetPercentList.size) {
val temp = targetPercentList.size - percentList.size
for (i in 0 until temp) {
percentList.add(1f)
}
} else if (percentList.size > targetPercentList.size) {
val temp = percentList.size - targetPercentList.size
for (i in 0 until temp) {
percentList.removeAt(percentList.size - 1)
}
}
removeCallbacks(animator)
post(animator)
}
override fun onDraw(canvas: Canvas) {
if (percentList.isNotEmpty()) {
for (i in 1 until percentList.size) {
val left = spacing * i + barWidth * (i - 1)
Log.d(TAG, "$spacing, $i, $barWidth = $left")
val right = (spacing + barWidth) * i
val bottom = height - bottomTextHeight - textTopMargin
val top = topMargin + ((bottom - topMargin) * percentList[i - 1])
rect.set(left, top.toInt(), right, bottom)
canvas.drawRect(rect, fgPaint)
}
}
if (bottomTextList != null && !bottomTextList!!.isEmpty()) {
var i = 1
for (s in bottomTextList!!) {
canvas.drawText(
s,
(spacing * i + barWidth * (i - 1) + barWidth / 2).toFloat(),
(height - bottomTextDescent).toFloat(),
textPaint
)
i++
}
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val mViewWidth = measureWidth(widthMeasureSpec)
val mViewHeight = measureHeight(heightMeasureSpec)
setMeasuredDimension(mViewWidth, mViewHeight)
}
private fun measureWidth(measureSpec: Int): Int {
var preferred = 0
if (bottomTextList != null) {
preferred = bottomTextList!!.size * (barWidth + spacing)
}
return getMeasurement(measureSpec, preferred)
}
private fun measureHeight(measureSpec: Int): Int {
val preferred = 222
return getMeasurement(measureSpec, preferred)
}
private fun getMeasurement(measureSpec: Int, preferred: Int): Int {
val specSize = MeasureSpec.getSize(measureSpec)
val measurement = when (MeasureSpec.getMode(measureSpec)) {
MeasureSpec.EXACTLY -> specSize
MeasureSpec.AT_MOST -> Math.min(preferred, specSize)
else -> preferred
}
return measurement
}
}

View File

@ -0,0 +1,17 @@
package com.dzeio.charts
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}