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
+27
View File
@@ -0,0 +1,27 @@
plugins {
id("com.android.library")
kotlin("android")
`maven-publish`
}
apply(from = rootProject.file("gradle/configure-android.gradle"))
apply(from = rootProject.file("gradle/configure-compose.gradle"))
apply(from = rootProject.file("gradle/jitpack-publish.gradle"))
dependencies {
api(project(":lib:common"))
implementation(Kotlin.stdLib)
implementation(Compose.animation)
implementation(Compose.core)
implementation(Compose.layout)
implementation(Compose.foundation)
implementation(Compose.runtime)
// Previews weren't working when using debugImplementation
implementation(Compose.tooling)
}
android {
namespace = "com.github.tehras.charts.piechart"
}
+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,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
@@ -0,0 +1,86 @@
package com.github.tehras.charts.piechart
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.tooling.preview.Preview
import com.github.tehras.charts.piechart.PieChartUtils.calculateAngle
import com.github.tehras.charts.piechart.animation.simpleChartAnimation
import com.github.tehras.charts.piechart.renderer.SimpleSliceDrawer
import com.github.tehras.charts.piechart.renderer.SliceDrawer
@Composable
fun PieChart(
pieChartData: PieChartData,
modifier: Modifier = Modifier,
animation: AnimationSpec<Float> = simpleChartAnimation(),
sliceDrawer: SliceDrawer = SimpleSliceDrawer()
) {
val transitionProgress = remember(pieChartData.slices) { Animatable(initialValue = 0f) }
// When slices value changes we want to re-animated the chart.
LaunchedEffect(pieChartData.slices) {
transitionProgress.animateTo(1f, animationSpec = animation)
}
DrawChart(
pieChartData = pieChartData,
modifier = modifier.fillMaxSize(),
progress = transitionProgress.value,
sliceDrawer = sliceDrawer
)
}
@Composable
private fun DrawChart(
pieChartData: PieChartData,
modifier: Modifier,
progress: Float,
sliceDrawer: SliceDrawer
) {
val slices = pieChartData.slices
Canvas(modifier = modifier) {
drawIntoCanvas {
var startArc = 0f
slices.forEach { slice ->
val arc = calculateAngle(
sliceLength = slice.value,
totalLength = pieChartData.totalSize,
progress = progress
)
sliceDrawer.drawSlice(
drawScope = this,
canvas = drawContext.canvas,
area = size,
startAngle = startArc,
sweepAngle = arc,
slice = slice
)
startArc += arc
}
}
}
}
@Preview
@Composable
fun PieChartPreview() = PieChart(
pieChartData = PieChartData(
slices = listOf(
PieChartData.Slice(25f, Color.Red),
PieChartData.Slice(42f, Color.Blue),
PieChartData.Slice(23f, Color.Green)
)
)
)
@@ -0,0 +1,19 @@
package com.github.tehras.charts.piechart
import androidx.compose.ui.graphics.Color
data class PieChartData(
val slices: List<Slice>
) {
internal val totalSize: Float
get() {
var total = 0f
slices.forEach { total += it.value }
return total
}
data class Slice(
val value: Float,
val color: Color
)
}
@@ -0,0 +1,11 @@
package com.github.tehras.charts.piechart
internal object PieChartUtils {
fun calculateAngle(
sliceLength: Float,
totalLength: Float,
progress: Float
): Float {
return 360.0f * (sliceLength * progress) / totalLength
}
}
@@ -0,0 +1,64 @@
package com.github.tehras.charts.piechart.renderer
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import com.github.tehras.charts.piechart.PieChartData.Slice
class SimpleSliceDrawer(private val sliceThickness: Float = 25f) : SliceDrawer {
init {
require(sliceThickness in 10f..100f) {
"Thickness of $sliceThickness must be between 10-100"
}
}
private val sectionPaint = Paint().apply {
isAntiAlias = true
style = PaintingStyle.Stroke
}
override fun drawSlice(
drawScope: DrawScope,
canvas: Canvas,
area: Size,
startAngle: Float,
sweepAngle: Float,
slice: Slice
) {
val sliceThickness = calculateSectorThickness(area = area)
val drawableArea = calculateDrawableArea(area = area)
canvas.drawArc(
rect = drawableArea,
paint = sectionPaint.apply {
color = slice.color
strokeWidth = sliceThickness
},
startAngle = startAngle,
sweepAngle = sweepAngle,
useCenter = false
)
}
private fun calculateSectorThickness(area: Size): Float {
val minSize = minOf(area.width, area.height)
return minSize * (sliceThickness / 200f)
}
private fun calculateDrawableArea(area: Size): Rect {
val sliceThicknessOffset =
calculateSectorThickness(area = area) / 2f
val offsetHorizontally = (area.width - area.height) / 2f
return Rect(
left = sliceThicknessOffset + offsetHorizontally,
top = sliceThicknessOffset,
right = area.width - sliceThicknessOffset - offsetHorizontally,
bottom = area.height - sliceThicknessOffset
)
}
}
@@ -0,0 +1,17 @@
package com.github.tehras.charts.piechart.renderer
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope
import com.github.tehras.charts.piechart.PieChartData.Slice
interface SliceDrawer {
fun drawSlice(
drawScope: DrawScope,
canvas: Canvas,
area: Size,
startAngle: Float,
sweepAngle: Float,
slice: Slice
)
}