的
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
+64
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
+17
@@ -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
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user