1
0
mirror of https://github.com/dzeiocom/OpenHealth.git synced 2025-04-23 11:22:10 +00:00

feat: Add Custom Chart Library

This commit is contained in:
Florian Bouillon 2022-07-20 00:21:02 +02:00
parent 902c24bf89
commit 1f72b68a13
Signed by: Florian Bouillon
GPG Key ID: 0A288052C94BD2C8
10 changed files with 312 additions and 0 deletions

1
charts/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

3
charts/README.md Normal file
View File

@ -0,0 +1,3 @@
# Dzeio Charts
Originally from https://github.com/HackPlan/AndroidCharts but We wanted more options :D

40
charts/build.gradle Normal file
View File

@ -0,0 +1,40 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 32
defaultConfig {
minSdk 21
targetSdk 32
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
kotlinOptions {
jvmTarget = '1.6'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

View File

21
charts/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

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)
}
}

View File

@ -15,4 +15,6 @@ dependencyResolutionManagement {
} }
} }
rootProject.name = "OpenHealth" rootProject.name = "OpenHealth"
include ':app' include ':app'
include ':charts'