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
+29
View File
@@ -0,0 +1,29 @@
plugins {
id("com.android.application")
kotlin("android")
}
apply(from = rootProject.file("gradle/configure-android.gradle"))
apply(from = rootProject.file("gradle/configure-compose.gradle"))
dependencies {
implementation(Kotlin.stdLib)
implementation(Android.appcompat)
implementation(Android.activityCompose)
implementation(Compose.core)
implementation(Compose.layout)
implementation(Compose.material)
implementation(Compose.materialIconsExt)
implementation(Compose.foundation)
implementation(Compose.runtime)
// Previews weren't working when using debugImplementation
implementation(Compose.tooling)
implementation(project(":lib:pie"))
implementation(project(":lib:bar"))
implementation(project(":lib:line"))
}
android {
namespace = "com.github.tehras.charts"
}
+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,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Charts">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,16 @@
package com.github.tehras.charts
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import com.github.tehras.charts.ui.ChartsApp
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.setContent {
ChartsApp()
}
}
}
@@ -0,0 +1,9 @@
package com.github.tehras.charts.theme
import androidx.compose.ui.graphics.Color
val Red200 = Color(0xfff297a2)
val Red300 = Color(0xffea6d7e)
val Red700 = Color(0xffdd0d3c)
val Red800 = Color(0xffd00036)
val Red900 = Color(0xffc20029)
@@ -0,0 +1,11 @@
package com.github.tehras.charts.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(8.dp)
)
@@ -0,0 +1,10 @@
package com.github.tehras.charts.theme
import androidx.compose.ui.unit.dp
object Margins {
val horizontal = 12.dp
val horizontalLarge = 24.dp
val vertical = 8.dp
val verticalLarge = 16.dp
}
@@ -0,0 +1,39 @@
package com.github.tehras.charts.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
private val LightThemeColors = lightColors(
primary = Red700,
primaryVariant = Red900,
onPrimary = Color.White,
secondary = Red700,
secondaryVariant = Red900,
onSecondary = Color.White,
error = Red800
)
private val DarkThemeColors = darkColors(
primary = Red300,
primaryVariant = Red700,
onPrimary = Color.Black,
secondary = Red300,
onSecondary = Color.White,
error = Red200
)
@Composable
fun ChartsTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
colors = if (darkTheme) DarkThemeColors else LightThemeColors,
shapes = shapes,
content = content
)
}
@@ -0,0 +1,25 @@
package com.github.tehras.charts.ui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
sealed class ChartScreen {
object SelectChart : ChartScreen()
object Pie : ChartScreen()
object Bar : ChartScreen()
object Line : ChartScreen()
}
object ChartScreenStatus {
var currentChart by mutableStateOf<ChartScreen>(ChartScreen.SelectChart)
private set
fun navigateTo(screen: ChartScreen) {
currentChart = screen
}
fun navigateHome() {
navigateTo(ChartScreen.SelectChart)
}
}
@@ -0,0 +1,32 @@
package com.github.tehras.charts.ui
import androidx.compose.animation.Crossfade
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import com.github.tehras.charts.theme.ChartsTheme
import com.github.tehras.charts.ui.bar.BarChartScreen
import com.github.tehras.charts.ui.line.LineChartScreen
import com.github.tehras.charts.ui.pie.PieChartScreen
import com.github.tehras.charts.ui.selector.SelectChartScreen
@Composable
fun ChartsApp() {
ChartsTheme {
AppContent()
}
}
@Composable
fun AppContent() {
Crossfade(ChartScreenStatus.currentChart) { screen ->
Surface(color = MaterialTheme.colors.background) {
when (screen) {
ChartScreen.SelectChart -> SelectChartScreen()
ChartScreen.Pie -> PieChartScreen()
ChartScreen.Bar -> BarChartScreen()
ChartScreen.Line -> LineChartScreen()
}
}
}
}
@@ -0,0 +1,105 @@
package com.github.tehras.charts.ui.bar
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion.Black
import androidx.compose.ui.graphics.Color.Companion.White
import com.github.tehras.charts.bar.BarChartData
import com.github.tehras.charts.bar.BarChartData.Bar
import com.github.tehras.charts.bar.renderer.label.SimpleValueDrawer
import com.github.tehras.charts.bar.renderer.label.SimpleValueDrawer.DrawLocation
import com.github.tehras.charts.bar.renderer.label.SimpleValueDrawer.DrawLocation.Inside
import com.github.tehras.charts.bar.renderer.label.SimpleValueDrawer.DrawLocation.Outside
import com.github.tehras.charts.bar.renderer.label.SimpleValueDrawer.DrawLocation.XAxis
class BarChartDataModel {
private var colors = mutableListOf(
Color(0XFFF44336),
Color(0XFFE91E63),
Color(0XFF9C27B0),
Color(0XFF673AB7),
Color(0XFF3F51B5),
Color(0XFF03A9F4),
Color(0XFF009688),
Color(0XFFCDDC39),
Color(0XFFFFC107),
Color(0XFFFF5722),
Color(0XFF795548),
Color(0XFF9E9E9E),
Color(0XFF607D8B)
)
var labelDrawer: SimpleValueDrawer by mutableStateOf(SimpleValueDrawer(drawLocation = Inside))
private set
var barChartData by mutableStateOf(
BarChartData(
bars = listOf(
Bar(
label = "Bar1",
value = randomValue(),
color = randomColor()
),
Bar(
label = "Bar2",
value = randomValue(),
color = randomColor()
),
Bar(
label = "Bar3",
value = randomValue(),
color = randomColor()
)
)
)
)
val bars: List<Bar>
get() = barChartData.bars
var labelLocation: DrawLocation = Inside
set(value) {
val color = when (value) {
Inside -> White
Outside, XAxis -> Black
}
labelDrawer = SimpleValueDrawer(
drawLocation = value,
labelTextColor = color
)
field = value
}
fun addBar() {
barChartData = barChartData
.copy(bars = bars.toMutableList().apply {
add(
Bar(
label = "Bar${bars.size + 1}",
value = randomValue(),
color = randomColor()
)
)
})
}
fun removeBar() {
// Remove last.
barChartData = barChartData.copy(bars = bars.toMutableList().apply {
val lastBar = bars[bars.size - 1]
colors.add(lastBar.color)
remove(lastBar)
})
}
private fun randomValue(): Float = (100 * Math.random() + 25).toFloat()
private fun randomColor(): Color {
val randomIndex = (Math.random() * colors.size).toInt()
val color = colors[randomIndex]
colors.removeAt(randomIndex)
return color
}
}
@@ -0,0 +1,144 @@
package com.github.tehras.charts.ui.bar
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Remove
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.github.tehras.charts.bar.BarChart
import com.github.tehras.charts.bar.renderer.label.SimpleValueDrawer.DrawLocation
import com.github.tehras.charts.theme.Margins
import com.github.tehras.charts.ui.ChartScreenStatus
@Composable
fun BarChartScreen() {
Scaffold(
topBar = {
TopAppBar(
navigationIcon = {
IconButton(onClick = { ChartScreenStatus.navigateHome() }) {
Icon(Icons.Filled.ArrowBack, contentDescription = "Go back to home")
}
},
title = { Text(text = "Bar Chart") }
)
},
) { BarChartScreenContent() }
}
@Composable
private fun BarChartScreenContent() {
val barChartDataModel = BarChartDataModel()
Column(
modifier = Modifier.padding(
horizontal = Margins.horizontal,
vertical = Margins.vertical
)
) {
BarChartRow(barChartDataModel)
DrawValueLocation(barChartDataModel) {
barChartDataModel.labelLocation = it
}
AddOrRemoveBar(barChartDataModel)
}
}
@Composable
fun DrawValueLocation(
barChartDataModel: BarChartDataModel,
newLocation: (DrawLocation) -> Unit
) {
val selectedAlignment = remember(barChartDataModel.labelDrawer) {
barChartDataModel.labelLocation
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = Margins.verticalLarge),
verticalAlignment = CenterVertically
) {
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = Margins.horizontal, vertical = Margins.vertical)
.align(CenterVertically)
) {
DrawLocation.values().forEach { location ->
OutlinedButton(
border = ButtonDefaults.outlinedBorder.takeIf { selectedAlignment == location },
onClick = { newLocation(location) }
) {
Text(location.name)
}
}
}
}
}
@Composable
fun AddOrRemoveBar(barChartDataModel: BarChartDataModel) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = Margins.vertical),
verticalAlignment = CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Button(
enabled = barChartDataModel.bars.size > 2,
onClick = { barChartDataModel.removeBar() },
shape = CircleShape
) { Icon(Icons.Filled.Remove, contentDescription = "Remove bar from chart") }
Row(
modifier = Modifier.padding(horizontal = Margins.horizontal),
verticalAlignment = CenterVertically
) {
Text(text = "Bars: ")
Text(
text = barChartDataModel.bars.count().toString(),
style = TextStyle(
fontWeight = FontWeight.ExtraBold,
fontSize = 18.sp
)
)
}
Button(
enabled = barChartDataModel.bars.size < 6,
onClick = { barChartDataModel.addBar() },
shape = CircleShape
) { Icon(Icons.Filled.Add, contentDescription = "Add bar to chart") }
}
}
@Composable
private fun BarChartRow(barChartDataModel: BarChartDataModel) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(250.dp)
.padding(vertical = Margins.verticalLarge)
) {
BarChart(
barChartData = barChartDataModel.barChartData,
labelDrawer = barChartDataModel.labelDrawer
)
}
}
@Preview
@Composable
fun BarChartPreview() = BarChartScreen()
@@ -0,0 +1,65 @@
package com.github.tehras.charts.ui.line
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import com.github.tehras.charts.line.LineChartData
import com.github.tehras.charts.line.LineChartData.Point
import com.github.tehras.charts.line.renderer.line.SolidLineDrawer
import com.github.tehras.charts.line.renderer.point.FilledCircularPointDrawer
import com.github.tehras.charts.line.renderer.point.HollowCircularPointDrawer
import com.github.tehras.charts.line.renderer.point.NoPointDrawer
import com.github.tehras.charts.line.renderer.point.PointDrawer
import com.github.tehras.charts.ui.line.LineChartDataModel.PointDrawerType.Filled
import com.github.tehras.charts.ui.line.LineChartDataModel.PointDrawerType.Hollow
import com.github.tehras.charts.ui.line.LineChartDataModel.PointDrawerType.None
class LineChartDataModel {
var lineChartData by mutableStateOf(
LineChartData(
points = listOf(
Point(randomYValue(), "Label1"),
Point(randomYValue(), "Label2"),
Point(randomYValue(), "Label3"),
Point(randomYValue(), "Label4"),
Point(randomYValue(), "Label5"),
Point(randomYValue(), "Label6"),
Point(randomYValue(), "Label7")
),
lineDrawer = SolidLineDrawer(),
)
)
var lineChartData2 by mutableStateOf(
LineChartData(
points = listOf(
Point(randomYValue(), "Label1"),
Point(randomYValue(), "Label2"),
Point(randomYValue(), "Label3"),
),
lineDrawer = SolidLineDrawer(
color = Color(0xFF00FF00)
)
)
)
var horizontalOffset by mutableStateOf(5f)
var pointDrawerType by mutableStateOf(Filled)
val pointDrawer: PointDrawer
get() {
return when (pointDrawerType) {
None -> NoPointDrawer
Filled -> FilledCircularPointDrawer()
Hollow -> HollowCircularPointDrawer()
}
}
private fun randomYValue(): Float = (100f * Math.random()).toFloat() + 45f
enum class PointDrawerType {
None,
Filled,
Hollow
}
}
@@ -0,0 +1,125 @@
package com.github.tehras.charts.ui.line
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.github.tehras.charts.line.LineChart
import com.github.tehras.charts.theme.Margins.horizontal
import com.github.tehras.charts.theme.Margins.vertical
import com.github.tehras.charts.theme.Margins.verticalLarge
import com.github.tehras.charts.ui.ChartScreenStatus
import com.github.tehras.charts.ui.line.LineChartDataModel.PointDrawerType
@Composable
fun LineChartScreen() {
Scaffold(
topBar = {
TopAppBar(
navigationIcon = {
IconButton(onClick = { ChartScreenStatus.navigateHome() }) {
Icon(Icons.Filled.ArrowBack, contentDescription = "Go back to home")
}
},
title = { Text(text = "Line Chart") }
)
},
) { LineChartScreenContent() }
}
@Composable
fun LineChartScreenContent() {
val lineChartDataModel = LineChartDataModel()
Column(
modifier = Modifier.padding(
horizontal = horizontal,
vertical = vertical
)
) {
LineChartRow(lineChartDataModel)
HorizontalOffsetSelector(lineChartDataModel)
OffsetProgress(lineChartDataModel)
}
}
@Composable
fun HorizontalOffsetSelector(lineChartDataModel: LineChartDataModel) {
val selectedType = lineChartDataModel.pointDrawerType
Row(
modifier = Modifier.padding(
horizontal = horizontal,
vertical = verticalLarge
),
verticalAlignment = CenterVertically
) {
Text("Point Drawer")
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = horizontal, vertical = vertical)
.align(CenterVertically)
) {
PointDrawerType.values().forEach { type ->
OutlinedButton(
border = ButtonDefaults.outlinedBorder.takeIf { selectedType == type },
onClick = { lineChartDataModel.pointDrawerType = type }
) {
Text(type.name)
}
}
}
}
}
@Composable
fun OffsetProgress(lineChartDataModel: LineChartDataModel) {
Row(
modifier = Modifier.padding(horizontal = horizontal),
verticalAlignment = CenterVertically
) {
Text("Offset")
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxWidth()
.align(CenterVertically)
) {
Slider(
value = lineChartDataModel.horizontalOffset,
onValueChange = { lineChartDataModel.horizontalOffset = it },
valueRange = 0f.rangeTo(25f)
)
}
}
}
@Composable
fun LineChartRow(lineChartDataModel: LineChartDataModel) {
Box(
modifier = Modifier
.height(250.dp)
.fillMaxWidth()
) {
LineChart(
linesChartData = listOf(lineChartDataModel.lineChartData, lineChartDataModel.lineChartData2),
horizontalOffset = lineChartDataModel.horizontalOffset,
pointDrawer = lineChartDataModel.pointDrawer,
)
}
}
@Preview
@Composable
fun LineChartPreview() = LineChartScreen()
@@ -0,0 +1,77 @@
package com.github.tehras.charts.ui.pie
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import com.github.tehras.charts.piechart.PieChartData
class PieChartDataModel {
private var colors = mutableListOf(
Color(0XFFF44336),
Color(0XFFE91E63),
Color(0XFF9C27B0),
Color(0XFF673AB7),
Color(0XFF3F51B5),
Color(0XFF03A9F4),
Color(0XFF009688),
Color(0XFFCDDC39),
Color(0XFFFFC107),
Color(0XFFFF5722),
Color(0XFF795548),
Color(0XFF9E9E9E),
Color(0XFF607D8B)
)
var sliceThickness by mutableStateOf(25f)
var pieChartData by mutableStateOf(
PieChartData(
slices = listOf(
PieChartData.Slice(
randomLength(),
randomColor()
),
PieChartData.Slice(
randomLength(),
randomColor()
),
PieChartData.Slice(
randomLength(),
randomColor()
)
)
)
)
val slices
get() = pieChartData.slices
fun addSlice() {
pieChartData = pieChartData.copy(
slices = slices.toMutableList().apply {
add(PieChartData.Slice(randomLength(), randomColor()))
}.toList()
)
}
fun removeSlice() {
pieChartData = pieChartData.copy(
slices = slices.toMutableList().apply {
val lastSlice = slices[slices.size - 1]
colors.add(lastSlice.color)
remove(lastSlice)
}.toList()
)
}
private fun randomColor(): Color {
val randomIndex = (Math.random() * colors.size).toInt()
val color = colors[randomIndex]
colors.removeAt(randomIndex)
return color
}
private fun randomLength(): Float = (20 * Math.random() + 10).toFloat()
}
@@ -0,0 +1,149 @@
package com.github.tehras.charts.ui.pie
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.Slider
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Remove
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.github.tehras.charts.piechart.PieChart
import com.github.tehras.charts.piechart.renderer.SimpleSliceDrawer
import com.github.tehras.charts.theme.Margins
import com.github.tehras.charts.ui.ChartScreenStatus
@Composable
fun PieChartScreen() {
Scaffold(
topBar = {
TopAppBar(
navigationIcon = {
IconButton(onClick = { ChartScreenStatus.navigateHome() }) {
Icon(Icons.Filled.ArrowBack, contentDescription = "Go back to home")
}
},
title = { Text(text = "Pie Chart") }
)
},
) { PieChartScreenContent() }
}
@Composable
private fun PieChartScreenContent() {
val pieChartDataModel = remember { PieChartDataModel() }
Column(
modifier = Modifier.padding(
horizontal = Margins.horizontal,
vertical = Margins.vertical
)
) {
PieChartRow(pieChartDataModel)
SliceThicknessRow(pieChartDataModel.sliceThickness) {
pieChartDataModel.sliceThickness = it
}
AddOrRemoveSliceRow(pieChartDataModel)
}
}
@Composable
private fun PieChartRow(pieChartDataModel: PieChartDataModel) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.padding(vertical = Margins.vertical)
) {
PieChart(
pieChartData = pieChartDataModel.pieChartData,
sliceDrawer = SimpleSliceDrawer(
sliceThickness = pieChartDataModel.sliceThickness
)
)
}
}
@Composable
private fun SliceThicknessRow(sliceThickness: Float, onValueUpdated: (Float) -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = Margins.verticalLarge),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Slice thickness",
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(end = Margins.horizontal)
)
Slider(
value = sliceThickness,
onValueChange = { onValueUpdated(it) },
valueRange = 10f.rangeTo(100f)
)
}
}
@Composable
private fun AddOrRemoveSliceRow(pieChartDataModel: PieChartDataModel) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = Margins.vertical),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Button(
enabled = pieChartDataModel.slices.size > 3,
onClick = { pieChartDataModel.removeSlice() },
shape = CircleShape
) {
Icon(Icons.Filled.Remove, contentDescription = "Remove slice from pie chart")
}
Row(
modifier = Modifier.padding(horizontal = Margins.horizontal),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = "Slices: ")
Text(
text = pieChartDataModel.slices.count().toString(),
style = TextStyle(
fontWeight = FontWeight.ExtraBold,
fontSize = 18.sp
)
)
}
Button(
enabled = pieChartDataModel.slices.size < 9,
onClick = { pieChartDataModel.addSlice() },
shape = CircleShape
) {
Icon(Icons.Filled.Add, contentDescription = "Add slice to pie chart")
}
}
}
@Preview
@Composable
fun PieChartScreenPreview() = PieChartScreen()
@@ -0,0 +1,59 @@
package com.github.tehras.charts.ui.selector
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.github.tehras.charts.theme.Margins
import com.github.tehras.charts.ui.ChartScreen
import com.github.tehras.charts.ui.ChartScreen.Bar
import com.github.tehras.charts.ui.ChartScreen.Line
import com.github.tehras.charts.ui.ChartScreen.Pie
import com.github.tehras.charts.ui.ChartScreenStatus
@Composable
fun SelectChartScreen() {
Scaffold(
topBar = { TopAppBar(title = { Text(text = "Select Chart") }) }
) { SelectChartScreenContent() }
}
@Composable
private fun SelectChartScreenContent() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
ChartRow(text = "Pie Chart", navigateTo = Pie)
ChartRow(text = "Bar Chart", navigateTo = Bar)
ChartRow(text = "Line Chart", navigateTo = Line)
}
}
@Composable
private fun ChartRow(
text: String,
navigateTo: ChartScreen
) {
Row(modifier = Modifier.padding(horizontal = Margins.horizontal, vertical = Margins.vertical)) {
TextButton(
onClick = { ChartScreenStatus.navigateTo(navigateTo) }
) {
Text(text = text)
}
}
}
@Preview
@Composable
fun SelectChartScreenPreview() = SelectChartScreen()
@@ -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,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: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="status_bar">#0e0e0e</color>
</resources>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="red700">#dd0d3e</color>
<color name="red900">#c20029</color>
<color name="status_bar">@color/red900</color>
</resources>
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">charts</string>
</resources>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. In this Jetpack Compose sample app, these colors
don't affect the Composable functions, just the color of the Status Bar.
For the colors used by the Composable functions, see Theme.kt -->
<style name="Theme.Charts" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/red700</item>
<item name="colorPrimaryDark">@color/red900</item>
<item name="colorAccent">@color/red700</item>
<item name="android:statusBarColor">@color/status_bar</item>
</style>
</resources>