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:
parent
902c24bf89
commit
1f72b68a13
1
charts/.gitignore
vendored
Normal file
1
charts/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
3
charts/README.md
Normal file
3
charts/README.md
Normal 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
40
charts/build.gradle
Normal 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'
|
||||||
|
}
|
0
charts/consumer-rules.pro
Normal file
0
charts/consumer-rules.pro
Normal file
21
charts/proguard-rules.pro
vendored
Normal file
21
charts/proguard-rules.pro
vendored
Normal 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
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
5
charts/src/main/AndroidManifest.xml
Normal file
5
charts/src/main/AndroidManifest.xml
Normal 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>
|
199
charts/src/main/java/com/dzeio/charts/BarView.kt
Normal file
199
charts/src/main/java/com/dzeio/charts/BarView.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
17
charts/src/test/java/com/dzeio/charts/ExampleUnitTest.kt
Normal file
17
charts/src/test/java/com/dzeio/charts/ExampleUnitTest.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -15,4 +15,6 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "OpenHealth"
|
rootProject.name = "OpenHealth"
|
||||||
|
|
||||||
include ':app'
|
include ':app'
|
||||||
|
include ':charts'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user