This commit is contained in:
coco
2026-07-03 15:56:07 +08:00
commit caef23209c
5767 changed files with 1004268 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+31
View File
@@ -0,0 +1,31 @@
plugins {
id("ycharts.android.application")
id("ycharts.android.application.compose")
id("ycharts.android.test")
}
android {
namespace = "co.yml.ycharts.app"
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
defaultConfig {
versionCode = 1
versionName = "1.0"
applicationId = "co.yml.ycharts.app"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
}
dependencies {
implementation(project(mapOf("path" to ":YChartsLib")))
}
+21
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.kts.
#
# 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 co.yml.ycharts.app
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("co.yml.ycharts.app", appContext.packageName)
}
}
+62
View File
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.YCharts"
tools:targetApi="31">
<activity
android:name="co.yml.ycharts.app.presentation.BarChartActivity"
android:exported="false"
android:label="@string/title_activity_bar_chart"
android:theme="@style/Theme.YCharts" />
<activity
android:name="co.yml.ycharts.app.presentation.LineChartActivity"
android:exported="false"
android:label="@string/title_activity_line_chart"
android:theme="@style/Theme.YCharts" />
<activity
android:name="co.yml.ycharts.app.presentation.WaveChartActivity"
android:exported="false"
android:label="@string/title_activity_wave_chart"
android:theme="@style/Theme.YCharts" />
<activity
android:name="co.yml.ycharts.app.presentation.PieChartActivity"
android:exported="false"
android:label="@string/title_activity_pie_chart"
android:theme="@style/Theme.YCharts" />
<activity
android:name="co.yml.ycharts.app.presentation.DonutChartActivity"
android:exported="false"
android:label="@string/title_activity_donut_chart"
android:theme="@style/Theme.YCharts" />
<activity
android:name="co.yml.ycharts.app.presentation.CombinedLineAndBarChartActivity"
android:exported="false"
android:label="@string/title_activity_combined_line_bar_chart"
android:theme="@style/Theme.YCharts" />
<activity
android:name="co.yml.ycharts.app.presentation.BubbleChartActivity"
android:exported="false"
android:label="@string/title_activity_bubble_chart"
android:theme="@style/Theme.YCharts" />
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.YCharts">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,150 @@
package co.yml.ycharts.app
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import co.yml.ycharts.app.presentation.*
import co.yml.ycharts.app.ui.theme.YChartsTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
ChartsMenu()
}
}
}
@Composable
private fun ChartsMenu() {
Scaffold(modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = { AppBar() }) {
Column(
modifier = Modifier
.padding(it)
.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
ChartButton(title = getString(R.string.title_bar_chart), onClick = {
startActivity(
Intent(
this@MainActivity, BarChartActivity::class.java
)
)
addActivityInOutAnim()
})
ChartButton(title = getString(R.string.title_line_chart), onClick = {
startActivity(
Intent(
this@MainActivity, LineChartActivity::class.java
)
)
addActivityInOutAnim()
})
ChartButton(title = getString(R.string.title_wave_chart), onClick = {
startActivity(
Intent(
this@MainActivity, WaveChartActivity::class.java
)
)
addActivityInOutAnim()
})
ChartButton(title = getString(R.string.title_pie_chart), onClick = {
startActivity(
Intent(
this@MainActivity, PieChartActivity::class.java
)
)
addActivityInOutAnim()
})
ChartButton(title = getString(R.string.title_donut_chart), onClick = {
startActivity(
Intent(
this@MainActivity, DonutChartActivity::class.java
)
)
addActivityInOutAnim()
})
ChartButton(title = getString(R.string.title_bar_with_line_chart), onClick = {
startActivity(
Intent(
this@MainActivity, CombinedLineAndBarChartActivity::class.java
)
)
addActivityInOutAnim()
})
ChartButton(title = getString(R.string.bubble_chart), onClick = {
startActivity(
Intent(
this@MainActivity, BubbleChartActivity::class.java
)
)
addActivityInOutAnim()
})
}
}
}
private fun addActivityInOutAnim() {
overridePendingTransition(
R.anim.move_right_in_activity, R.anim.move_left_out_activity
)
}
}
@Composable
private fun AppBar() {
TopAppBar(modifier = Modifier.fillMaxWidth(),
backgroundColor = YChartsTheme.colors.button,
elevation = 6.dp,
title = {
Text(
text = stringResource(R.string.app_name),
color = YChartsTheme.colors.text,
textAlign = TextAlign.Center,
style = YChartsTheme.typography.header
)
})
}
@Composable
private fun ChartButton(title: String, onClick: () -> Unit) {
Column {
Spacer(modifier = Modifier.height(20.dp))
Button(
modifier = Modifier
.padding(end = 10.dp, start = 10.dp)
.fillMaxWidth()
.height(50.dp),
onClick = onClick,
colors = ButtonDefaults.buttonColors(backgroundColor = YChartsTheme.colors.button)
) {
Text(
text = title,
style = YChartsTheme.typography.button,
color = YChartsTheme.colors.text
)
}
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
ChartButton(title = "Chart", onClick = {})
}
@@ -0,0 +1,491 @@
package co.yml.ycharts.app.presentation
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.yml.charts.axis.AxisData
import co.yml.charts.axis.DataCategoryOptions
import co.yml.charts.common.components.Legends
import co.yml.charts.common.extensions.getMaxElementInYAxis
import co.yml.charts.common.model.LegendsConfig
import co.yml.charts.common.utils.DataUtils
import co.yml.charts.ui.barchart.BarChart
import co.yml.charts.ui.barchart.GroupBarChart
import co.yml.charts.ui.barchart.StackedBarChart
import co.yml.charts.ui.barchart.models.*
import co.yml.ycharts.app.R
import kotlin.random.Random
import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton
import co.yml.ycharts.app.ui.theme.YChartsTheme
/**
* Bar chart activity
*
* @constructor Create empty Bar chart activity
*/
class BarChartActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
Scaffold(modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = {
AppBarWithBackButton(
stringResource(id = R.string.title_bar_chart),
onBackPressed = {
onBackPressed()
})
})
{
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Alignment.TopCenter
) {
LazyColumn(content = {
items(6) { item ->
when (item) {
0 ->{ Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.barchart_solid_colors),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
BarchartWithSolidBars()
}
1 -> { Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.barchart_gradient_colors),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
BarchartWithGradientBars()}
2 ->{
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.barchart_background_color),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
BarchartWithBackgroundColor()
}
3 ->{
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.horizontal_bar_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
HorizontalBarChart()
}
4 ->{
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.grouped_bar_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
VerticalGroupBarChart()
}
5 ->{
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.stacked_barchart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
VerticalStackedBarChart()
}
}
}
})
}
}
}
}
}
}
/**
* Barchart with solid bars
*
*/
@Composable
private fun BarchartWithSolidBars() {
val maxRange = 50
val barData = DataUtils.getBarChartData(50, maxRange, BarChartType.VERTICAL, DataCategoryOptions())
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(barData.size - 1)
.bottomPadding(40.dp)
.axisLabelAngle(20f)
.startDrawPadding(48.dp)
.labelData { index -> barData[index].label }
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.labelData { index -> (index * (maxRange / yStepSize)).toString() }
.build()
val barChartData = BarChartData(
chartData = barData,
xAxisData = xAxisData,
yAxisData = yAxisData,
barStyle = BarStyle(
paddingBetweenBars = 20.dp,
barWidth = 25.dp
),
showYAxis = true,
showXAxis = true,
horizontalExtraSpace = 10.dp,
)
BarChart(modifier = Modifier.height(350.dp), barChartData = barChartData)
}
/**
* Barchart with gradient bars
*
*/
@Composable
private fun BarchartWithGradientBars() {
val maxRange = 100
val barData = DataUtils.getGradientBarChartData(50, 100)
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(barData.size - 1)
.bottomPadding(40.dp)
.axisLabelAngle(20f)
.startDrawPadding(48.dp)
.labelData { index -> barData[index].label }
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.labelData { index -> (index * (maxRange / yStepSize)).toString() }
.build()
val barChartData = BarChartData(
chartData = barData,
xAxisData = xAxisData,
yAxisData = yAxisData,
barStyle = BarStyle(paddingBetweenBars = 20.dp,
barWidth = 35.dp,
isGradientEnabled = true,
selectionHighlightData = SelectionHighlightData(
highlightBarColor = Color.Red,
highlightTextBackgroundColor = Color.Green,
popUpLabel = { _, y -> " Value : $y " }
)),
showYAxis = true,
showXAxis = true,
horizontalExtraSpace = 20.dp
)
BarChart(modifier = Modifier.height(350.dp), barChartData = barChartData)
}
/**
* Barchart with background color
*
*/
@Composable
private fun BarchartWithBackgroundColor() {
val maxRange = 100
val backgroundColor = Color.LightGray
val barData = DataUtils.getBarChartData(50, 100, BarChartType.VERTICAL, DataCategoryOptions())
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(barData.size - 1)
.bottomPadding(40.dp)
.startDrawPadding(48.dp)
.axisLabelAngle(20f)
.labelData { index -> barData[index].label }
.backgroundColor(backgroundColor)
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.backgroundColor(backgroundColor)
.labelData { index -> (index * (maxRange / yStepSize)).toString() }
.build()
val barChartData = BarChartData(
chartData = barData,
xAxisData = xAxisData,
yAxisData = yAxisData,
barStyle = BarStyle(paddingBetweenBars = 20.dp,
barWidth = 35.dp,
selectionHighlightData = SelectionHighlightData(
highlightBarColor = Color.Red,
highlightTextBackgroundColor = Color.Green,
popUpLabel = { _, y -> " Value : $y " }
)),
showYAxis = true,
showXAxis = true,
horizontalExtraSpace = 20.dp,
backgroundColor = backgroundColor
)
BarChart(modifier = Modifier.height(350.dp), barChartData = barChartData)
}
@Composable
private fun VerticalBarChart() {
val maxRange = 50
val barData =
DataUtils.getBarChartData(50, maxRange, BarChartType.VERTICAL, DataCategoryOptions())
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(barData.size - 1)
.bottomPadding(12.dp)
.axisLabelAngle(20f)
.startDrawPadding(48.dp)
.shouldDrawAxisLineTillEnd(false)
.labelData { index -> barData[index].label }
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.labelData { index -> (index * (maxRange / yStepSize)).toString() }
.build()
val barChartData = BarChartData(
chartData = barData,
xAxisData = xAxisData,
yAxisData = yAxisData,
barStyle = BarStyle(
paddingBetweenBars = 20.dp,
barWidth = 25.dp
),
showYAxis = true,
showXAxis = true,
horizontalExtraSpace = 10.dp,
drawBar = { drawScope, barData, drawOffset, height, barChartType, barStyle ->
with(drawScope) {
with(barStyle) {
drawRect(
color = barData.color,
topLeft = drawOffset,
size = if (barChartType == BarChartType.VERTICAL) Size(
barWidth.toPx(),
height
) else Size(height, barWidth.toPx()),
style = barDrawStyle,
blendMode = barBlendMode
)
}
}
}
)
BarChart(modifier = Modifier.height(350.dp), barChartData = barChartData)
}
@Composable
private fun HorizontalBarChart() {
val maxRange = 30
val barData =
DataUtils.getBarChartData(
10,
maxRange,
BarChartType.HORIZONTAL,
DataCategoryOptions(isDataCategoryInYAxis = true)
)
val xStepSize = 10
val xAxisData = AxisData.Builder()
.steps(xStepSize)
.bottomPadding(12.dp)
.endPadding(40.dp)
.labelData { index -> (index * (maxRange / xStepSize)).toString() }
.build()
val yAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(barData.size - 1)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.setDataCategoryOptions(
DataCategoryOptions(
isDataCategoryInYAxis = true,
isDataCategoryStartFromBottom = false
)
)
.startDrawPadding(48.dp)
.labelData { index -> barData[index].label }
.build()
val barChartData = BarChartData(
chartData = barData,
xAxisData = xAxisData,
yAxisData = yAxisData,
barStyle = BarStyle(
isGradientEnabled = false,
paddingBetweenBars = 20.dp,
barWidth = 35.dp,
selectionHighlightData = SelectionHighlightData(
highlightBarColor = Color.Red,
highlightTextBackgroundColor = Color.Green,
popUpLabel = { x, _ -> " Value : $x " },
barChartType = BarChartType.HORIZONTAL
),
),
showYAxis = true,
showXAxis = true,
horizontalExtraSpace = 20.dp,
barChartType = BarChartType.HORIZONTAL
)
BarChart(
modifier = Modifier.height(350.dp),
barChartData = barChartData
)
}
@Composable
fun VerticalGroupBarChart() {
val maxRange = 100
val barSize = 3
val groupBarData = DataUtils.getGroupBarChartData(50, maxRange, barSize)
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.bottomPadding(5.dp)
.startDrawPadding(48.dp)
.labelData { index -> index.toString() }
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.labelData { index -> (index * (maxRange / yStepSize)).toString() }
.build()
val colorPaletteList = DataUtils.getColorPaletteList(barSize)
val legendsConfig = LegendsConfig(
legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList),
gridColumnCount = 3
)
val groupBarPlotData = BarPlotData(
groupBarList = groupBarData,
barStyle = BarStyle(barWidth = 35.dp),
barColorPaletteList = colorPaletteList
)
val groupBarChartData = GroupBarChartData(
barPlotData = groupBarPlotData,
xAxisData = xAxisData,
yAxisData = yAxisData,
groupSeparatorConfig = GroupSeparatorConfig(0.dp)
)
Column(
Modifier
.height(450.dp)
) {
GroupBarChart(
modifier = Modifier
.height(400.dp),
groupBarChartData = groupBarChartData
)
Legends(
legendsConfig = legendsConfig
)
}
}
@Composable
fun VerticalStackedBarChart() {
val barSize = 3
val listSize = 10
val groupBarData = DataUtils.getGroupBarChartData(listSize, 100, barSize)
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(listSize - 1)
.startDrawPadding(48.dp)
.labelData { index -> "C $index" }
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.labelData { index ->
val valueList = mutableListOf<Float>()
groupBarData.map { groupBar ->
var yMax = 0f
groupBar.barList.forEach {
yMax += it.point.y
}
valueList.add(yMax)
}
val maxElementInYAxis = getMaxElementInYAxis(valueList.maxOrNull() ?: 0f, yStepSize)
(index * (maxElementInYAxis / yStepSize)).toString()
}
.topPadding(36.dp)
.build()
val colorPaletteList = DataUtils.getColorPaletteList(barSize)
val legendsConfig = LegendsConfig(
legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList),
gridColumnCount = 3
)
val groupBarPlotData = BarPlotData(
groupBarList = groupBarData,
barStyle = BarStyle(
barWidth = 35.dp,
selectionHighlightData = SelectionHighlightData(
isHighlightFullBar = true,
groupBarPopUpLabel = { name, value ->
"Name : C$name Value : ${String.format("%.2f", value)}"
}
)
),
barColorPaletteList = colorPaletteList
)
val groupBarChartData = GroupBarChartData(
barPlotData = groupBarPlotData,
xAxisData = xAxisData,
yAxisData = yAxisData,
paddingBetweenStackedBars = 4.dp,
drawBar = { drawScope, barChartData, barStyle, drawOffset, height, barIndex ->
with(drawScope) {
drawRect(
color = colorPaletteList[barIndex],
topLeft = drawOffset,
size = Size(barStyle.barWidth.toPx(), height),
style = barStyle.barDrawStyle,
blendMode = barStyle.barBlendMode
)
}
}
)
Column(
Modifier
.height(500.dp)
) {
StackedBarChart(
modifier = Modifier
.height(400.dp),
groupBarChartData = groupBarChartData
)
Legends(
legendsConfig = legendsConfig
)
}
}
@@ -0,0 +1,185 @@
package co.yml.ycharts.app.presentation
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.yml.charts.axis.AxisData
import co.yml.charts.common.extensions.formatToSinglePrecision
import co.yml.charts.common.model.Point
import co.yml.charts.common.utils.DataUtils
import co.yml.charts.ui.bubblechart.BubbleChart
import co.yml.charts.ui.bubblechart.model.BubbleChartData
import co.yml.charts.ui.linechart.model.GridLines
import co.yml.ycharts.app.R
import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton
import co.yml.ycharts.app.ui.theme.YChartsTheme
/**
* Line chart activity
*
* @constructor Create empty Line chart activity
*/
class BubbleChartActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
Scaffold(modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = {
AppBarWithBackButton(
stringResource(id = R.string.bubble_chart),
onBackPressed = {
onBackPressed()
})
})
{
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Alignment.TopCenter
) {
LazyColumn(content = {
item {
Text(
modifier = Modifier.padding(12.dp),
text = getString(R.string.gradient_bubble_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
BubbleChartWithGrid(
pointsData = DataUtils.getRandomPoints(
200,
start = 30,
maxRange = 100
)
)
Spacer(modifier = Modifier.height(12.dp))
}
item {
Text(
modifier = Modifier.padding(12.dp),
text = getString(R.string.solid_bubble_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
SolidBubbleChart(
pointsData = DataUtils.getRandomPoints(
200,
start = 30,
maxRange = 900
)
)
Spacer(modifier = Modifier.height(12.dp))
}
})
}
}
}
}
}
}
/**
* Bubble chart with grid lines
*
* @param pointsData
*/
@Composable
private fun BubbleChartWithGrid(pointsData: List<Point>) {
val steps = 5
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(pointsData.size - 1)
.labelData { i -> pointsData[i].x.toInt().toString() }
.labelAndAxisLinePadding(15.dp)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.labelAndAxisLinePadding(15.dp)
.labelData { i ->
// Add yMin to get the negative axis values to the scale
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}.build()
val data = BubbleChartData(
DataUtils.getBubbleChartDataWithGradientStyle(pointsData),
xAxisData = xAxisData,
yAxisData = yAxisData,
gridLines = GridLines()
)
BubbleChart(
modifier = Modifier
.fillMaxWidth()
.height(500.dp),
bubbleChartData = data
)
}
/**
* Bubble chart with grid lines
*
* @param pointsData
*/
@Composable
private fun SolidBubbleChart(pointsData: List<Point>) {
val steps = 5
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(pointsData.size - 1)
.labelData { i ->pointsData[i].x.toInt().toString()}
.labelAndAxisLinePadding(15.dp)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.labelAndAxisLinePadding(15.dp)
.labelData { i ->
// Add yMin to get the negative axis values to the scale
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}.build()
val data = BubbleChartData(
DataUtils.getBubbleChartDataWithSolidStyle(pointsData),
xAxisData = xAxisData,
yAxisData = yAxisData,
gridLines = GridLines()
)
BubbleChart(
modifier = Modifier
.fillMaxWidth()
.height(500.dp),
bubbleChartData = data
)
}
@@ -0,0 +1,235 @@
@file:OptIn(ExperimentalMaterialApi::class)
package co.yml.ycharts.app.presentation
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.yml.charts.axis.AxisData
import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton
import co.yml.ycharts.app.ui.theme.YChartsTheme
import co.yml.charts.ui.barchart.models.BarPlotData
import co.yml.charts.ui.barchart.models.BarStyle
import co.yml.charts.ui.combinedchart.CombinedChart
import co.yml.charts.ui.combinedchart.model.CombinedChartData
import co.yml.charts.common.components.Legends
import co.yml.charts.common.model.LegendsConfig
import co.yml.charts.common.utils.DataUtils
import co.yml.charts.ui.linechart.model.IntersectionPoint
import co.yml.charts.ui.linechart.model.Line
import co.yml.charts.ui.linechart.model.LinePlotData
import co.yml.charts.ui.linechart.model.LineStyle
import co.yml.charts.ui.linechart.model.SelectionHighlightPoint
import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp
import co.yml.ycharts.app.R
/**
* Combined line and bar chart activity
*
* @constructor Create empty Combined line and bar chart activity
*/
class CombinedLineAndBarChartActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
Scaffold(modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = {
AppBarWithBackButton(
stringResource(id = R.string.title_bar_with_line_chart),
onBackPressed = {
onBackPressed()
})
})
{
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Alignment.TopCenter
) {
LazyColumn(content = {
items(2) { item ->
when (item) {
0 ->{
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.combined_bar_line_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
BarWithLineChart()
}
1->{
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.combined_chart_with_background),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
BarWithLineChartAndBackground()
}
}
}
})
}
}
}
}
}
}
/**
* Bar with line chart
*
*/
@Composable
fun BarWithLineChart() {
val maxRange = 100
val groupBarData = DataUtils.getGroupBarChartData(50, 100, 3)
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.bottomPadding(5.dp)
.labelData { index -> index.toString() }
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.labelData { index -> (index * (maxRange / yStepSize)).toString() }
.build()
val linePlotData = LinePlotData(
lines = listOf(
Line(
DataUtils.getLineChartData(50, maxRange = 100),
lineStyle = LineStyle(color = Color.Blue),
intersectionPoint = IntersectionPoint(),
selectionHighlightPoint = SelectionHighlightPoint(),
selectionHighlightPopUp = SelectionHighlightPopUp()
),
Line(
DataUtils.getLineChartData(50, maxRange = 100),
lineStyle = LineStyle(color = Color.Black),
intersectionPoint = IntersectionPoint(),
selectionHighlightPoint = SelectionHighlightPoint(),
selectionHighlightPopUp = SelectionHighlightPopUp()
)
)
)
val colorPaletteList = DataUtils.getColorPaletteList(3)
val legendsConfig = LegendsConfig(
legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList),
gridColumnCount = 3
)
val barPlotData = BarPlotData(
groupBarList = groupBarData,
barStyle = BarStyle(barWidth = 35.dp),
barColorPaletteList = colorPaletteList
)
val combinedChartData = CombinedChartData(
combinedPlotDataList = listOf(barPlotData, linePlotData),
xAxisData = xAxisData,
yAxisData = yAxisData
)
Column(
Modifier
.height(500.dp)
) {
CombinedChart(
modifier = Modifier
.height(400.dp),
combinedChartData = combinedChartData
)
Legends(
legendsConfig = legendsConfig
)
}
}
/**
* Bar with line chart and background
*
*/
@Composable
fun BarWithLineChartAndBackground() {
val maxRange = 100
val groupBarData = DataUtils.getGroupBarChartData(50, 100, 3)
val yStepSize = 10
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.bottomPadding(5.dp)
.backgroundColor(Color.Yellow)
.labelData { index -> index.toString() }
.build()
val yAxisData = AxisData.Builder()
.steps(yStepSize)
.backgroundColor(Color.Yellow)
.labelAndAxisLinePadding(20.dp)
.axisOffset(20.dp)
.labelData { index -> (index * (maxRange / yStepSize)).toString() }
.build()
val linePlotData = LinePlotData(
lines = listOf(
Line(
DataUtils.getLineChartData(50, maxRange = 100),
lineStyle = LineStyle(color = Color.Blue),
intersectionPoint = IntersectionPoint(),
selectionHighlightPoint = SelectionHighlightPoint(),
selectionHighlightPopUp = SelectionHighlightPopUp()
),
Line(
DataUtils.getLineChartData(50, maxRange = 100),
lineStyle = LineStyle(color = Color.Black),
intersectionPoint = IntersectionPoint(),
selectionHighlightPoint = SelectionHighlightPoint(),
selectionHighlightPopUp = SelectionHighlightPopUp()
)
)
)
val colorPaletteList = DataUtils.getColorPaletteList(3)
val legendsConfig = LegendsConfig(
legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList),
gridColumnCount = 3
)
val barPlotData = BarPlotData(
groupBarList = groupBarData,
barStyle = BarStyle(barWidth = 35.dp),
barColorPaletteList = colorPaletteList
)
val combinedChartData = CombinedChartData(
combinedPlotDataList = listOf(barPlotData, linePlotData),
xAxisData = xAxisData,
yAxisData = yAxisData,
backgroundColor = Color.Yellow
)
Column(
Modifier
.height(500.dp)
) {
CombinedChart(
modifier = Modifier
.height(400.dp),
combinedChartData = combinedChartData
)
Legends(
legendsConfig = legendsConfig
)
}
}
@@ -0,0 +1,250 @@
package co.yml.ycharts.app.presentation
import android.content.Context
import android.graphics.Typeface
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import co.yml.charts.common.components.Legends
import co.yml.charts.common.utils.DataUtils
import co.yml.charts.ui.piechart.charts.DonutPieChart
import co.yml.charts.ui.piechart.models.PieChartConfig
import co.yml.charts.ui.piechart.utils.proportion
import co.yml.ycharts.app.R
import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton
import co.yml.ycharts.app.ui.theme.YChartsTheme
/**
* Donut chart activity
*
* @constructor Create empty Donut chart activity
*/
@OptIn(ExperimentalMaterialApi::class)
class DonutChartActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
Scaffold(
modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = {
AppBarWithBackButton(
stringResource(id = R.string.title_donut_chart),
onBackPressed = {
onBackPressed()
})
})
{
val context = LocalContext.current
LazyColumn(content = {
items(2) { item ->
when (item) {
0 -> {
Text(
modifier = Modifier.padding(12.dp),
text = getString(R.string.simple_donut_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
Box(
modifier = Modifier
.padding(it)
.fillMaxWidth()
) {
Spacer(modifier = Modifier.height(20.dp))
SimpleDonutChart(context)
}
}
1 -> {
Text(
modifier = Modifier.padding(12.dp),
text = getString(R.string.multiple_donuts),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
MultipleSmallDonutCharts(context)
}
}
}
})
}
}
}
}
}
/**
* Simple donut chart
*
* @param context
*/
@ExperimentalMaterialApi
@Composable
private fun SimpleDonutChart(context: Context) {
val accessibilitySheetState =
rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
val data = DataUtils.getDonutChartData()
// Sum of all the values
val sumOfValues = data.totalLength
// Calculate each proportion value
val proportions = data.slices.proportion(sumOfValues)
val pieChartConfig =
PieChartConfig(
labelVisible = true,
strokeWidth = 120f,
labelColor = Color.Black,
activeSliceAlpha = .9f,
isEllipsizeEnabled = true,
labelTypeface = Typeface.defaultFromStyle(Typeface.BOLD),
isAnimationEnable = true,
chartPadding = 25,
labelFontSize = 42.sp,
)
Column(
modifier = Modifier
.fillMaxWidth()
.height(500.dp)
) {
Legends(legendsConfig = DataUtils.getLegendsConfigFromPieChartData(pieChartData = data, 3))
DonutPieChart(
modifier = Modifier
.fillMaxWidth()
.height(400.dp),
data,
pieChartConfig
) { slice ->
Toast.makeText(context, slice.label, Toast.LENGTH_SHORT).show()
}
}
}
/**
* Multiple small donut charts
*
* @param context
*/
@ExperimentalMaterialApi
@Composable
private fun MultipleSmallDonutCharts(context: Context) {
val accessibilitySheetState =
rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
val data = DataUtils.getDonutChartData()
// Sum of all the values
val sumOfValues = data.totalLength
// Calculate each proportion value
val proportions = data.slices.proportion(sumOfValues)
val firstPieChartConfig =
PieChartConfig(
labelVisible = true,
strokeWidth = 50f,
labelColor = Color.Black,
backgroundColor = Color.Yellow,
activeSliceAlpha = .9f,
isEllipsizeEnabled = true,
labelTypeface = Typeface.defaultFromStyle(Typeface.BOLD),
isAnimationEnable = true,
chartPadding = 25,
labelFontSize = 16.sp
)
val secondPieChartConfig =
PieChartConfig(
labelVisible = true,
strokeWidth = 50f,
labelColor = Color.Black,
activeSliceAlpha = .9f,
isEllipsizeEnabled = true,
backgroundColor = Color.Black,
labelTypeface = Typeface.defaultFromStyle(Typeface.BOLD),
isAnimationEnable = true,
chartPadding = 25,
labelFontSize = 16.sp,
isSumVisible = true,
sumUnit = "unit",
labelColorType = PieChartConfig.LabelColorType.SLICE_COLOR,
labelType = PieChartConfig.LabelType.VALUE
)
val thirdPieChartConfig =
PieChartConfig(
labelVisible = true,
strokeWidth = 50f,
labelColor = Color.Black,
activeSliceAlpha = .9f,
backgroundColor = Color.LightGray,
isEllipsizeEnabled = true,
labelTypeface = Typeface.defaultFromStyle(Typeface.BOLD),
isAnimationEnable = true,
chartPadding = 25,
labelFontSize = 16.sp
)
Column(
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
) {
Legends(legendsConfig = DataUtils.getLegendsConfigFromPieChartData(pieChartData = data, 3))
Spacer(modifier = Modifier.height(20.dp))
LazyRow(
modifier = Modifier
.fillMaxWidth()
) {
item {
DonutPieChart(
modifier = Modifier
.width(100.dp)
.height(100.dp),
data,
firstPieChartConfig
) { slice ->
Toast.makeText(context, slice.label, Toast.LENGTH_SHORT).show()
}
Spacer(modifier = Modifier.width(30.dp))
}
item {
DonutPieChart(
modifier = Modifier
.width(100.dp)
.height(100.dp),
data,
secondPieChartConfig
) { slice ->
Toast.makeText(context, slice.label, Toast.LENGTH_SHORT).show()
}
Spacer(modifier = Modifier.width(30.dp))
}
item {
DonutPieChart(
modifier = Modifier
.width(100.dp)
.height(100.dp),
data,
thirdPieChartConfig
) { slice ->
Toast.makeText(context, slice.label, Toast.LENGTH_SHORT).show()
}
Spacer(modifier = Modifier.width(30.dp))
}
}
}
}
@@ -0,0 +1,642 @@
package co.yml.ycharts.app.presentation
import android.graphics.Typeface
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.yml.charts.axis.AxisData
import co.yml.charts.common.components.Legends
import co.yml.charts.common.extensions.formatToSinglePrecision
import co.yml.charts.common.model.LegendsConfig
import co.yml.charts.common.model.Point
import co.yml.charts.common.utils.DataUtils
import co.yml.charts.ui.linechart.LineChart
import co.yml.charts.ui.linechart.model.GridLines
import co.yml.charts.ui.linechart.model.IntersectionPoint
import co.yml.charts.ui.linechart.model.Line
import co.yml.charts.ui.linechart.model.LineChartData
import co.yml.charts.ui.linechart.model.LinePlotData
import co.yml.charts.ui.linechart.model.LineStyle
import co.yml.charts.ui.linechart.model.LineType
import co.yml.charts.ui.linechart.model.SelectionHighlightPoint
import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp
import co.yml.charts.ui.linechart.model.ShadowUnderLine
import co.yml.ycharts.app.R
import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton
import co.yml.ycharts.app.ui.theme.YChartsTheme
/**
* Line chart activity
*
* @constructor Create empty Line chart activity
*/
class LineChartActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
Scaffold(modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = {
AppBarWithBackButton(
stringResource(id = R.string.title_line_chart),
onBackPressed = {
onBackPressed()
})
})
{
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Alignment.TopCenter
) {
LazyColumn(content = {
item {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.linechart_default_style),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
SingleLineChartWithGridLines(
DataUtils.getLineChartData(
100,
start = 50,
maxRange = 100
)
)
Spacer(modifier = Modifier.height(12.dp))
Divider(modifier = Modifier
.fillMaxWidth()
.height(1.dp))
}
item {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.linechart_straight_line_style),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
StraightLinechart(DataUtils.getLineChartData(50, maxRange = 200))
Spacer(modifier = Modifier.height(12.dp))
Divider(modifier = Modifier
.fillMaxWidth()
.height(1.dp))
}
item {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.linechart_dotted_style),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
DottedLinechart(DataUtils.getLineChartData(
200,
start = -50,
maxRange = 50
))
Spacer(modifier = Modifier.height(12.dp))
Divider(modifier = Modifier
.fillMaxWidth()
.height(1.dp))
}
item {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.linechart_multiple_tones),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
MultipleToneLinechart(DataUtils.getLineChartData(
200,
start = -50,
maxRange = 50
))
Spacer(modifier = Modifier.height(12.dp))
Divider(modifier = Modifier
.fillMaxWidth()
.height(1.dp))
}
item {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.combined_line_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
CombinedLinechart(DataUtils.getLineChartData(
200,
start = -50,
maxRange = 50
))
Spacer(modifier = Modifier.height(12.dp))
Divider(modifier = Modifier
.fillMaxWidth()
.height(1.dp))
}
item {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.combined_line_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
CombinedLinechartWithBackground(DataUtils.getLineChartData(
200,
start = -50,
maxRange = 50
))
}
})
}
}
}
}
}
}
/**
* Single line chart with grid lines
*
* @param pointsData
*/
@Composable
private fun SingleLineChartWithGridLines(pointsData: List<Point>) {
val steps = 5
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.topPadding(105.dp)
.steps(pointsData.size - 1)
.labelData { i -> pointsData[i].x.toInt().toString() }
.labelAndAxisLinePadding(15.dp)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.labelAndAxisLinePadding(20.dp)
.labelData { i ->
// Add yMin to get the negative axis values to the scale
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}.build()
val data = LineChartData(
linePlotData = LinePlotData(
lines = listOf(
Line(
dataPoints = pointsData,
LineStyle(),
IntersectionPoint(),
SelectionHighlightPoint(),
ShadowUnderLine(),
SelectionHighlightPopUp()
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData,
gridLines = GridLines()
)
LineChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
lineChartData = data
)
}
/**
* Straight linechart
*
* @param pointsData
*/
@Composable
private fun StraightLinechart(pointsData: List<Point>) {
val xAxisData = AxisData.Builder()
.axisStepSize(40.dp)
.steps(pointsData.size - 1)
.labelData { i -> (1900 + i).toString() }
.axisLabelAngle(20f)
.labelAndAxisLinePadding(15.dp)
.axisLabelColor(Color.Blue)
.axisLineColor(Color.DarkGray)
.typeFace(Typeface.DEFAULT_BOLD)
.build()
val yAxisData = AxisData.Builder()
.steps(10)
.labelData { i -> "${(i * 20)}k" }
.labelAndAxisLinePadding(30.dp)
.axisLabelColor(Color.Blue)
.axisLineColor(Color.DarkGray)
.typeFace(Typeface.DEFAULT_BOLD)
.build()
val data = LineChartData(
linePlotData = LinePlotData(
lines = listOf(
Line(
dataPoints = pointsData,
lineStyle = LineStyle(lineType = LineType.Straight(), color = Color.Blue),
intersectionPoint = IntersectionPoint(color = Color.Red),
selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y ->
val xLabel = "x : ${(1900 + x).toInt()} "
val yLabel = "y : ${String.format("%.2f", y)}"
"$xLabel $yLabel"
})
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData
)
LineChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
lineChartData = data
)
}
/**
* Multiple tone linechart
*
* @param pointsData
*/
@Composable
private fun MultipleToneLinechart(pointsData: List<Point>) {
val xAxisData = AxisData.Builder()
.axisStepSize(40.dp)
.steps(pointsData.size - 1)
.labelData { i -> (1900 + i).toString() }
.axisLabelAngle(20f)
.labelAndAxisLinePadding(15.dp)
.axisLabelColor(Color.Blue)
.axisLineColor(Color.DarkGray)
.typeFace(Typeface.DEFAULT_BOLD)
.build()
val yAxisData = AxisData.Builder()
.steps(10)
.labelData { i -> "${(i * 20)}k" }
.labelAndAxisLinePadding(30.dp)
.axisLabelColor(Color.Blue)
.axisLineColor(Color.DarkGray)
.typeFace(Typeface.DEFAULT_BOLD)
.build()
val data = LineChartData(
linePlotData = LinePlotData(
lines = listOf(
Line(
dataPoints = pointsData,
lineStyle = LineStyle(lineType = LineType.Straight(), color = Color.Blue),
intersectionPoint = IntersectionPoint(color = Color.Red),
selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y ->
val xLabel = "x : ${(1900 + x).toInt()} "
val yLabel = "y : ${String.format("%.2f", y)}"
"$xLabel $yLabel"
})
), Line(
dataPoints = pointsData.subList(0, 10),
lineStyle = LineStyle(lineType = LineType.Straight(), color = Color.Magenta),
intersectionPoint = IntersectionPoint(color = Color.Red),
selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y ->
val xLabel = "x : ${(1900 + x).toInt()} "
val yLabel = "y : ${String.format("%.2f", y)}"
"$xLabel $yLabel"
})
), Line(
dataPoints = pointsData.subList(15, 30),
lineStyle = LineStyle(lineType = LineType.Straight(), color = Color.Yellow),
intersectionPoint = IntersectionPoint(color = Color.Red),
selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y ->
val xLabel = "x : ${(1900 + x).toInt()} "
val yLabel = "y : ${String.format("%.2f", y)}"
"$xLabel $yLabel"
})
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData
)
LineChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
lineChartData = data
)
}
/**
* Dotted linechart
*
* @param pointsData
*/
@Composable
private fun DottedLinechart(pointsData: List<Point>) {
val steps = 10
val xAxisData = AxisData.Builder()
.axisStepSize(40.dp)
.steps(pointsData.size - 1)
.labelData { i -> i.toString() }
.labelAndAxisLinePadding(15.dp)
.axisLineColor(Color.Red)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.labelData { i ->
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}
.axisLineColor(Color.Red)
.labelAndAxisLinePadding(20.dp)
.build()
val data = LineChartData(
linePlotData = LinePlotData(
lines = listOf(
Line(
dataPoints = pointsData,
lineStyle = LineStyle(
lineType = LineType.SmoothCurve(isDotted = true),
color = Color.Green
),
shadowUnderLine = ShadowUnderLine(
brush = Brush.verticalGradient(
listOf(
Color.Green,
Color.Transparent
)
), alpha = 0.3f
),
selectionHighlightPoint = SelectionHighlightPoint(
color = Color.Green
),
selectionHighlightPopUp = SelectionHighlightPopUp(
backgroundColor = Color.Black,
backgroundStyle = Stroke(2f),
labelColor = Color.Red,
labelTypeface = Typeface.DEFAULT_BOLD
)
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData
)
LineChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
lineChartData = data
)
}
/**
* Combined linechart
*
* @param pointsData
*/
@Composable
private fun CombinedLinechart(pointsData: List<Point>) {
val steps = 5
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(pointsData.size - 1)
.labelData { i -> i.toString() }
.labelAndAxisLinePadding(15.dp)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.labelAndAxisLinePadding(20.dp)
.labelData { i ->
// Add yMin to get the negative axis values to the scale
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}.build()
val colorPaletteList = listOf<Color>(Color.Blue,Color.Yellow,Color.Magenta,Color.DarkGray)
val legendsConfig = LegendsConfig(
legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList),
gridColumnCount = 4
)
val data = LineChartData(
linePlotData = LinePlotData(
lines = listOf(
Line(
dataPoints = pointsData,
lineStyle = LineStyle(
lineType = LineType.SmoothCurve(isDotted = true),
color = colorPaletteList.first()
),
shadowUnderLine = ShadowUnderLine(
brush = Brush.verticalGradient(
listOf(
Color.Green,
Color.Transparent
)
), alpha = 0.3f
),
selectionHighlightPoint = SelectionHighlightPoint(
color = Color.Green
),
selectionHighlightPopUp = SelectionHighlightPopUp(
backgroundColor = Color.Black,
backgroundStyle = Stroke(2f),
labelColor = Color.Red,
labelTypeface = Typeface.DEFAULT_BOLD
)
),
Line(
dataPoints = pointsData.subList(10,20),
lineStyle = LineStyle(lineType = LineType.SmoothCurve(), color = colorPaletteList[1]),
intersectionPoint = IntersectionPoint(color = Color.Red),
selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y ->
val xLabel = "x : ${(1900 + x).toInt()} "
val yLabel = "y : ${String.format("%.2f", y)}"
"$xLabel $yLabel"
})
),
Line(
dataPoints = DataUtils.getLineChartData(
20,
start = 0,
maxRange = 50
),
LineStyle(color = colorPaletteList[2]),
IntersectionPoint(),
SelectionHighlightPoint(),
shadowUnderLine = ShadowUnderLine(
brush = Brush.verticalGradient(
listOf(
Color.Cyan,
Color.Blue
)
), alpha = 0.5f
),
SelectionHighlightPopUp()
),
Line(
dataPoints = pointsData.subList(10, 20),
LineStyle(color = colorPaletteList[3]),
IntersectionPoint(),
SelectionHighlightPoint(),
ShadowUnderLine(),
SelectionHighlightPopUp()
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData,
gridLines = GridLines()
)
Column(modifier = Modifier.height(400.dp)) {
LineChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
lineChartData = data
)
Legends(
legendsConfig = legendsConfig
)
}
}
/**
* Combined linechart with background
*
* @param pointsData
*/
@Composable
private fun CombinedLinechartWithBackground(pointsData: List<Point>) {
val steps = 5
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.steps(pointsData.size - 1)
.backgroundColor(Color.Yellow)
.labelData { i -> i.toString() }
.labelAndAxisLinePadding(15.dp)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.backgroundColor(Color.Yellow)
.labelAndAxisLinePadding(20.dp)
.labelData { i ->
// Add yMin to get the negative axis values to the scale
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}.build()
val colorPaletteList = listOf<Color>(Color.Blue,Color.Yellow,Color.Magenta,Color.DarkGray)
val legendsConfig = LegendsConfig(
legendLabelList = DataUtils.getLegendsLabelData(colorPaletteList),
gridColumnCount = 4
)
val data = LineChartData(
linePlotData = LinePlotData(
lines = listOf(
Line(
dataPoints = pointsData,
lineStyle = LineStyle(
lineType = LineType.SmoothCurve(isDotted = true),
color = colorPaletteList.first()
),
shadowUnderLine = ShadowUnderLine(
brush = Brush.verticalGradient(
listOf(
Color.Green,
Color.Transparent
)
), alpha = 0.3f
),
selectionHighlightPoint = SelectionHighlightPoint(
color = Color.Green
),
selectionHighlightPopUp = SelectionHighlightPopUp(
backgroundColor = Color.Black,
backgroundStyle = Stroke(2f),
labelColor = Color.Red,
labelTypeface = Typeface.DEFAULT_BOLD
)
),
Line(
dataPoints = pointsData.subList(10,20),
lineStyle = LineStyle(lineType = LineType.SmoothCurve(), color = colorPaletteList[1]),
intersectionPoint = IntersectionPoint(color = Color.Red),
selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y ->
val xLabel = "x : ${(1900 + x).toInt()} "
val yLabel = "y : ${String.format("%.2f", y)}"
"$xLabel $yLabel"
})
),
Line(
dataPoints = DataUtils.getLineChartData(
20,
start = 0,
maxRange = 50
),
LineStyle(color = colorPaletteList[2]),
IntersectionPoint(),
SelectionHighlightPoint(),
shadowUnderLine = ShadowUnderLine(
brush = Brush.verticalGradient(
listOf(
Color.Cyan,
Color.Blue
)
), alpha = 0.5f
),
SelectionHighlightPopUp()
),
Line(
dataPoints = pointsData.subList(10, 20),
LineStyle(color = colorPaletteList[3]),
IntersectionPoint(),
SelectionHighlightPoint(),
ShadowUnderLine(),
SelectionHighlightPopUp()
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData,
gridLines = GridLines(),
backgroundColor = Color.Yellow
)
Column(
modifier = Modifier
.height(400.dp)
) {
LineChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
lineChartData = data
)
Legends(
legendsConfig = legendsConfig
)
}
}
@@ -0,0 +1,156 @@
package co.yml.ycharts.app.presentation
import android.content.Context
import android.graphics.Typeface
import android.os.Bundle
import android.text.TextUtils
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.yml.charts.common.components.Legends
import co.yml.charts.common.utils.DataUtils
import co.yml.charts.ui.piechart.charts.PieChart
import co.yml.charts.ui.piechart.models.PieChartConfig
import co.yml.ycharts.app.R
import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton
import co.yml.ycharts.app.ui.theme.YChartsTheme
/**
* Pie chart activity
*
* @constructor Create empty Pie chart activity
*/
class PieChartActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
Scaffold(modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = {
AppBarWithBackButton(
stringResource(id = R.string.title_pie_chart),
onBackPressed = {
onBackPressed()
})
})
{
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Alignment.TopCenter
) {
LazyColumn(content = {
items(3) { item ->
when (item) {
0 -> {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.simple_piechart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
SimplePiechart(LocalContext.current)
}
1 -> {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.piechart_with_lables),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
PiechartWithSliceLables(LocalContext.current)
}
}
}
})
}
}
}
}
}
}
/**
* Simple piechart
*
* @param context
*/
@Composable
private fun SimplePiechart(context: Context) {
val pieChartData = DataUtils.getPieChartData()
val pieChartConfig =
PieChartConfig(
labelVisible = true,
activeSliceAlpha = .9f,
isEllipsizeEnabled = true,
sliceLabelEllipsizeAt = TextUtils.TruncateAt.MIDDLE,
isAnimationEnable = true,
chartPadding = 30,
backgroundColor = Color.Black,
showSliceLabels = false,
animationDuration = 1500
)
Column(modifier = Modifier.height(500.dp)) {
Spacer(modifier = Modifier.height(20.dp))
Legends(legendsConfig = DataUtils.getLegendsConfigFromPieChartData(pieChartData, 3))
PieChart(
modifier = Modifier
.fillMaxWidth()
.height(400.dp),
pieChartData,
pieChartConfig
) { slice ->
Toast.makeText(context, slice.label, Toast.LENGTH_SHORT).show()
}
}
}
/**
* Piechart with slice lables
*
* @param context
*/
@Composable
private fun PiechartWithSliceLables(context: Context) {
val pieChartData = DataUtils.getPieChartData2()
val pieChartConfig =
PieChartConfig(
activeSliceAlpha = .9f,
isEllipsizeEnabled = true,
sliceLabelEllipsizeAt = TextUtils.TruncateAt.MIDDLE,
sliceLabelTypeface = Typeface.defaultFromStyle(Typeface.ITALIC),
isAnimationEnable = true,
chartPadding = 20,
showSliceLabels = true,
labelVisible = true
)
Column(modifier = Modifier.height(500.dp)) {
Legends(legendsConfig = DataUtils.getLegendsConfigFromPieChartData(pieChartData, 3))
PieChart(
modifier = Modifier
.fillMaxWidth()
.height(400.dp),
pieChartData,
pieChartConfig
) { slice ->
Toast.makeText(context, slice.label, Toast.LENGTH_SHORT).show()
}
}
}
@@ -0,0 +1,296 @@
package co.yml.ycharts.app.presentation
import android.graphics.Typeface
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.yml.charts.axis.AxisData
import co.yml.charts.common.extensions.formatToSinglePrecision
import co.yml.charts.common.model.Point
import co.yml.charts.common.utils.DataUtils
import co.yml.charts.ui.linechart.model.*
import co.yml.charts.ui.wavechart.WaveChart
import co.yml.charts.ui.wavechart.model.Wave
import co.yml.charts.ui.wavechart.model.WaveChartData
import co.yml.charts.ui.wavechart.model.WaveFillColor
import co.yml.charts.ui.wavechart.model.WavePlotData
import co.yml.ycharts.app.R
import co.yml.ycharts.app.ui.compositions.AppBarWithBackButton
import co.yml.ycharts.app.ui.theme.YChartsTheme
class WaveChartActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YChartsTheme {
Scaffold(modifier = Modifier.fillMaxSize(),
backgroundColor = YChartsTheme.colors.background,
topBar = {
AppBarWithBackButton(
stringResource(id = R.string.title_wave_chart),
onBackPressed = {
onBackPressed()
})
})
{
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Alignment.TopCenter
) {
LazyColumn(content = {
items(3) { item ->
when (item) {
0 -> {
Text(
modifier = Modifier.padding(12.dp),
text = getString(R.string.simple_wave_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
WaveGraph1(
DataUtils.getWaveChartData(
15.0,
50.0,
0.5
)
)
}
1 -> {
Text(
modifier=Modifier.padding(12.dp),
text = getString(R.string.sine_wave_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
WaveGraph2(
DataUtils.getWaveChartData(
15.0,
50.0,
0.2
)
)
}
2 -> {
Text(
modifier = Modifier.padding(12.dp),
text = getString(R.string.amplitude_wave_chart),
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
WaveGraph3(
DataUtils.getWaveChartData(
20.0,
5.0,
1.0
)
)
}
}
}
})
}
}
}
}
}
}
@Composable
private fun WaveGraph1(pointsData: List<Point>) {
val steps = 5
val xAxisData = AxisData.Builder()
.axisStepSize(30.dp)
.startDrawPadding(48.dp)
.steps(pointsData.size - 1)
.labelData { i -> i.toString() }
.labelAndAxisLinePadding(15.dp)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.labelAndAxisLinePadding(20.dp)
.labelData { i ->
// Add yMin to get the negative axis values to the scale
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}.build()
val data = WaveChartData(
wavePlotData = WavePlotData(
lines = listOf(
Wave(
dataPoints = pointsData,
waveStyle = LineStyle(color = Color.Black),
selectionHighlightPoint = SelectionHighlightPoint(),
shadowUnderLine = ShadowUnderLine(),
selectionHighlightPopUp = SelectionHighlightPopUp(),
waveFillColor = WaveFillColor(topColor = Color.Green, bottomColor = Color.Red),
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData,
gridLines = GridLines()
)
WaveChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
waveChartData = data
)
}
@Composable
private fun WaveGraph2(pointsData: List<Point>) {
val steps = 10
val xAxisData = AxisData.Builder()
.axisStepSize(40.dp)
.startDrawPadding(48.dp)
.steps(pointsData.size - 1)
.labelData { i -> (i).toString() }
.axisLabelAngle(20f)
.labelAndAxisLinePadding(15.dp)
.axisLabelColor(Color.Blue)
.axisLineColor(Color.DarkGray)
.typeFace(Typeface.DEFAULT_BOLD)
.build()
val yAxisData = AxisData.Builder()
.steps(10)
.labelData { i ->
// Add yMin to get the negative axis values to the scale
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}
.labelAndAxisLinePadding(30.dp)
.axisLabelColor(Color.Blue)
.axisLineColor(Color.DarkGray)
.typeFace(Typeface.DEFAULT_BOLD)
.build()
val data = WaveChartData(
wavePlotData = WavePlotData(
lines = listOf(
Wave(
dataPoints = pointsData,
waveStyle = LineStyle(lineType = LineType.SmoothCurve(), color = Color.Blue),
shadowUnderLine = ShadowUnderLine(alpha = 0.5f),
waveFillColor = WaveFillColor(
topColor = Color.Yellow,
bottomColor = Color.Magenta
),
selectionHighlightPopUp = SelectionHighlightPopUp(popUpLabel = { x, y ->
val xLabel = "x : ${(1900 + x).toInt()} "
val yLabel = "y : ${String.format("%.2f", y)}"
"$xLabel $yLabel"
})
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData
)
WaveChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
waveChartData = data
)
}
@Composable
private fun WaveGraph3(pointsData: List<Point>) {
val steps = 10
val xAxisData = AxisData.Builder()
.axisStepSize(40.dp)
.startDrawPadding(48.dp)
.steps(pointsData.size - 1)
.labelData { i -> i.toString() }
.labelAndAxisLinePadding(15.dp)
.axisLineColor(Color.Red)
.build()
val yAxisData = AxisData.Builder()
.steps(steps)
.labelData { i ->
val yMin = pointsData.minOf { it.y }
val yMax = pointsData.maxOf { it.y }
val yScale = (yMax - yMin) / steps
((i * yScale) + yMin).formatToSinglePrecision()
}
.axisLineColor(Color.Red)
.labelAndAxisLinePadding(20.dp)
.bottomPadding(15.dp)
.build()
val data = WaveChartData(
wavePlotData = WavePlotData(
lines = listOf(
Wave(
dataPoints = pointsData,
waveStyle = LineStyle(
lineType = LineType.Straight(isDotted = true),
color = Color.Green
),
shadowUnderLine = ShadowUnderLine(
brush = Brush.verticalGradient(
listOf(
Color.Green,
Color.Transparent
)
), alpha = 0.3f
),
selectionHighlightPoint = SelectionHighlightPoint(
color = Color.Green
),
selectionHighlightPopUp = SelectionHighlightPopUp(
backgroundColor = Color.Black,
backgroundStyle = Stroke(2f),
labelColor = Color.Red,
labelTypeface = Typeface.DEFAULT_BOLD
),
waveFillColor = WaveFillColor(
topColor = Color.Green,
bottomColor = Color.Red,
topBrush = Brush.verticalGradient(
listOf(
Color.Green,
Color.Blue
)
),
bottomBrush = Brush.verticalGradient(
listOf(
Color.Green,
Color.Red
)
)
)
)
)
),
xAxisData = xAxisData,
yAxisData = yAxisData
)
WaveChart(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
waveChartData = data
)
}
@@ -0,0 +1,60 @@
package co.yml.ycharts.app.ui.compositions
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import co.yml.ycharts.app.R
import co.yml.ycharts.app.ui.theme.YChartsTheme
@Composable
fun AppBarWithBackButton(title: String, onBackPressed: () -> Unit) {
TopAppBar(
modifier = Modifier
.fillMaxWidth()
.height(60.dp),
backgroundColor = YChartsTheme.colors.button,
elevation = 6.dp,
content = {
Box(
modifier = Modifier
.fillMaxWidth()
) {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Spacer(modifier = Modifier.width(5.dp))
IconButton(
onClick = { onBackPressed() },
modifier = Modifier
.size(32.dp)
) {
Icon(
painter = painterResource(id = R.drawable.ic_back_arrow),
contentDescription = "Back",
tint = Color.Unspecified
)
}
}
Text(
modifier = Modifier.fillMaxWidth(),
text = title,
color = YChartsTheme.colors.text,
textAlign = TextAlign.Center,
style = YChartsTheme.typography.header
)
}
}
)
}
@@ -0,0 +1,52 @@
package co.yml.ycharts.app.ui.theme
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
class YChartsColors(
background: Color,
primary: Color,
text: Color,
button: Color
) {
var background by mutableStateOf(background)
private set
var primary by mutableStateOf(primary)
private set
var text by mutableStateOf(text)
private set
var button by mutableStateOf(button)
private set
}
fun lightColors(
background: Color = Color.White,
primary: Color = Color.White,
text: Color = Color.White,
button: Color = DarkGrey
): YChartsColors = YChartsColors(
background,
primary,
text,
button
)
fun darkColors(
background: Color = DarkGrey,
primary: Color = DarkGrey,
text: Color = DarkGrey,
button: Color = Color.White
): YChartsColors = YChartsColors(
background,
primary,
text,
button
)
val DarkGrey = Color(0xFF0F0F0F)
val LocalColors = staticCompositionLocalOf { lightColors() }
@@ -0,0 +1,13 @@
package co.yml.ycharts.app.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.dp
data class YChartsShapes(
val small: RoundedCornerShape = RoundedCornerShape(4.dp),
val medium: RoundedCornerShape = RoundedCornerShape(8.dp),
val large: RoundedCornerShape = RoundedCornerShape(0.dp)
)
internal val LocalShapes = staticCompositionLocalOf { YChartsShapes() }
@@ -0,0 +1,56 @@
package co.yml.ycharts.app.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.Color
private val DarkColorPalette = darkColors(
background = DarkGrey,
primary = DarkGrey,
text = DarkGrey,
button = Color.White
)
private val LightColorPalette = lightColors(
background = Color.White,
primary = Color.White,
text = Color.White,
button = DarkGrey
)
object YChartsTheme {
val colors: YChartsColors
@Composable
@ReadOnlyComposable
get() = LocalColors.current
val typography: YChartsTypography
@Composable
@ReadOnlyComposable
get() = LocalTypography.current
val shapes: YChartsShapes
@Composable
@ReadOnlyComposable
get() = LocalShapes.current
}
@Composable
fun YChartsTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
typography: YChartsTypography = YChartsTheme.typography,
shapes: YChartsShapes = YChartsTheme.shapes,
content: @Composable () -> Unit
) {
val colors = if (darkTheme) DarkColorPalette else LightColorPalette
CompositionLocalProvider(
LocalColors provides colors,
LocalShapes provides shapes,
LocalTypography provides typography
) {
content()
}
}
@@ -0,0 +1,21 @@
package co.yml.ycharts.app.ui.theme
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
data class YChartsTypography(
val header: TextStyle = TextStyle(
fontWeight = FontWeight.Medium,
fontSize = 24.sp,
letterSpacing = 0.15.sp
),
val button: TextStyle = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
letterSpacing = 0.25.sp
)
)
internal val LocalTypography = staticCompositionLocalOf { YChartsTypography() }
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="0%p"
android:toXDelta="-100%p">
</translate>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="100%p"
android:toXDelta="0%p">
</translate>
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffff"
android:pathData="M16.62,2.99c-0.49,-0.49 -1.28,-0.49 -1.77,0L6.54,11.3c-0.39,0.39 -0.39,1.02 0,1.41l8.31,8.31c0.49,0.49 1.28,0.49 1.77,0s0.49,-1.28 0,-1.77L9.38,12l7.25,-7.25c0.48,-0.48 0.48,-1.28 -0.01,-1.76z"/>
</vector>
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="dark_grey">#242222</color>
<color name="white">#FFFFFFFF</color>
</resources>
@@ -0,0 +1,41 @@
<resources>
<string name="app_name">YCharts</string>
<string name="title_activity_bar_chart">BarChartActivity</string>
<string name="title_activity_line_chart">LineChartActivity</string>
<string name="title_activity_wave_chart">WaveChartActivity</string>
<string name="title_activity_pie_chart">PieChartActivity</string>
<string name="title_activity_donut_chart">DonutChartActivity</string>
<string name="title_activity_combined_line_bar_chart">CombinedLineAndBarChartActivity</string>
<string name="title_bar_chart">Bar Chart</string>
<string name="title_line_chart">Line Chart</string>
<string name="title_wave_chart">Wave Chart</string>
<string name="title_pie_chart">Pie Chart</string>
<string name="title_donut_chart">Donut Chart</string>
<string name="title_bar_with_line_chart">Combined Chart</string>
<string name="linechart_default_style">Single Line chart with default style and grid</string>
<string name="linechart_straight_line_style">Single Linechart with Straight-Line style</string>
<string name="linechart_dotted_style"> Dotted Single Linechart with gradient background</string>
<string name="linechart_multiple_tones">Multicolor line-chart</string>
<string name="combined_line_chart">Linechart with combination Multiple lines with different styles</string>
<string name="barchart_solid_colors">Simple Barchart with solid bars</string>
<string name="barchart_gradient_colors">Barchart with gradient bars</string>
<string name="barchart_background_color">Barchart with background color</string>
<string name="combined_bar_line_chart">A Combined chart with Barchart and Linechart</string>
<string name="combined_chart_with_background">A Combined-chart with background</string>
<string name="simple_donut_chart">A Simple Donutchart</string>
<string name="donut_chart_with_background">A Donutchart with a background color</string>
<string name="multiple_donuts">Multiple Donutcharts</string>
<string name="simple_piechart">Simple Piechart</string>
<string name="piechart_with_lables">"Piechart with slice-labels "</string>
<string name="horizontal_bar_chart">Horizontal Barchart</string>
<string name="grouped_bar_chart">Grouped Barchart</string>
<string name="stacked_barchart">Stacked Barchart</string>
<string name="simple_wave_chart">Simple Wave Chart</string>
<string name="sine_wave_chart">Sine-Wave Chart</string>
<string name="amplitude_wave_chart">Amplitude Wave Chart</string>
<string name="title_activity_bubble_chart">Bubble Chart</string>
<string name="bubble_chart">Bubblechart</string>
<string name="gradient_bubble_chart">Gradient Bubblechart</string>
<string name="solid_bubble_chart">Solid Bubble chart</string>
</resources>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.YCharts" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">@color/dark_grey</item>
</style>
</resources>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>
@@ -0,0 +1,17 @@
package co.yml.ycharts.app
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)
}
}