mirror of
https://github.com/dzeiocom/charts.git
synced 2025-04-22 10:42:09 +00:00
feat: Make sample more customizable (#43)
This commit is contained in:
parent
1a812acb9e
commit
3df7541505
@ -58,7 +58,7 @@ chart.refresh()
|
|||||||
<img width="40%" src=".github/usage-example.jpg" />
|
<img width="40%" src=".github/usage-example.jpg" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
_note: Every charts used above used a helper function to have Material 3 colors [See MainFragment.kt the materialTheme function](./sample/src/main/java/com/dzeio/chartstest/ui/MainFragment.kt)_
|
_note: Every charts used above used a helper function to have Material 3 colors [See the MaterialUtils.kt class](sample/src/main/java/com/dzeio/chartsapp/utils/MaterialUtils.kt)_
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ buildscript {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:7.4.1")
|
classpath("com.android.tools.build:gradle:7.4.1")
|
||||||
|
|
||||||
|
// Safe Navigation
|
||||||
|
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
|||||||
View(context, attrs), ChartViewInterface {
|
View(context, attrs), ChartViewInterface {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val TAG = "Charts/ChartView"
|
const val TAG = "ChartView"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val animator: Animation = Animation()
|
override val animator: Animation = Animation()
|
||||||
@ -97,6 +97,10 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
|||||||
|
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
setOnToggleScroll {
|
||||||
|
// note: true == no scroll
|
||||||
|
parent?.requestDisallowInterceptTouchEvent(!it && (yAxis.scrollEnabled || xAxis.scrollEnabled))
|
||||||
|
}
|
||||||
setOnChartClick { x, y ->
|
setOnChartClick { x, y ->
|
||||||
if (getDataset().isEmpty()) {
|
if (getDataset().isEmpty()) {
|
||||||
return@setOnChartClick
|
return@setOnChartClick
|
||||||
@ -161,15 +165,11 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
|||||||
|
|
||||||
// invalidate the view
|
// invalidate the view
|
||||||
invalidate()
|
invalidate()
|
||||||
// removeCallbacks(animator)
|
|
||||||
// post(animator)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lastRun = runUpdates
|
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
// don't draw anything if everything is empty
|
// don't draw anything if everything is empty
|
||||||
if (!runUpdates && lastRun == runUpdates && series.isEmpty() || series.maxOf { it.entries.size } == 0) {
|
if (!runUpdates || series.isEmpty() || series.maxOf { it.entries.size } == 0) {
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -92,18 +92,14 @@ class XAxis(
|
|||||||
|
|
||||||
var maxHeight = 0f
|
var maxHeight = 0f
|
||||||
|
|
||||||
val graphIncrement = space.width() / (labelCount - 1)
|
val valueIncrement = getDataWidth() / (labelCount - 1).coerceAtLeast(1)
|
||||||
val valueIncrement = getDataWidth() / (labelCount - 1)
|
|
||||||
for (index in 0 until labelCount) {
|
for (index in 0 until labelCount) {
|
||||||
val text = onValueFormat(x + valueIncrement * index)
|
val text = onValueFormat(x + valueIncrement * index)
|
||||||
textPaint.getTextBounds(text, 0, text.length, rect)
|
textPaint.getTextBounds(text, 0, text.length, rect)
|
||||||
|
getPositionOnRect(valueIncrement, space)
|
||||||
maxHeight = maxHeight.coerceAtLeast(rect.height().toFloat() + 1)
|
maxHeight = maxHeight.coerceAtLeast(rect.height().toFloat() + 1)
|
||||||
|
|
||||||
var xPos = space.left + graphIncrement * index
|
val xPos = getPositionOnRect(x + valueIncrement * index, space).toFloat()
|
||||||
|
|
||||||
if (xPos + rect.width() > space.right) {
|
|
||||||
xPos = space.right - rect.width()
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.drawText(
|
canvas.drawText(
|
||||||
text,
|
text,
|
||||||
@ -134,8 +130,8 @@ class XAxis(
|
|||||||
.coerceIn(1.0, drawableSpace.width().toDouble())
|
.coerceIn(1.0, drawableSpace.width().toDouble())
|
||||||
|
|
||||||
// handle grouped series
|
// handle grouped series
|
||||||
if (view.type == ChartType.GROUPED) {
|
if (view.type == ChartType.GROUPED && view.series.size > 1) {
|
||||||
return result / view.series.size - spacing / 2 * (view.series.size - 1)
|
return ((result - (spacing / 2 * view.series.size)) / view.series.size).coerceAtLeast(1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -145,5 +141,4 @@ class XAxis(
|
|||||||
// TODO: handle the auto dataWidth better (still not sure it is good enough)
|
// TODO: handle the auto dataWidth better (still not sure it is good enough)
|
||||||
return dataWidth ?: (getXMax() - getXMin() + 1)
|
return dataWidth ?: (getXMax() - getXMin() + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ class YAxis(
|
|||||||
val max = getYMax() - min
|
val max = getYMax() - min
|
||||||
var maxWidth = 0f
|
var maxWidth = 0f
|
||||||
|
|
||||||
val valueIncrement = max / (labelCount - 1)
|
val valueIncrement = max / (labelCount - 1).coerceAtLeast(1)
|
||||||
for (index in 0 until labelCount) {
|
for (index in 0 until labelCount) {
|
||||||
val value = min + (valueIncrement * index)
|
val value = min + (valueIncrement * index)
|
||||||
|
|
||||||
|
@ -73,6 +73,11 @@ class Annotation(
|
|||||||
|
|
||||||
val xCenter = view.xAxis.getEntryWidth(space) / 2.0 + x
|
val xCenter = view.xAxis.getEntryWidth(space) / 2.0 + x
|
||||||
|
|
||||||
|
if (xCenter < space.left || xCenter > space.right) {
|
||||||
|
entry = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val xText = annotationSubTitleFormat.invoke(entry!!)
|
val xText = annotationSubTitleFormat.invoke(entry!!)
|
||||||
val yText = annotationTitleFormat.invoke(entry!!)
|
val yText = annotationTitleFormat.invoke(entry!!)
|
||||||
|
|
||||||
|
@ -1,160 +0,0 @@
|
|||||||
package com.dzeio.charts.series
|
|
||||||
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.Paint
|
|
||||||
import android.graphics.Rect
|
|
||||||
import android.graphics.RectF
|
|
||||||
import android.util.Log
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
class BarSerie : SerieAbstract() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "DzeioCharts/BarSerie"
|
|
||||||
}
|
|
||||||
|
|
||||||
var spacing: Float = 8f
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Values displayed on the grapd
|
|
||||||
*/
|
|
||||||
var displayedDatas = arrayListOf<Float>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Target values
|
|
||||||
*/
|
|
||||||
var targetDatas = arrayListOf<Float>()
|
|
||||||
|
|
||||||
var targetPercentList = arrayListOf<Float>()
|
|
||||||
var percentList = arrayListOf<Float>()
|
|
||||||
|
|
||||||
var previousRefresh = 0
|
|
||||||
|
|
||||||
private var fgPaint: Paint = Paint().apply {
|
|
||||||
isAntiAlias = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private val r = Rect()
|
|
||||||
|
|
||||||
override fun onUpdate(): Boolean {
|
|
||||||
var needNewFrame = false
|
|
||||||
for (i in targetPercentList.indices) {
|
|
||||||
val value = view.animation.updateValue(
|
|
||||||
1f,
|
|
||||||
targetPercentList[i],
|
|
||||||
percentList[i],
|
|
||||||
0f,
|
|
||||||
0.00f
|
|
||||||
)
|
|
||||||
|
|
||||||
if (value != percentList[i]) {
|
|
||||||
needNewFrame = true
|
|
||||||
percentList[i] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return needNewFrame
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun prepareData() {
|
|
||||||
val max: Float = if (view.yAxis.max != null) view.yAxis.max!! else {
|
|
||||||
getYMax(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
targetPercentList = arrayListOf()
|
|
||||||
|
|
||||||
// Log.d(TAG, "offset: ${view.getXOffset()}, displayed: ${view.getDisplayedEntries()}")
|
|
||||||
for (item in getDisplayedEntries()) {
|
|
||||||
// // // Process bottom texts
|
|
||||||
// val text = view.xAxis.onValueFormat(item.x)
|
|
||||||
// bottomTexts.add(text)
|
|
||||||
//
|
|
||||||
// // get Text boundaries
|
|
||||||
// view.xAxis.labels.build().getTextBounds(text, 0, text.length, r)
|
|
||||||
//
|
|
||||||
// // get height of text
|
|
||||||
// if (bottomTextHeight < r.height()) {
|
|
||||||
// bottomTextHeight = r.height()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // get text descent
|
|
||||||
// val descent = abs(r.bottom)
|
|
||||||
// if (bottomTextDescent < descent) {
|
|
||||||
// bottomTextDescent = descent
|
|
||||||
// }
|
|
||||||
|
|
||||||
// process values
|
|
||||||
// Log.d(TAG, item.y.toString())
|
|
||||||
|
|
||||||
// add to animations the values
|
|
||||||
targetPercentList.add(1 - item.y / max)
|
|
||||||
}
|
|
||||||
|
|
||||||
// post list
|
|
||||||
val offset = view.getXOffset()
|
|
||||||
val movement = offset - previousRefresh
|
|
||||||
Log.d(TAG, "$offset - $previousRefresh = $movement")
|
|
||||||
if (movement != 0) {
|
|
||||||
previousRefresh = offset
|
|
||||||
}
|
|
||||||
// if (movement != 0) {
|
|
||||||
// Log.d(TAG, movement.toString())
|
|
||||||
// }
|
|
||||||
if (movement >= 1) {
|
|
||||||
percentList = percentList.subList(1, percentList.size).toCollection(ArrayList())
|
|
||||||
percentList.add(1f)
|
|
||||||
} else if (movement <= -1) {
|
|
||||||
percentList = percentList.subList(0, percentList.size - 1).toCollection(ArrayList())
|
|
||||||
percentList.add(0, 1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fgPaint.color = view.yAxis.color
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun displayData(canvas: Canvas, rect: RectF) {
|
|
||||||
val barWidth = (rect.width() - view.padding * 2) / view.getDisplayedEntries() - spacing
|
|
||||||
|
|
||||||
if (percentList.isNotEmpty()) {
|
|
||||||
// draw each rectangles
|
|
||||||
for (i in 1..percentList.size) {
|
|
||||||
// Log.d(TAG, percentList[i - 1].toString())
|
|
||||||
val left = rect.left + spacing * i + barWidth * (i - 1).toFloat() + view.padding
|
|
||||||
// Log.d(TAG, "$spacing, $i, $barWidth = $left")
|
|
||||||
val right = rect.left + (spacing + barWidth) * i.toFloat()
|
|
||||||
val bottom = rect.top + rect.height() - view.padding
|
|
||||||
val top = (bottom - rect.top) * percentList[i - 1] + view.padding
|
|
||||||
|
|
||||||
// create rounded rect
|
|
||||||
canvas.drawRoundRect(left, top, right, bottom, 8f, 8f, fgPaint)
|
|
||||||
// remove the bottom corners DUH
|
|
||||||
canvas.drawRect(left, max(top, bottom - 8f), right, bottom, fgPaint)
|
|
||||||
val targetTop = (bottom - rect.top) * targetPercentList[i - 1]
|
|
||||||
|
|
||||||
val text = view.yAxis.onValueFormat(getYMax(true) - getYMax(true) * targetPercentList[i - 1], true)
|
|
||||||
view.xAxis.labels.build().getTextBounds(text, 0, text.length, r)
|
|
||||||
val doDisplayIn =
|
|
||||||
r.width() + 10f < barWidth && bottom - targetTop > r.height() + 40f
|
|
||||||
if (view.debug || !doDisplayIn || (doDisplayIn && bottom - top > r.height() + 40f)) {
|
|
||||||
val y = if (doDisplayIn) top + r.height() + 20f else top - r.height()
|
|
||||||
canvas.drawText(
|
|
||||||
text,
|
|
||||||
left + (right - left) / 2,
|
|
||||||
y,
|
|
||||||
view.xAxis.labels.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,23 @@
|
|||||||
plugins {
|
plugins {
|
||||||
|
// Android Application?
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
|
|
||||||
|
// Support for kotlin in Android
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
|
|
||||||
|
// Safe Navigation
|
||||||
|
id("androidx.navigation.safeargs")
|
||||||
|
|
||||||
|
// keep at bottom
|
||||||
|
kotlin("kapt")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.dzeio.chartstest"
|
namespace = "com.dzeio.chartsapp"
|
||||||
compileSdk = 33
|
compileSdk = 33
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.dzeio.chartstest"
|
applicationId = "com.dzeio.chartsapp"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 33
|
targetSdk = 33
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
@ -34,6 +43,7 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
|
dataBinding = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,8 +52,9 @@ dependencies {
|
|||||||
implementation(project(":library"))
|
implementation(project(":library"))
|
||||||
|
|
||||||
// Material Design
|
// Material Design
|
||||||
implementation("com.google.android.material:material:1.7.0")
|
implementation("com.google.android.material:material:1.8.0")
|
||||||
|
|
||||||
// Navigation because I don't want to maintain basic transactions and shit
|
// Navigation because I don't want to maintain basic transactions and shit
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
|
||||||
|
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="Dzeio Charts"
|
||||||
android:theme="@style/Theme.Charts">
|
android:theme="@style/Theme.Charts">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
|
178
sample/src/main/java/com/dzeio/chartsapp/ui/ChartFragment.kt
Normal file
178
sample/src/main/java/com/dzeio/chartsapp/ui/ChartFragment.kt
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
package com.dzeio.chartsapp.ui
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import com.dzeio.charts.ChartType
|
||||||
|
import com.dzeio.charts.ChartViewInterface
|
||||||
|
import com.dzeio.charts.Entry
|
||||||
|
import com.dzeio.charts.series.BarSerie
|
||||||
|
import com.dzeio.charts.series.LineSerie
|
||||||
|
import com.dzeio.charts.series.SerieInterface
|
||||||
|
import com.dzeio.chartsapp.databinding.FragmentChartBinding
|
||||||
|
import com.dzeio.chartsapp.utils.MaterialUtils
|
||||||
|
import com.dzeio.chartsapp.utils.Utils
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class ChartFragment : Fragment() {
|
||||||
|
private var _binding: FragmentChartBinding? = null
|
||||||
|
|
||||||
|
private val args: ChartFragmentArgs by navArgs()
|
||||||
|
|
||||||
|
private var numberOfValues = 5
|
||||||
|
|
||||||
|
private lateinit var chart: ChartViewInterface
|
||||||
|
private val binding: FragmentChartBinding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = FragmentChartBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
chart = binding.chart
|
||||||
|
addSerie()
|
||||||
|
|
||||||
|
MaterialUtils.materielTheme(chart, requireView())
|
||||||
|
|
||||||
|
chart.refresh()
|
||||||
|
|
||||||
|
binding.addValue.setOnClickListener {
|
||||||
|
chart.series.forEach {
|
||||||
|
it.entries.add(
|
||||||
|
Entry(
|
||||||
|
it.entries.size.toDouble(),
|
||||||
|
Random.nextInt(0, 100).toFloat()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
numberOfValues++
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.removeValue.setOnClickListener {
|
||||||
|
chart.series.forEach {
|
||||||
|
it.entries.removeLastOrNull()
|
||||||
|
}
|
||||||
|
numberOfValues--
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.addSerie.setOnClickListener {
|
||||||
|
addSerie()
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.removeSerie.setOnClickListener {
|
||||||
|
chart.series.removeLastOrNull()
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.switchSubtype.setOnClickListener {
|
||||||
|
when (chart.type) {
|
||||||
|
ChartType.BASIC -> {
|
||||||
|
chart.type = ChartType.GROUPED
|
||||||
|
binding.switchSubtype.setText("Grouped Chart")
|
||||||
|
}
|
||||||
|
ChartType.GROUPED -> {
|
||||||
|
chart.type = ChartType.STACKED
|
||||||
|
binding.switchSubtype.setText("Stacked Chart")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
chart.type = ChartType.BASIC
|
||||||
|
binding.switchSubtype.setText("Basic Chart")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.switchAnimations.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
chart.animator.enabled = isChecked
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.switchXAxis.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
chart.xAxis.enabled = isChecked
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.switchYAxis.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
chart.yAxis.enabled = isChecked
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.sliderXAxis.addOnChangeListener { _, value, _ ->
|
||||||
|
chart.xAxis.labelCount = value.roundToInt()
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.sliderYAxis.addOnChangeListener { _, value, _ ->
|
||||||
|
chart.yAxis.labelCount = value.roundToInt()
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.switchXAxisScrollable.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
chart.xAxis.scrollEnabled = isChecked
|
||||||
|
if (isChecked) {
|
||||||
|
chart.xAxis.dataWidth = binding.sliderXAxisScroll.value.toDouble()
|
||||||
|
binding.sliderXAxisScroll.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
chart.xAxis.dataWidth = null
|
||||||
|
chart.xAxis.x = 0.0
|
||||||
|
binding.sliderXAxisScroll.visibility = View.GONE
|
||||||
|
}
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.sliderXAxisScroll.visibility = View.GONE
|
||||||
|
binding.sliderXAxisScroll.addOnChangeListener { _, value, _ ->
|
||||||
|
if (chart.xAxis.dataWidth != null) {
|
||||||
|
chart.xAxis.dataWidth = value.toDouble()
|
||||||
|
chart.refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var lastGenerated = 0
|
||||||
|
private fun addSerie(): SerieInterface {
|
||||||
|
val toGet = if (args.chartType == null) {
|
||||||
|
if (lastGenerated == 0) {
|
||||||
|
"barchart"
|
||||||
|
} else {
|
||||||
|
"linechart"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args.chartType
|
||||||
|
}
|
||||||
|
if (lastGenerated++ >= 1) {
|
||||||
|
lastGenerated = 0
|
||||||
|
}
|
||||||
|
val serie = if (toGet === "barchart") {
|
||||||
|
BarSerie(chart).apply {
|
||||||
|
barPaint.color = randomColor()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LineSerie(chart).apply {
|
||||||
|
linePaint.color = randomColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serie.entries = Utils.generateRandomDataset(numberOfValues)
|
||||||
|
return serie
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun randomColor(): Int {
|
||||||
|
return Color.argb(
|
||||||
|
255,
|
||||||
|
Random.nextInt(),
|
||||||
|
Random.nextInt(),
|
||||||
|
Random.nextInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
97
sample/src/main/java/com/dzeio/chartsapp/ui/MainActivity.kt
Normal file
97
sample/src/main/java/com/dzeio/chartsapp/ui/MainActivity.kt
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package com.dzeio.chartsapp.ui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
|
import androidx.navigation.ui.navigateUp
|
||||||
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
|
import com.dzeio.chartsapp.R
|
||||||
|
import com.dzeio.chartsapp.databinding.ActivityMainBinding
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
|
||||||
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
|
|
||||||
|
private lateinit var navController: NavController
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
|
// Comportement chelou API 28-
|
||||||
|
// Comportement normal 31+
|
||||||
|
|
||||||
|
// do not do the cool status/navigation bars for API 29 & 30
|
||||||
|
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.R && Build.VERSION.SDK_INT != Build.VERSION_CODES.Q) {
|
||||||
|
// allow to put the content behind the status bar & Navigation bar (one of them at least lul)
|
||||||
|
// ALSO: make the status/navigation bars semi-transparent
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
|
|
||||||
|
// Make the color of the navigation bar semi-transparent
|
||||||
|
// window.navigationBarColor = Color.TRANSPARENT
|
||||||
|
// Make the color of the status bar transparent
|
||||||
|
// window.statusBarColor = Color.TRANSPARENT
|
||||||
|
// Apply the previous changes
|
||||||
|
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
|
|
||||||
|
// Update toolbar height with the statusbar size included
|
||||||
|
// ALSO: make both the status/navigation bars transparent (WHYYYYYYY)
|
||||||
|
val toolbarHeight = binding.toolbar.layoutParams.height
|
||||||
|
window.decorView.setOnApplyWindowInsetsListener { _, insets ->
|
||||||
|
val statusBarSize = insets.systemWindowInsetTop
|
||||||
|
// Add padding to the toolbar (YaY I know how something works)
|
||||||
|
binding.toolbar.updatePadding(top = statusBarSize)
|
||||||
|
binding.toolbar.layoutParams.height = toolbarHeight + statusBarSize
|
||||||
|
return@setOnApplyWindowInsetsListener insets
|
||||||
|
}
|
||||||
|
|
||||||
|
// normally makes sure icons are at the correct color but idk if it works
|
||||||
|
when (this.resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
|
||||||
|
Configuration.UI_MODE_NIGHT_YES -> {
|
||||||
|
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||||
|
// force to display the bars in light color
|
||||||
|
isAppearanceLightNavigationBars = true
|
||||||
|
isAppearanceLightStatusBars = false // WHY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_UNDEFINED -> {
|
||||||
|
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||||
|
// force to display the bars in dark color
|
||||||
|
isAppearanceLightNavigationBars = false
|
||||||
|
isAppearanceLightStatusBars = true // WHY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val navHostFragment =
|
||||||
|
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
||||||
|
|
||||||
|
navController = navHostFragment.navController
|
||||||
|
|
||||||
|
appBarConfiguration = AppBarConfiguration(
|
||||||
|
setOf(
|
||||||
|
R.id.main_fragment
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean =
|
||||||
|
navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
||||||
|
}
|
93
sample/src/main/java/com/dzeio/chartsapp/ui/MainFragment.kt
Normal file
93
sample/src/main/java/com/dzeio/chartsapp/ui/MainFragment.kt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package com.dzeio.chartsapp.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.dzeio.charts.series.BarSerie
|
||||||
|
import com.dzeio.charts.series.LineSerie
|
||||||
|
import com.dzeio.chartsapp.databinding.FragmentMainBinding
|
||||||
|
import com.dzeio.chartsapp.utils.MaterialUtils
|
||||||
|
import com.dzeio.chartsapp.utils.Utils
|
||||||
|
|
||||||
|
class MainFragment : Fragment() {
|
||||||
|
private var _binding: FragmentMainBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
private val months = arrayListOf(
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec"
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
_binding = FragmentMainBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.gotoBarchart.setOnClickListener {
|
||||||
|
findNavController().navigate(
|
||||||
|
MainFragmentDirections.actionMainFragmentToChartFragment("barchart")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.gotoLinechart.setOnClickListener {
|
||||||
|
findNavController().navigate(
|
||||||
|
MainFragmentDirections.actionMainFragmentToChartFragment("linechart")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.gotoBarLineChart.setOnClickListener {
|
||||||
|
findNavController().navigate(
|
||||||
|
MainFragmentDirections.actionMainFragmentToChartFragment(null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.barchart.apply {
|
||||||
|
BarSerie(this).apply {
|
||||||
|
entries = Utils.generateRandomDataset(5)
|
||||||
|
}
|
||||||
|
MaterialUtils.materielTheme(this, requireView())
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.linechart.apply {
|
||||||
|
LineSerie(this).apply {
|
||||||
|
entries = Utils.generateRandomDataset(5)
|
||||||
|
}
|
||||||
|
MaterialUtils.materielTheme(this, requireView())
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.bothchart.apply {
|
||||||
|
BarSerie(this).apply {
|
||||||
|
entries = Utils.generateRandomDataset(5)
|
||||||
|
}
|
||||||
|
LineSerie(this).apply {
|
||||||
|
entries = Utils.generateRandomDataset(5)
|
||||||
|
}
|
||||||
|
MaterialUtils.materielTheme(this, requireView())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.dzeio.chartsapp.utils
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import com.dzeio.charts.ChartViewInterface
|
||||||
|
import com.dzeio.charts.series.BarSerie
|
||||||
|
import com.dzeio.charts.series.LineSerie
|
||||||
|
import com.google.android.material.color.MaterialColors
|
||||||
|
|
||||||
|
object MaterialUtils {
|
||||||
|
fun materielTheme(chart: ChartViewInterface, view: View) {
|
||||||
|
chart.yAxis.apply {
|
||||||
|
textLabel.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer)
|
||||||
|
linePaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer)
|
||||||
|
goalLinePaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorError)
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.xAxis.textPaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer)
|
||||||
|
chart.annotator.apply {
|
||||||
|
backgroundPaint.color = MaterialColors.getColor(
|
||||||
|
view,
|
||||||
|
com.google.android.material.R.attr.colorBackgroundFloating
|
||||||
|
)
|
||||||
|
titlePaint.color = MaterialColors.getColor(
|
||||||
|
view,
|
||||||
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
|
)
|
||||||
|
subTitlePaint.color = MaterialColors.getColor(
|
||||||
|
view,
|
||||||
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (serie in chart.series) {
|
||||||
|
if (serie is BarSerie) {
|
||||||
|
serie.apply {
|
||||||
|
barPaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorPrimary)
|
||||||
|
textPaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimary)
|
||||||
|
textExternalPaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorPrimary)
|
||||||
|
}
|
||||||
|
} else if (serie is LineSerie) {
|
||||||
|
serie.apply {
|
||||||
|
linePaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorPrimary)
|
||||||
|
textPaint.color =
|
||||||
|
MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
sample/src/main/java/com/dzeio/chartsapp/utils/Utils.kt
Normal file
19
sample/src/main/java/com/dzeio/chartsapp/utils/Utils.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.dzeio.chartsapp.utils
|
||||||
|
|
||||||
|
import com.dzeio.charts.Entry
|
||||||
|
import kotlin.random.Random.Default.nextInt
|
||||||
|
|
||||||
|
object Utils {
|
||||||
|
fun generateRandomDataset(size: Int = 100, min: Int = 0, max: Int = 100, xStep: Int = 1): ArrayList<Entry> {
|
||||||
|
val dataset: ArrayList<Entry> = ArrayList()
|
||||||
|
for (i in 0 until size) {
|
||||||
|
dataset.add(
|
||||||
|
Entry(
|
||||||
|
(i * xStep).toDouble(),
|
||||||
|
nextInt(min, max).toFloat()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return dataset
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
package com.dzeio.chartstest.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.view.WindowCompat
|
|
||||||
import com.dzeio.chartstest.databinding.ActivityMainBinding
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,273 +0,0 @@
|
|||||||
package com.dzeio.chartstest.ui
|
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.Paint
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.dzeio.charts.ChartType
|
|
||||||
import com.dzeio.charts.ChartView
|
|
||||||
import com.dzeio.charts.Entry
|
|
||||||
import com.dzeio.charts.axis.Line
|
|
||||||
import com.dzeio.charts.series.BarSerie
|
|
||||||
import com.dzeio.charts.series.LineSerie
|
|
||||||
import com.dzeio.chartstest.databinding.FragmentMainBinding
|
|
||||||
import com.google.android.material.color.MaterialColors
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
class MainFragment : Fragment() {
|
|
||||||
private var _binding: FragmentMainBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
_binding = FragmentMainBinding.inflate(inflater, container, false)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
binding.chartGrouped.apply {
|
|
||||||
// setup the Serie
|
|
||||||
val serie1 = BarSerie(this)
|
|
||||||
val serie2 = BarSerie(this)
|
|
||||||
|
|
||||||
animator.duration = 750
|
|
||||||
|
|
||||||
// transform the chart into a grouped chart
|
|
||||||
type = ChartType.GROUPED
|
|
||||||
yAxis.setYMin(0f)
|
|
||||||
|
|
||||||
// utils function to use Material3 auto colors
|
|
||||||
materielTheme(this, requireView())
|
|
||||||
serie2.barPaint.color = Color.RED
|
|
||||||
|
|
||||||
// give the serie it's entries
|
|
||||||
serie1.entries = generateRandomDataset(5)
|
|
||||||
serie2.entries = generateRandomDataset(5)
|
|
||||||
|
|
||||||
// refresh the Chart
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.chartStacked.apply {
|
|
||||||
// setup the Serie
|
|
||||||
val serie1 = BarSerie(this)
|
|
||||||
val serie2 = BarSerie(this)
|
|
||||||
|
|
||||||
animator.duration = 750
|
|
||||||
|
|
||||||
// transform the chart into a grouped chart
|
|
||||||
type = ChartType.STACKED
|
|
||||||
yAxis.setYMin(0f)
|
|
||||||
|
|
||||||
// utils function to use Material3 auto colors
|
|
||||||
materielTheme(this, requireView())
|
|
||||||
serie2.barPaint.color = Color.RED
|
|
||||||
|
|
||||||
// give the serie it's entries
|
|
||||||
serie1.entries = generateRandomDataset(10)
|
|
||||||
serie2.entries = generateRandomDataset(10)
|
|
||||||
|
|
||||||
// refresh the Chart
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.chartLine.apply {
|
|
||||||
// setup the Serie
|
|
||||||
val serie = LineSerie(this)
|
|
||||||
|
|
||||||
// utils function to use Material3 auto colors
|
|
||||||
materielTheme(this, requireView())
|
|
||||||
|
|
||||||
// give the serie its entries
|
|
||||||
serie.entries = generateRandomDataset(10)
|
|
||||||
|
|
||||||
// refresh the Chart
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.chartBar.apply {
|
|
||||||
// setup the Serie
|
|
||||||
val serie = BarSerie(this)
|
|
||||||
yAxis.setYMin(0f)
|
|
||||||
|
|
||||||
// utils function to use Material3 auto colors
|
|
||||||
materielTheme(this, requireView())
|
|
||||||
|
|
||||||
// give the serie its entries
|
|
||||||
serie.entries = generateRandomDataset(10)
|
|
||||||
|
|
||||||
// refresh the Chart
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.chartCustomization.apply {
|
|
||||||
// setup the Series
|
|
||||||
val serie1 = BarSerie(this)
|
|
||||||
val serie2 = LineSerie(this)
|
|
||||||
|
|
||||||
// utils function to use Material3 auto colors
|
|
||||||
materielTheme(this, requireView())
|
|
||||||
|
|
||||||
// give the series their entries
|
|
||||||
serie2.entries = generateRandomDataset(20, -50, 50)
|
|
||||||
serie1.entries = generateRandomDataset(20, -50, 50).apply {
|
|
||||||
for (idx in 0 until size) {
|
|
||||||
val compared = serie2.entries[idx]
|
|
||||||
val toCompare = this[idx]
|
|
||||||
if (compared.y > toCompare.y) {
|
|
||||||
toCompare.color = Color.RED
|
|
||||||
} else {
|
|
||||||
toCompare.color = Color.GREEN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// serie1.textExternalPaint = Color.WHITE
|
|
||||||
|
|
||||||
// make the lineSerie red
|
|
||||||
serie2.linePaint.color = Color.WHITE
|
|
||||||
|
|
||||||
// strokeWidth also control the points width
|
|
||||||
serie2.linePaint.strokeWidth = 10f
|
|
||||||
|
|
||||||
yAxis.apply {
|
|
||||||
// Enable vertical scrolling
|
|
||||||
scrollEnabled = true
|
|
||||||
|
|
||||||
// change the number of labels
|
|
||||||
labelCount = 11
|
|
||||||
|
|
||||||
// change how labels are displayed
|
|
||||||
onValueFormat = { "${it.roundToInt()}g" }
|
|
||||||
|
|
||||||
// change labels colors
|
|
||||||
textLabel.color = Color.WHITE
|
|
||||||
|
|
||||||
// change line color
|
|
||||||
linePaint.color = Color.WHITE
|
|
||||||
|
|
||||||
// Add horizontal Lines
|
|
||||||
val paint: Paint = Paint(yAxis.linePaint).apply {
|
|
||||||
strokeWidth = 8f
|
|
||||||
}
|
|
||||||
addLine(10f, Line(true, paint))
|
|
||||||
addLine(-10f, Line(true, paint))
|
|
||||||
|
|
||||||
// change the min/max high
|
|
||||||
setYMin(-20f)
|
|
||||||
setYMax(20f)
|
|
||||||
}
|
|
||||||
|
|
||||||
xAxis.apply {
|
|
||||||
// Enable horizontal scrolling
|
|
||||||
scrollEnabled = true
|
|
||||||
|
|
||||||
// set the width of the datas
|
|
||||||
dataWidth = 10.0
|
|
||||||
|
|
||||||
// change the number of labels displayed
|
|
||||||
labelCount = 5
|
|
||||||
|
|
||||||
// change the spacing between values (it can be overriden if size to to small)
|
|
||||||
spacing = 8.0
|
|
||||||
|
|
||||||
// set the offset in data (use with [dataWidth])
|
|
||||||
x = 5.0
|
|
||||||
}
|
|
||||||
|
|
||||||
// refresh the Chart
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a random dataset
|
|
||||||
*/
|
|
||||||
private fun generateRandomDataset(size: Int = 100, min: Int = 0, max: Int = 100): ArrayList<Entry> {
|
|
||||||
val dataset: ArrayList<Entry> = arrayListOf()
|
|
||||||
|
|
||||||
for (i in 0 until size) {
|
|
||||||
dataset.add(
|
|
||||||
Entry(
|
|
||||||
i.toDouble(),
|
|
||||||
Random.nextInt(min, max).toFloat()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataset
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply Material3 theme to a [ChartView]
|
|
||||||
*/
|
|
||||||
private fun materielTheme(chart: ChartView, view: View) {
|
|
||||||
chart.apply {
|
|
||||||
yAxis.apply {
|
|
||||||
textLabel.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
|
||||||
)
|
|
||||||
linePaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
|
||||||
)
|
|
||||||
goalLinePaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorError
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
xAxis.apply {
|
|
||||||
textPaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (serie in series) {
|
|
||||||
if (serie is BarSerie) {
|
|
||||||
serie.apply {
|
|
||||||
barPaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorPrimary
|
|
||||||
)
|
|
||||||
textPaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorOnPrimary
|
|
||||||
)
|
|
||||||
textExternalPaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorPrimary
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (serie is LineSerie) {
|
|
||||||
serie.apply {
|
|
||||||
linePaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorPrimary
|
|
||||||
)
|
|
||||||
textPaint.color = MaterialColors.getColor(
|
|
||||||
view,
|
|
||||||
com.google.android.material.R.attr.colorOnPrimary
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<size
|
|
||||||
android:height="16dp"
|
|
||||||
android:width="0dp" />
|
|
||||||
</shape>
|
|
@ -1,23 +1,52 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
tools:context=".ui.MainActivity">
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/fragment"
|
android:layout_height="match_parent">
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_marginTop="?attr/actionBarSize"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/coordinatorLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
app:defaultNavHost="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:navGraph="@navigation/nav_graph" />
|
>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:titleCentered="true" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
|
||||||
|
android:id="@+id/scrollView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/nav_host_fragment"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
|
||||||
|
app:defaultNavHost="true"
|
||||||
|
app:navGraph="@navigation/nav_graph" />
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
216
sample/src/main/res/layout/fragment_chart.xml
Normal file
216
sample/src/main/res/layout/fragment_chart.xml
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.dzeio.charts.ChartView
|
||||||
|
android:id="@+id/chart"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/remove_value"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="Remove Value"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="8dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/add_value"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="Add Value"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/remove_serie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="Remove Serie"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="8dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/add_serie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="Add Serie"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/switch_subtype"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="Basic Chart"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/switch_animations"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Switch Animations"
|
||||||
|
android:checked="true"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="X Axis manipulation"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/switch_x_axis"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Enable X Axis"
|
||||||
|
android:checked="true"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="X Axis Number of labels"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.slider.Slider
|
||||||
|
android:id="@+id/slider_x_axis"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:value="2"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="20"
|
||||||
|
android:stepSize="1"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/switch_x_axis_scrollable"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:checked="false"
|
||||||
|
android:text="scrollable"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="X Axis number of items in view"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.slider.Slider
|
||||||
|
android:id="@+id/slider_x_axis_scroll"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:value="5"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="10"
|
||||||
|
android:stepSize="1"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="Y Axis manipulation"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="Y Axis Number of labels"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/switch_y_axis"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Switch Y Axis"
|
||||||
|
android:checked="true"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.slider.Slider
|
||||||
|
android:id="@+id/slider_y_axis"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Switch X Axis"
|
||||||
|
android:checked="true"
|
||||||
|
android:value="5"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:stepSize="1"
|
||||||
|
android:valueTo="20"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -4,6 +4,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:padding="16dp"
|
||||||
tools:context=".ui.MainFragment">
|
tools:context=".ui.MainFragment">
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
@ -13,35 +14,44 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:showDividers="middle"
|
|
||||||
android:divider="@drawable/shape_divider"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<com.dzeio.charts.ChartView
|
<com.dzeio.charts.ChartView
|
||||||
android:id="@+id/chart_grouped"
|
android:id="@+id/barchart"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp" />
|
android:layout_height="100dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/goto_barchart"
|
||||||
|
android:text="BarChart"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
|
||||||
<com.dzeio.charts.ChartView
|
<com.dzeio.charts.ChartView
|
||||||
android:id="@+id/chart_stacked"
|
android:id="@+id/linechart"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp" />
|
android:layout_height="100dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/goto_linechart"
|
||||||
|
android:text="LineChart"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
|
||||||
<com.dzeio.charts.ChartView
|
<com.dzeio.charts.ChartView
|
||||||
android:id="@+id/chart_customization"
|
android:id="@+id/bothchart"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp" />
|
android:layout_height="100dp" />
|
||||||
|
|
||||||
<com.dzeio.charts.ChartView
|
<Button
|
||||||
android:id="@+id/chart_bar"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp" />
|
android:id="@+id/goto_bar_line_chart"
|
||||||
|
android:text="Both barChart & lineChart"
|
||||||
<com.dzeio.charts.ChartView
|
android:layout_height="wrap_content"/>
|
||||||
android:id="@+id/chart_line"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="200dp" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
@ -3,10 +3,27 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/nav_graph"
|
android:id="@+id/nav_graph"
|
||||||
app:startDestination="@id/MainFragment">
|
app:startDestination="@id/main_fragment">
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/MainFragment"
|
android:id="@+id/main_fragment"
|
||||||
android:name="com.dzeio.chartstest.ui.MainFragment"
|
android:name="com.dzeio.chartsapp.ui.MainFragment"
|
||||||
tools:layout="@layout/fragment_main"/>
|
android:label="Dzeio Charts Examples"
|
||||||
|
tools:layout="@layout/fragment_main">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_MainFragment_to_ChartFragment"
|
||||||
|
app:destination="@id/chart_fragment" />
|
||||||
|
</fragment>
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/chart_fragment"
|
||||||
|
android:name="com.dzeio.chartsapp.ui.ChartFragment"
|
||||||
|
android:label="Test the chart"
|
||||||
|
tools:layout="@layout/fragment_chart">
|
||||||
|
|
||||||
|
<argument
|
||||||
|
android:name="chart_type"
|
||||||
|
app:nullable="true"
|
||||||
|
app:argType="string" />
|
||||||
|
</fragment>
|
||||||
</navigation>
|
</navigation>
|
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_launcher_background">#FFFFFF</color>
|
|
||||||
</resources>
|
|
@ -1,3 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<string name="app_name" translatable="false">Dzeio Charts</string>
|
|
||||||
</resources>
|
|
@ -1,4 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources>
|
||||||
|
<style name="Theme.Charts" parent="Theme.Material3.DynamicColors.DayNight">
|
||||||
<style name="Theme.Charts" parent="Theme.Material3.DynamicColors.DayNight" />
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user