mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-04-23 03:12:14 +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"
|
||||
|
||||
include ':app'
|
||||
include ':charts'
|
||||
|
Loading…
x
Reference in New Issue
Block a user