Skip to content

Commit 73a91ef

Browse files
committed
feat: add custom animator support (Roadmap #4)
- Enabled custom Interpolator support in ShineButton and ShineView - Added custom FiniteAnimationSpec support in ShineButtonCompose - Updated README.md and versioned to 0.6.0 - Updated CHANGELOG.md and marked Roadmap #4 as complete
1 parent ddee653 commit 73a91ef

7 files changed

Lines changed: 39 additions & 29 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.6.0] - 2026-06-13
6+
7+
### Added
8+
- **Custom Animators Support**: Introduced ability to set custom `Interpolator` in View version and `AnimationSpec` in Compose version.
9+
- Added `setInterpolator(Interpolator)` method to `ShineButton`.
10+
- Exposed `shineAnimationSpec` and `scaleAnimationSpec` in `ShineButtonCompose`.
11+
12+
### Changed
13+
- Bumped version to 0.6.0.
14+
515
## [0.5.0] - 2026-06-13
616

717
### Added

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ allprojects {
3838

3939
```gradle
4040
dependencies {
41-
implementation 'com.github.ChadCSong:ShineButton:v0.4.1'
41+
implementation 'com.github.ChadCSong:ShineButton:v0.6.0'
4242
}
4343
```
4444

@@ -48,7 +48,7 @@ Add the dependency to your `app/build.gradle` file:
4848

4949
```gradle
5050
dependencies {
51-
implementation 'com.sackcentury:shinebutton:0.4.1'
51+
implementation 'com.sackcentury:shinebutton:0.6.0'
5252
}
5353
```
5454

@@ -58,7 +58,7 @@ dependencies {
5858
<dependency>
5959
<groupId>com.sackcentury</groupId>
6060
<artifactId>shinebutton</artifactId>
61-
<version>0.4.1</version>
61+
<version>0.6.0</version>
6262
<type>aar</type>
6363
</dependency>
6464
```

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
1818
# org.gradle.parallel=true
1919
org.gradle.jvmargs=-Xmx1536M
20-
VERSION_NAME=0.5.0
21-
VERSION_CODE=15
20+
VERSION_NAME=0.6.0
21+
VERSION_CODE=16
2222
POM_GROUP_ID=com.sackcentury
2323
POM_DESCRIPTION=This is a UI lib for Android. Effects like shining.
2424
POM_URL=https://github.com/ChadCSong/ShineButton

shinebuttonlib/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ android {
88
defaultConfig {
99
minSdkVersion 21
1010
targetSdkVersion 34
11-
versionCode 15
12-
versionName "0.5.0"
11+
versionCode 16
12+
versionName "0.6.0"
1313

1414
}
1515

shinebuttonlib/src/main/java/com/sackcentury/shinebuttonlib/ShineButton.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import android.util.Log
1414
import android.view.View
1515
import android.view.ViewGroup
1616
import android.view.Window
17+
import android.view.animation.Interpolator
1718
import android.view.animation.LinearInterpolator
1819
import androidx.core.content.res.ResourcesCompat
1920
import com.sackcentury.shinebuttonlib.listener.SimpleAnimatorListener
@@ -250,6 +251,10 @@ class ShineButton : PorterShapeImageView {
250251
shineParams.shineSize = size
251252
}
252253

254+
fun setInterpolator(interpolator: Interpolator) {
255+
shineParams.customInterpolator = interpolator
256+
}
257+
253258
override fun setOnClickListener(l: OnClickListener?) {
254259
if (l is OnButtonClickListener) {
255260
super.setOnClickListener(l)

shinebuttonlib/src/main/java/com/sackcentury/shinebuttonlib/ShineButtonCompose.kt

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,30 @@ import androidx.compose.runtime.*
1010
import androidx.compose.ui.Alignment
1111
import androidx.compose.ui.Modifier
1212
import androidx.compose.ui.geometry.Offset
13-
import androidx.compose.ui.geometry.Size
1413
import androidx.compose.ui.graphics.Color
15-
import androidx.compose.ui.graphics.ImageBitmap
16-
import androidx.compose.ui.graphics.Paint
17-
import androidx.compose.ui.graphics.PaintingStyle
18-
import androidx.compose.ui.graphics.drawscope.Stroke
19-
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
20-
import androidx.compose.ui.graphics.nativeCanvas
21-
import androidx.compose.ui.graphics.painter.Painter
22-
import androidx.compose.ui.res.imageResource
14+
import androidx.compose.ui.graphics.graphicsLayer
15+
import androidx.compose.ui.graphics.vector.ImageVector
16+
import androidx.compose.ui.graphics.vector.rememberVectorPainter
2317
import androidx.compose.ui.unit.Dp
2418
import androidx.compose.ui.unit.dp
2519
import kotlin.math.PI
2620
import kotlin.math.cos
2721
import kotlin.math.sin
2822

29-
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
30-
import androidx.compose.ui.graphics.nativeCanvas
31-
import androidx.compose.ui.graphics.vector.ImageVector
32-
import androidx.compose.ui.graphics.vector.rememberVectorPainter
33-
import androidx.compose.ui.graphics.graphicsLayer
34-
import androidx.compose.ui.graphics.drawscope.translate
35-
3623
/**
3724
* A native Jetpack Compose implementation of the ShineButton.
3825
*
3926
* @param isChecked The current checked state.
4027
* @param onCheckedChange Callback when the state changes.
41-
* @param modifier Modifier for the button.
4228
* @param shape The Vector image to use for the button icon (e.g., Icons.Default.Favorite).
29+
* @param modifier Modifier for the button.
4330
* @param btnColor Base color of the button (unchecked).
4431
* @param btnFillColor Color when the button is checked.
4532
* @param shineColor Primary color for shine particles.
4633
* @param shineSize Size of the button.
4734
* @param shineCount Number of shine particles.
35+
* @param shineAnimationSpec Custom finite animation spec for the shine particle effect.
36+
* @param scaleAnimationSpec Custom finite animation spec for the icon scale effect.
4837
*/
4938
@Composable
5039
fun ShineButtonCompose(
@@ -58,7 +47,9 @@ fun ShineButtonCompose(
5847
shineSize: Dp = 50.dp,
5948
shineCount: Int = 8,
6049
animDuration: Int = 1000,
61-
allowRandomColor: Boolean = false
50+
allowRandomColor: Boolean = false,
51+
shineAnimationSpec: FiniteAnimationSpec<Float>? = null,
52+
scaleAnimationSpec: FiniteAnimationSpec<Float>? = null
6253
) {
6354
val interactionSource = remember { MutableInteractionSource() }
6455
val painter = rememberVectorPainter(image = shape)
@@ -69,7 +60,7 @@ fun ShineButtonCompose(
6960
val shineProgress by transition.animateFloat(
7061
transitionSpec = {
7162
if (targetState) {
72-
tween(durationMillis = animDuration, easing = LinearOutSlowInEasing)
63+
shineAnimationSpec ?: tween(durationMillis = animDuration, easing = LinearOutSlowInEasing)
7364
} else {
7465
tween(durationMillis = 0)
7566
}
@@ -82,7 +73,7 @@ fun ShineButtonCompose(
8273
val scale by transition.animateFloat(
8374
transitionSpec = {
8475
if (targetState) {
85-
keyframes {
76+
scaleAnimationSpec ?: keyframes {
8677
durationMillis = 500
8778
0.4f at 0
8879
1.1f at 250
@@ -95,7 +86,6 @@ fun ShineButtonCompose(
9586
label = "Scale"
9687
) { checked ->
9788
// To satisfy Compose's UnusedTransitionTargetStateParameter lint, we must read the state parameter.
98-
// The scale naturally returns to 1.0f in both checked/unchecked idle states.
9989
if (checked) 1.0f else 1.0f
10090
}
10191

shinebuttonlib/src/main/java/com/sackcentury/shinebuttonlib/ShineView.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import android.graphics.RectF
1010
import android.util.AttributeSet
1111
import android.view.View
1212
import android.view.ViewGroup
13+
import android.view.animation.Interpolator
1314
import com.sackcentury.shinebuttonlib.interpolator.QuartOutInterpolator
1415
import com.sackcentury.shinebuttonlib.listener.SimpleAnimatorListener
1516
import java.util.*
@@ -68,6 +69,7 @@ class ShineView : View {
6869
private var shineSize = 0
6970
private var allowRandomColor = false
7071
private var enableFlashing = false
72+
private var customInterpolator: Interpolator? = null
7173

7274
private val rectF = RectF()
7375
private val rectFSmall = RectF()
@@ -95,6 +97,7 @@ class ShineView : View {
9597
constructor(context: Context, shineButton: ShineButton, shineParams: ShineParams) : super(context) {
9698
initShineParams(shineParams, shineButton)
9799
this.shineAnimator = ShineAnimator(animDuration, shineDistanceMultiple, clickAnimDuration)
100+
this.shineAnimator?.interpolator = customInterpolator ?: QuartOutInterpolator()
98101
this.shineButton = shineButton
99102

100103
// Main shine particles paint
@@ -123,7 +126,7 @@ class ShineView : View {
123126
// Click splash animation (expanding circle)
124127
clickAnimator = ValueAnimator.ofFloat(0f, 1.1f).apply {
125128
duration = clickAnimDuration
126-
interpolator = QuartOutInterpolator()
129+
interpolator = customInterpolator ?: QuartOutInterpolator()
127130
addUpdateListener { valueAnimator ->
128131
clickValue = valueAnimator.animatedValue as Float
129132
invalidate()
@@ -255,6 +258,7 @@ class ShineView : View {
255258
var smallShineOffsetAngle = 20f
256259
var smallShineColor = 0
257260
var shineSize = 0
261+
var customInterpolator: Interpolator? = null
258262
}
259263

260264
/**
@@ -272,6 +276,7 @@ class ShineView : View {
272276
smallShineColor = shineParams.smallShineColor
273277
bigShineColor = shineParams.bigShineColor
274278
shineSize = shineParams.shineSize
279+
customInterpolator = shineParams.customInterpolator
275280

276281
if (smallShineColor == 0) {
277282
smallShineColor = colorRandom[6]

0 commit comments

Comments
 (0)