() {
13 |
14 | /**
15 | * Returns the current instance of ColorHighlightBuilder for method chaining.
16 | */
17 | override val self: ColorHighlightBuilder get() = this
18 |
19 | init {
20 | // Disable alpha shimmer by default
21 | shimmer.alphaShimmer = false
22 | }
23 |
24 | /**
25 | * Sets the highlight color for the shimmer effect.
26 | *
27 | * @param color The color to be set as the highlight color.
28 | */
29 | private fun setHighlightColor(@ColorInt color: Int) = apply {
30 | shimmer.highlightColor = color
31 | }
32 |
33 | /**
34 | * Sets the base color for the shimmer effect.
35 | * This method modifies the base color while keeping the alpha channel intact.
36 | *
37 | * @param color The color to be set as the base color.
38 | */
39 | private fun setBaseColor(@ColorInt color: Int) = apply {
40 | shimmer.baseColor = (shimmer.baseColor and -0x1000000) or (color and 0x00FFFFFF)
41 | }
42 |
43 | /**
44 | * Loads attributes from a TypedArray and applies the color values.
45 | *
46 | * @param typedArray The TypedArray containing the attribute values.
47 | * @return The builder instance for method chaining.
48 | */
49 | override fun loadAttributes(typedArray: TypedArray): ColorHighlightBuilder {
50 | super.loadAttributes(typedArray)
51 |
52 | // Get the base color and highlight color from the attributes and apply them
53 | typedArray.getColorOrNull(R.styleable.ShimmerFrameLayout_shimmer_base_color)?.let { setBaseColor(it) }
54 | typedArray.getColorOrNull(R.styleable.ShimmerFrameLayout_shimmer_highlight_color)?.let { setHighlightColor(it) }
55 |
56 | return this
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_data.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
30 |
31 |
41 |
42 |
43 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/shimmer_item_data.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
22 |
23 |
24 |
33 |
34 |
35 |
45 |
46 |
47 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/shimmer_animation/src/main/java/az/lahza/shimmer_animation/shimmer/Shimmer.kt:
--------------------------------------------------------------------------------
1 | package az.lahza.shimmer_animation.shimmer
2 |
3 | import android.animation.ValueAnimator
4 | import android.graphics.Color
5 | import androidx.annotation.ColorInt
6 | import az.lahza.shimmer_animation.shimmer.annotations.Direction
7 | import az.lahza.shimmer_animation.shimmer.annotations.Shape
8 | import kotlin.math.roundToInt
9 |
10 | /**
11 | * A class that defines the shimmer animation properties and behavior.
12 | */
13 | class Shimmer {
14 |
15 | // ============================
16 | // Shimmer Configuration
17 | // ============================
18 |
19 | /** Direction of the shimmer animation */
20 | @Direction var direction = Direction.LEFT_TO_RIGHT
21 |
22 | /** Highlight color of the shimmer effect */
23 | @ColorInt var highlightColor = Color.WHITE
24 |
25 | /** Base color of the shimmer effect */
26 | @ColorInt var baseColor = 0x4cffffff
27 |
28 | /** Shape of the shimmer animation */
29 | @Shape var shape = Shape.LINEAR
30 |
31 | /** Fixed width of the shimmer (0 for dynamic) */
32 | private var fixedWidth = 0
33 |
34 | /** Fixed height of the shimmer (0 for dynamic) */
35 | private var fixedHeight = 0
36 |
37 | /** Width ratio relative to the view's width */
38 | var widthRatio = 1f
39 |
40 | /** Height ratio relative to the view's height */
41 | var heightRatio = 1f
42 |
43 | /** Intensity of the shimmer effect */
44 | var intensity = 0f
45 |
46 | /** Dropoff effect for shimmer (controls fadeout) */
47 | var dropoff = 0.5f
48 |
49 | /** Tilt angle for shimmer movement */
50 | var tilt = 20f
51 |
52 | /** Whether to clip the shimmer to the children views */
53 | var clipToChildren = true
54 |
55 | /** Whether the shimmer effect should auto-start */
56 | var autoStart = true
57 |
58 | /** Whether the shimmer effect includes alpha shimmer */
59 | var alphaShimmer = true
60 |
61 | /** Number of repetitions for the shimmer effect */
62 | var repeatCount = ValueAnimator.INFINITE
63 |
64 | /** The repeat mode of the shimmer animation */
65 | var repeatMode = ValueAnimator.RESTART
66 |
67 | /** Duration of the shimmer animation */
68 | var animationDuration = 1400L
69 |
70 | /** Delay before the shimmer effect starts */
71 | var repeatDelay: Long = 0
72 |
73 | /** Delay before the shimmer effect begins animating */
74 | var startDelay: Long = 0
75 |
76 | // ============================
77 | // Shimmer Effect Arrays
78 | // ============================
79 |
80 | /** Array of shimmer effect positions */
81 | val positions = FloatArray(COMPONENT_COUNT)
82 |
83 | /** Array of shimmer effect colors */
84 | val colors = IntArray(COMPONENT_COUNT)
85 |
86 | // ============================
87 | // Width and Height Calculations
88 | // ============================
89 |
90 | /**
91 | * Calculates the shimmer width based on fixedWidth or widthRatio.
92 | */
93 | fun width(width: Int) = if (fixedWidth > 0) fixedWidth else (widthRatio * width).roundToInt()
94 |
95 | /**
96 | * Calculates the shimmer height based on fixedHeight or heightRatio.
97 | */
98 | fun height(height: Int) = if (fixedHeight > 0) fixedHeight else (heightRatio * height).roundToInt()
99 |
100 | // ============================
101 | // Update Shimmer Colors
102 | // ============================
103 |
104 | /**
105 | * Updates the shimmer colors based on the shape of the shimmer effect.
106 | */
107 | fun updateColors() {
108 | colors.apply {
109 | val colorSequence = when (shape) {
110 | Shape.LINEAR -> listOf(baseColor, highlightColor, highlightColor, baseColor)
111 | Shape.RADIAL -> listOf(highlightColor, highlightColor, baseColor, baseColor)
112 | else -> listOf(baseColor, highlightColor, highlightColor, baseColor)
113 | }
114 | colorSequence.forEachIndexed { index, color -> this[index] = color }
115 | }
116 | }
117 |
118 | // ============================
119 | // Update Shimmer Positions
120 | // ============================
121 |
122 | /**
123 | * Updates the shimmer positions based on intensity and dropOff.
124 | */
125 | fun updatePositions() {
126 | val halfIntensity = (1f - intensity - dropoff) / 2f
127 | val halfIntensityEdge = (1f - intensity - 0.001f) / 2f
128 | val halfEndEdge = (1f + intensity + 0.001f) / 2f
129 | val halfEnd = (1f + intensity + dropoff) / 2f
130 |
131 | // Ensure values are within valid bounds (0f to 1f)
132 | val startPosition = halfIntensity.coerceAtLeast(0f)
133 | val startEdgePosition = halfIntensityEdge.coerceAtLeast(0f)
134 | val endEdgePosition = halfEndEdge.coerceAtMost(1f)
135 | val endPosition = halfEnd.coerceAtMost(1f)
136 |
137 | // Update the shimmer gradient positions
138 | positions.apply {
139 | this[0] = startPosition
140 | this[1] = startEdgePosition
141 | this[2] = endEdgePosition
142 | this[3] = endPosition
143 | }
144 | }
145 |
146 | companion object {
147 | /** The number of components in the shimmer effect */
148 | private const val COMPONENT_COUNT = 4
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Shimmer Animation
4 | - 
5 | - 
6 | - 
7 | - 
8 |
9 | A Shimmer Animation library for Kotlin.
10 |
11 |
12 |
13 |
14 |
15 |
16 | ## 📦 Features
17 |
18 | - 🌈 **Customizable Direction**
19 | Tailor the shimmer animation’s direction to fit your design needs:
20 | - **LEFT_TO_RIGHT**: Shimmer moves from left to right
21 | - **TOP_TO_BOTTOM**: Shimmer moves from top to bottom
22 | - **RIGHT_TO_LEFT**: Shimmer moves from right to left
23 | - **BOTTOM_TO_TOP**: Shimmer moves from bottom to top
24 |
25 | - 🛠 **Customizable Shape**
26 | Choose the shimmer effect that best suits your UI:
27 | - **LINEAR**: A simple, linear shimmer effect
28 | - **RADIAL**: A circular, radial shimmer effect
29 |
30 | - 🔥 **Customizable Intensity**
31 | Adjust the shimmer intensity to create the desired visual impact that matches your app’s style.
32 |
33 | - ✨ **Customizable Fadeout**
34 | Smoothly control the fadeout effect of the shimmer animation for seamless transitions.
35 |
36 | - 🔁 **Customizable Repeat Count**
37 | Define how many times the shimmer animation will repeat, giving you full control over the effect duration.
38 |
39 | - ⏳ **Customizable Animation Duration**
40 | Set the duration of each shimmer cycle, allowing you to control how long the effect lasts.
41 |
42 | - 🕒 **Customizable Repeat Delay**
43 | Manage the time interval between consecutive shimmer cycles for a more natural animation flow.
44 |
45 | - 🚀 **Customizable Start Delay**
46 | Set a delay before the shimmer animation begins to create a more dynamic timing sequence.
47 |
48 | ## 🛠 Installation
49 | **Step 1.** Add it in your root setting.gradle at the end of repositories:
50 | ```kotlin
51 | dependencyResolutionManagement {
52 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
53 | repositories {
54 | mavenCentral()
55 |
56 | // Include JitPack, a repository that allows you to fetch dependencies from GitHub projects.
57 | maven { url 'https://jitpack.io' }
58 | }
59 | }
60 | ```
61 | **Step 2.** Add the dependency
62 |
63 | ```kotlin
64 | // This dependencies block is for the build.gradle
65 | dependencies {
66 | implementation 'com.github.zaminalirustamov:Shimmer_Animation:1.0.2'
67 | }
68 | ```
69 | ## 🚀 Usage
70 | ### Example
71 |
72 | ```xml
73 |
74 |
82 |
83 |
86 |
87 |
91 |
92 |
93 |
94 | ```
95 |
96 | ```xml
97 |
98 |
106 |
107 |
110 |
111 |
116 |
117 |
123 |
124 |
130 |
131 |
137 |
138 |
139 |
140 |
141 | ```
142 |
143 | For more examples, check out the code
144 |
145 | ## 🤝 Contribution
146 | I highly appreciate and welcome any issue reports, feature requests, pull requests, or GitHub stars you may provide.
147 |
148 |
149 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/shimmer_animation/src/main/java/az/lahza/shimmer_animation/shimmer/ShimmerFrameLayout.kt:
--------------------------------------------------------------------------------
1 | package az.lahza.shimmer_animation.shimmer
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Paint
6 | import android.graphics.drawable.Drawable
7 | import android.util.AttributeSet
8 | import android.view.View
9 | import android.widget.FrameLayout
10 | import az.lahza.shimmer_animation.R
11 | import az.lahza.shimmer_animation.shimmer.builders.AlphaHighlightBuilder
12 | import az.lahza.shimmer_animation.shimmer.builders.ColorHighlightBuilder
13 |
14 | /**
15 | * A custom FrameLayout that displays a shimmer effect over its content.
16 | * The shimmer effect can be customized using XML attributes.
17 | */
18 | class ShimmerFrameLayout : FrameLayout {
19 |
20 | // Paint object for content rendering
21 | private val mContentPaint = Paint()
22 |
23 | // Shimmer drawable that applies the shimmer effect
24 | private val mShimmerDrawable: ShimmerDrawable = ShimmerDrawable()
25 |
26 | // Flag to track if shimmer is visible
27 | private var isShimmerVisible = true
28 |
29 | // Flag to track if shimmer should be stopped because of visibility change
30 | private var mStoppedShimmerBecauseVisibility = false
31 |
32 | constructor(context: Context) : super(context) {
33 | init(context, null)
34 | }
35 |
36 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
37 | init(context, attrs)
38 | }
39 |
40 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
41 | context,
42 | attrs,
43 | defStyleAttr
44 | ) {
45 | init(context, attrs)
46 | }
47 |
48 | /**
49 | * Initializes the shimmer effect based on XML attributes.
50 | * If no attributes are provided, the default AlphaHighlightBuilder is used.
51 | */
52 | private fun init(context: Context, attrs: AttributeSet?) {
53 | setWillNotDraw(false) // Allow drawing the shimmer effect
54 | mShimmerDrawable.callback = this // Set the callback for drawing the shimmer effect
55 |
56 | // Set default shimmer if no attributes are provided
57 | if (attrs == null) {
58 | setShimmer(AlphaHighlightBuilder().build())
59 | return
60 | }
61 |
62 | // Load shimmer attributes from XML
63 | val typedArray = context.obtainStyledAttributes(
64 | /* set = */ attrs,
65 | /* attrs = */ R.styleable.ShimmerFrameLayout,
66 | /* defStyleAttr = */ 0,
67 | /* defStyleRes = */ 0
68 | )
69 | try {
70 | val shimmerBuilder =
71 | if (typedArray.hasValue(R.styleable.ShimmerFrameLayout_shimmer_colored)
72 | && typedArray.getBoolean(R.styleable.ShimmerFrameLayout_shimmer_colored, false)
73 | ) ColorHighlightBuilder() else AlphaHighlightBuilder()
74 |
75 | // Set shimmer properties from XML attributes
76 | setShimmer(shimmerBuilder.loadAttributes(typedArray)!!.build())
77 | } finally {
78 | typedArray.recycle()
79 | }
80 | }
81 |
82 | /**
83 | * Sets the shimmer effect to be displayed.
84 | * If the shimmer requires clipping to children, set hardware acceleration.
85 | */
86 | private fun setShimmer(shimmer: Shimmer?): ShimmerFrameLayout {
87 | mShimmerDrawable.shimmer = shimmer
88 | if (shimmer != null && shimmer.clipToChildren) {
89 | setLayerType(LAYER_TYPE_HARDWARE, mContentPaint)
90 | } else {
91 | setLayerType(LAYER_TYPE_NONE, null)
92 | }
93 | return this
94 | }
95 |
96 | /**
97 | * Stops the shimmer animation.
98 | */
99 | private fun stopShimmer() {
100 | mStoppedShimmerBecauseVisibility = false
101 | mShimmerDrawable.stopShimmer()
102 | }
103 |
104 | /**
105 | * Checks if the shimmer animation has started.
106 | */
107 | private val isShimmerStarted: Boolean
108 | get() = mShimmerDrawable.isShimmerStarted
109 |
110 | /**
111 | * Called when the layout bounds change. Updates the shimmer bounds accordingly.
112 | */
113 | public override fun onLayout(
114 | changed: Boolean,
115 | left: Int,
116 | top: Int,
117 | right: Int,
118 | bottom: Int
119 | ) {
120 | super.onLayout(changed, left, top, right, bottom)
121 | val width = width
122 | val height = height
123 | mShimmerDrawable.setBounds(0, 0, width, height)
124 | }
125 |
126 | /**
127 | * Handles visibility changes. Stops the shimmer if visibility is not VISIBLE,
128 | * and restarts it if visibility is restored.
129 | */
130 | override fun onVisibilityChanged(changedView: View, visibility: Int) {
131 | super.onVisibilityChanged(changedView, visibility)
132 |
133 | if (visibility != VISIBLE) {
134 | if (isShimmerStarted) {
135 | stopShimmer()
136 | mStoppedShimmerBecauseVisibility = true
137 | }
138 | } else if (mStoppedShimmerBecauseVisibility) {
139 | mShimmerDrawable.maybeStartShimmer()
140 | mStoppedShimmerBecauseVisibility = false
141 | }
142 | }
143 |
144 | /**
145 | * Called when the view is attached to the window. Starts the shimmer animation if needed.
146 | */
147 | public override fun onAttachedToWindow() {
148 | super.onAttachedToWindow()
149 | mShimmerDrawable.maybeStartShimmer()
150 | }
151 |
152 | /**
153 | * Called when the view is detached from the window. Stops the shimmer animation.
154 | */
155 | public override fun onDetachedFromWindow() {
156 | super.onDetachedFromWindow()
157 | stopShimmer()
158 | }
159 |
160 | /**
161 | * Draws the content of the layout along with the shimmer effect.
162 | */
163 | public override fun dispatchDraw(canvas: Canvas) {
164 | super.dispatchDraw(canvas)
165 | if (isShimmerVisible) {
166 | mShimmerDrawable.draw(canvas)
167 | }
168 | }
169 |
170 | /**
171 | * Verifies if the drawable is the shimmer drawable.
172 | */
173 | override fun verifyDrawable(who: Drawable) =
174 | super.verifyDrawable(who) || who === mShimmerDrawable
175 | }
176 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/shimmer_animation/src/main/java/az/lahza/shimmer_animation/shimmer/builders/Builder.kt:
--------------------------------------------------------------------------------
1 | package az.lahza.shimmer_animation.shimmer.builders
2 |
3 | import android.content.Context
4 | import android.content.res.TypedArray
5 | import android.util.AttributeSet
6 | import androidx.annotation.ColorInt
7 | import androidx.annotation.FloatRange
8 | import az.lahza.shimmer_animation.R
9 | import az.lahza.shimmer_animation.shimmer.Shimmer
10 | import az.lahza.shimmer_animation.shimmer.annotations.Direction
11 | import az.lahza.shimmer_animation.shimmer.annotations.Shape
12 | import az.lahza.shimmer_animation.shimmer.extensions.getBooleanOrNull
13 | import az.lahza.shimmer_animation.shimmer.extensions.getFloatOrNull
14 | import az.lahza.shimmer_animation.shimmer.extensions.getIntOrNull
15 |
16 | abstract class Builder?> {
17 |
18 | protected val shimmer = Shimmer()
19 |
20 | protected abstract val self: T
21 |
22 | /**
23 | * Builds the final Shimmer instance with applied attributes.
24 | */
25 | fun build() = shimmer.apply {
26 | updateColors()
27 | updatePositions()
28 | }
29 |
30 | /**
31 | * Loads custom attributes for a view from XML.
32 | *
33 | * This method retrieves the custom attributes defined in the XML layout
34 | * and applies them to the shimmer effect configuration.
35 | *
36 | * @param context The context used to obtain styled attributes.
37 | * @param attrs The attribute set from the XML layout.
38 | * @return The builder instance for method chaining.
39 | */
40 | fun loadAttributes(context: Context, attrs: AttributeSet?): T {
41 | context.obtainStyledAttributes(
42 | attrs, R.styleable.ShimmerFrameLayout
43 | ).use { typedArray ->
44 | loadAttributes(typedArray)
45 | }
46 | return self
47 | }
48 |
49 | /**
50 | * Loads custom attributes from a TypedArray and applies them to the shimmer object.
51 | *
52 | * @param typedArray The TypedArray containing the custom attributes.
53 | * @return The builder instance for method chaining.
54 | */
55 | open fun loadAttributes(typedArray: TypedArray): T {
56 | typedArray.apply {
57 | getBooleanOrNull(R.styleable.ShimmerFrameLayout_shimmer_clip_to_children)?.let { shimmer.clipToChildren = it }
58 | getBooleanOrNull(R.styleable.ShimmerFrameLayout_shimmer_auto_start)?.let { shimmer.autoStart = it }
59 | getFloatOrNull(R.styleable.ShimmerFrameLayout_shimmer_base_alpha)?.let { setBaseAlpha(it) }
60 | getFloatOrNull(R.styleable.ShimmerFrameLayout_shimmer_highlight_alpha)?.let { setHighlightAlpha(it) }
61 | getFloatOrNull(R.styleable.ShimmerFrameLayout_shimmer_intensity)?.let { setIntensity(it) }
62 | getFloatOrNull(R.styleable.ShimmerFrameLayout_shimmer_width_ratio)?.let { setWidthRatio(it) }
63 | getFloatOrNull(R.styleable.ShimmerFrameLayout_shimmer_height_ratio)?.let { setHeightRatio(it) }
64 | getFloatOrNull(R.styleable.ShimmerFrameLayout_shimmer_dropoff)?.let { setDropOff(it) }
65 | getFloatOrNull(R.styleable.ShimmerFrameLayout_shimmer_tilt)?.let { shimmer.tilt = it }
66 | getIntOrNull(R.styleable.ShimmerFrameLayout_shimmer_direction)?.let { setDirection(it) }
67 | getIntOrNull(R.styleable.ShimmerFrameLayout_shimmer_shape)?.let { setShape(it) }
68 | getIntOrNull(R.styleable.ShimmerFrameLayout_shimmer_duration)?.let { shimmer.animationDuration = it.toLong() }
69 | getIntOrNull(R.styleable.ShimmerFrameLayout_shimmer_repeat_count)?.let { shimmer.repeatCount = it }
70 | getIntOrNull(R.styleable.ShimmerFrameLayout_shimmer_repeat_delay)?.let { shimmer.repeatDelay = it.toLong() }
71 | getIntOrNull(R.styleable.ShimmerFrameLayout_shimmer_start_delay)?.let { shimmer.startDelay = it.toLong() }
72 | }
73 | return self
74 | }
75 |
76 | // ===========================
77 | // Setter Methods for Shimmer Properties
78 | // ===========================
79 |
80 | /**
81 | * Sets the base alpha value for the shimmer effect.
82 | */
83 | private fun setBaseAlpha(@FloatRange(from = 0.0, to = 1.0) alpha: Float) = apply {
84 | shimmer.baseColor = adjustAlpha(shimmer.baseColor, alpha)
85 | }
86 |
87 | /**
88 | * Sets the highlight alpha value for the shimmer effect.
89 | */
90 | private fun setHighlightAlpha(@FloatRange(from = 0.0, to = 1.0) alpha: Float) = apply {
91 | shimmer.highlightColor = adjustAlpha(shimmer.highlightColor, alpha)
92 | }
93 |
94 | /**
95 | * Adjusts the alpha value of a color by the given factor.
96 | * The factor should be between 0.0 and 1.0.
97 | */
98 | private fun adjustAlpha(@ColorInt color: Int, factor: Float): Int {
99 | val alpha = (255 * factor).toInt()
100 | return (color and 0x00FFFFFF) or (alpha shl 24)
101 | }
102 |
103 | /**
104 | * Sets the intensity for the shimmer effect.
105 | * Intensity controls how strong the shimmer effect appears.
106 | *
107 | * @param intensity The intensity value, typically between 0.0 and 1.0.
108 | */
109 | private fun setIntensity(intensity: Float) = apply {
110 | shimmer.intensity = intensity
111 | }
112 |
113 | /**
114 | * Sets the width ratio for the shimmer effect.
115 | * Width ratio controls how wide the shimmer effect appears relative to the view's width.
116 | *
117 | * @param widthRatio The width ratio, typically between 0.0 and 1.0.
118 | */
119 | private fun setWidthRatio(widthRatio: Float) = apply {
120 | shimmer.widthRatio = widthRatio
121 | }
122 |
123 | /**
124 | * Sets the height ratio for the shimmer effect.
125 | * Height ratio controls how tall the shimmer effect appears relative to the view's height.
126 | *
127 | * @param heightRatio The height ratio, typically between 0.0 and 1.0.
128 | */
129 | private fun setHeightRatio(heightRatio: Float) = apply {
130 | shimmer.heightRatio = heightRatio
131 | }
132 |
133 | /**
134 | * Sets the dropOff value for the shimmer effect.
135 | * DropOff controls how quickly the shimmer effect fades off.
136 | *
137 | * @param dropOff The dropOff value, typically between 0.0 and 1.0.
138 | */
139 | private fun setDropOff(dropOff: Float) = apply {
140 | shimmer.dropoff = dropOff
141 | }
142 |
143 | /**
144 | * Sets the direction of the shimmer effect.
145 | * Direction defines in which direction the shimmer animation occurs.
146 | *
147 | * @param direction One of the predefined direction constants from the [Direction] annotation.
148 | */
149 | private fun setDirection(@Direction direction: Int) = apply {
150 | shimmer.direction = direction
151 | }
152 |
153 | /**
154 | * Sets the shape of the shimmer effect.
155 | * Shape defines the pattern or geometry of the shimmer effect.
156 | *
157 | * @param shape One of the predefined shape constants from the [Shape] annotation.
158 | */
159 | private fun setShape(@Shape shape: Int) = apply {
160 | shimmer.shape = shape
161 | }
162 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
19 |
20 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
42 |
43 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
56 |
64 |
65 |
68 |
69 |
73 |
74 |
75 |
76 |
77 |
78 |
85 |
86 |
89 |
90 |
94 |
95 |
96 |
97 |
98 |
99 |
106 |
107 |
110 |
111 |
115 |
116 |
117 |
118 |
119 |
120 |
128 |
129 |
132 |
133 |
137 |
138 |
139 |
140 |
141 |
142 |
150 |
151 |
154 |
155 |
160 |
161 |
167 |
168 |
174 |
175 |
181 |
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/shimmer_animation/src/main/java/az/lahza/shimmer_animation/shimmer/ShimmerDrawable.kt:
--------------------------------------------------------------------------------
1 | package az.lahza.shimmer_animation.shimmer
2 |
3 | import android.animation.ValueAnimator
4 | import android.animation.ValueAnimator.AnimatorUpdateListener
5 | import android.graphics.Canvas
6 | import android.graphics.ColorFilter
7 | import android.graphics.LinearGradient
8 | import android.graphics.Matrix
9 | import android.graphics.Paint
10 | import android.graphics.PixelFormat
11 | import android.graphics.PorterDuff
12 | import android.graphics.PorterDuffXfermode
13 | import android.graphics.RadialGradient
14 | import android.graphics.Rect
15 | import android.graphics.Shader
16 | import android.graphics.drawable.Drawable
17 | import android.view.animation.LinearInterpolator
18 | import az.lahza.shimmer_animation.shimmer.annotations.Direction
19 | import az.lahza.shimmer_animation.shimmer.annotations.Shape
20 | import kotlin.math.sqrt
21 | import kotlin.math.tan
22 |
23 | /**
24 | * A drawable used to display a shimmer effect on a canvas.
25 | *
26 | * This drawable applies a shimmer effect over a view by animating a gradient
27 | * across the surface. The shimmer can be configured with various properties
28 | * such as direction, shape, tilt, intensity, and more.
29 | */
30 | class ShimmerDrawable : Drawable() {
31 |
32 | // Animator listener to trigger a redraw on each animation frame
33 | private val mUpdateListener = AnimatorUpdateListener { invalidateSelf() }
34 |
35 | // Paint object used to draw the shimmer
36 | private val mShimmerPaint = Paint().apply { isAntiAlias = true }
37 |
38 | // Rect object to define the bounds for drawing
39 | private val mDrawRect = Rect()
40 |
41 | // Matrix for manipulating the shader
42 | private val mShaderMatrix = Matrix()
43 |
44 | // ValueAnimator for controlling the shimmer animation
45 | private var mValueAnimator: ValueAnimator? = null
46 |
47 | // Used for static progress if animation is not running
48 | private var mStaticAnimationProgress = -1f
49 |
50 | // Holds the shimmer configuration
51 | private var mShimmer: Shimmer? = null
52 |
53 | /**
54 | * The shimmer configuration that defines the behavior of the shimmer effect.
55 | * Setting this property triggers the shimmer update process.
56 | */
57 | var shimmer: Shimmer?
58 | get() = mShimmer
59 | set(value) {
60 | mShimmer = value
61 | mShimmer?.let {
62 | mShimmerPaint.xfermode = PorterDuffXfermode(
63 | if (it.alphaShimmer) PorterDuff.Mode.DST_IN else PorterDuff.Mode.SRC_IN
64 | )
65 | updateShader()
66 | updateValueAnimator()
67 | invalidateSelf()
68 | }
69 | }
70 |
71 | /**
72 | * Indicates whether the shimmer animation has started.
73 | */
74 | val isShimmerStarted: Boolean
75 | get() = mValueAnimator?.isStarted == true
76 |
77 | /**
78 | * Stops the shimmer animation if it is currently running.
79 | */
80 | fun stopShimmer() {
81 | if (mValueAnimator != null && isShimmerStarted) {
82 | mValueAnimator!!.cancel()
83 | }
84 | }
85 |
86 | /**
87 | * Called when the bounds of the drawable change.
88 | * This updates the drawing rectangle and shader, and potentially starts the shimmer animation.
89 | */
90 | public override fun onBoundsChange(bounds: Rect) {
91 | super.onBoundsChange(bounds)
92 | mDrawRect.set(bounds)
93 | updateShader()
94 | maybeStartShimmer()
95 | }
96 |
97 | /**
98 | * Draws the shimmer effect on the canvas. The shimmer is animated by applying a gradient
99 | * shader and translating it according to the animation progress.
100 | *
101 | * @param canvas The canvas on which to draw the shimmer effect.
102 | */
103 | override fun draw(canvas: Canvas) {
104 | val shimmer = mShimmer ?: return
105 | val shader = mShimmerPaint.shader ?: return
106 |
107 | // Calculate the tilt factor based on the shimmer tilt angle
108 | val tiltTan = tan(Math.toRadians(shimmer.tilt.toDouble())).toFloat()
109 |
110 | // Calculate translation values based on the shimmer tilt and bounds
111 | val translateHeight = mDrawRect.height() + tiltTan * mDrawRect.width()
112 | val translateWidth = mDrawRect.width() + tiltTan * mDrawRect.height()
113 |
114 | // Determine the animation progress (either static or from the animator)
115 | val animatedValue =
116 | mStaticAnimationProgress.takeIf { it >= 0f } ?: (mValueAnimator?.animatedValue as? Float
117 | ?: 0f)
118 |
119 | // Calculate the translation offsets based on the shimmer direction
120 | val (dx, dy) = calculateTranslation(
121 | shimmer.direction, translateWidth, translateHeight, animatedValue
122 | )
123 |
124 | // Reset and update the shader matrix for applying translation and rotation
125 | mShaderMatrix.reset()
126 | mShaderMatrix.setRotate(shimmer.tilt, mDrawRect.width() / 2f, mDrawRect.height() / 2f)
127 | mShaderMatrix.preTranslate(dx, dy)
128 |
129 | // Apply the transformation to the shader and draw the shimmer effect
130 | shader.setLocalMatrix(mShaderMatrix)
131 | canvas.drawRect(mDrawRect, mShimmerPaint)
132 | }
133 |
134 |
135 | /**
136 | * Calculates the translation offsets (dx, dy) based on the shimmer direction.
137 | *
138 | * @param direction The direction of the shimmer effect.
139 | * @param translateWidth The width translation value.
140 | * @param translateHeight The height translation value.
141 | * @param animatedValue The current progress of the animation.
142 | * @return A pair of translation values (dx, dy).
143 | */
144 | private fun calculateTranslation(
145 | direction: Int, translateWidth: Float, translateHeight: Float, animatedValue: Float
146 | ) = when (direction) {
147 | Direction.LEFT_TO_RIGHT -> offset(-translateWidth, translateWidth, animatedValue) to 0f
148 | Direction.RIGHT_TO_LEFT -> offset(translateWidth, -translateWidth, animatedValue) to 0f
149 | Direction.TOP_TO_BOTTOM -> 0f to offset(-translateHeight, translateHeight, animatedValue)
150 | Direction.BOTTOM_TO_TOP -> 0f to offset(translateHeight, -translateHeight, animatedValue)
151 | else -> offset(-translateWidth, translateWidth, animatedValue) to 0f
152 | }
153 |
154 |
155 | override fun setAlpha(alpha: Int) {}
156 |
157 | override fun setColorFilter(colorFilter: ColorFilter?) {}
158 |
159 | @Deprecated("Deprecated in Java")
160 | override fun getOpacity(): Int {
161 | return if (mShimmer?.clipToChildren == true || mShimmer?.alphaShimmer == true) {
162 | PixelFormat.TRANSLUCENT
163 | } else {
164 | PixelFormat.OPAQUE
165 | }
166 | }
167 |
168 | /**
169 | * Adjusts the translation offset based on the animation progress.
170 | *
171 | * @param start The starting translation value.
172 | * @param end The ending translation value.
173 | * @param percent The progress of the animation (0f to 1f).
174 | * @return The calculated offset.
175 | */
176 | private fun offset(start: Float, end: Float, percent: Float) = start + (end - start) * percent
177 |
178 | /**
179 | * Updates the ValueAnimator for the shimmer animation based on the current shimmer properties.
180 | * Cancels the existing animator (if any) and creates a new one with the correct properties.
181 | */
182 | private fun updateValueAnimator() {
183 | // Ensure shimmer is not null
184 | mShimmer?.let { shimmer ->
185 | // Cancel and reset the existing animator, if it exists
186 | mValueAnimator?.cancel()
187 | mValueAnimator?.removeAllUpdateListeners()
188 |
189 | // Create a new ValueAnimator with the correct properties
190 | mValueAnimator = ValueAnimator.ofFloat(
191 | 0f, 1f + (shimmer.repeatDelay / shimmer.animationDuration).toFloat()
192 | ).apply {
193 | // Set the interpolator for the animation
194 | interpolator = LinearInterpolator()
195 |
196 | // Set repeat mode, count, and duration based on shimmer properties
197 | repeatMode = shimmer.repeatMode
198 | startDelay = shimmer.startDelay
199 | repeatCount = shimmer.repeatCount
200 | duration = shimmer.animationDuration + shimmer.repeatDelay
201 |
202 | // Add an update listener to trigger invalidation
203 | addUpdateListener(mUpdateListener)
204 |
205 | // Start the animation
206 | start()
207 | }
208 | }
209 | }
210 |
211 | /**
212 | * Starts the shimmer animation if it is not already started and the conditions are met.
213 | */
214 | fun maybeStartShimmer() {
215 | // Ensure the shimmer and animator are valid, and autoStart is true
216 | if (mValueAnimator?.isStarted == true || mShimmer?.autoStart == false || callback == null) return
217 |
218 | // Start the shimmer animation if the conditions are satisfied
219 | mValueAnimator?.start()
220 | }
221 |
222 |
223 | /**
224 | * Updates the shader used for drawing the shimmer effect.
225 | * This method creates the shader based on the shimmer properties.
226 | */
227 | private fun updateShader() {
228 | // Early return if shimmer is null or bounds are invalid
229 | val shimmer = mShimmer ?: return
230 | val bounds = bounds
231 | if (bounds.width() == 0 || bounds.height() == 0) return
232 |
233 | // Calculate the width and height for the shimmer effect
234 | val width = shimmer.width(bounds.width())
235 | val height = shimmer.height(bounds.height())
236 |
237 | // Create the appropriate shader based on shimmer properties
238 | val shader = createShader(shimmer, width, height)
239 |
240 | // Apply the shader to the paint object
241 | mShimmerPaint.shader = shader
242 | }
243 |
244 | /**
245 | * Creates the shader for the shimmer effect based on the current shimmer properties.
246 | *
247 | * @param width The width of the shimmer effect.
248 | * @param height The height of the shimmer effect.
249 | * @return The shader to be used for the shimmer effect.
250 | */
251 | private fun createShader(shimmer: Shimmer, width: Int, height: Int): Shader {
252 | return when (shimmer.shape) {
253 | Shape.LINEAR -> createLinearGradient(shimmer, width, height)
254 | Shape.RADIAL -> createRadialGradient(shimmer, width, height)
255 | else -> createLinearGradient(shimmer, width, height) // Default to linear gradient
256 | }
257 | }
258 |
259 | /**
260 | * Creates a linear gradient shader for the shimmer effect based on the shimmer direction.
261 | *
262 | * @param shimmer The shimmer configuration containing direction, colors, and positions.
263 | * @param width The width of the shimmer effect (view width).
264 | * @param height The height of the shimmer effect (view height).
265 | * @return The linear gradient shader based on the shimmer's direction and properties.
266 | */
267 | private fun createLinearGradient(shimmer: Shimmer, width: Int, height: Int): LinearGradient {
268 | // Determine whether the shimmer direction is vertical or horizontal
269 | val isVertical =
270 | shimmer.direction == Direction.TOP_TO_BOTTOM || shimmer.direction == Direction.BOTTOM_TO_TOP
271 |
272 | // Calculate the end coordinates for the gradient based on the direction
273 | val endX = if (isVertical) 0 else width
274 | val endY = if (isVertical) height else 0
275 |
276 | // Return the linear gradient shader with the calculated values
277 | return LinearGradient(
278 | 0f, // Start at the top-left corner (0,0)
279 | 0f, // Start at the top-left corner (0,0)
280 | endX.toFloat(), endY.toFloat(), shimmer.colors, // Color stops
281 | shimmer.positions, // Gradient positions
282 | Shader.TileMode.CLAMP // Prevents the gradient from repeating
283 | )
284 | }
285 |
286 |
287 | /**
288 | * Creates a radial gradient shader for the shimmer effect.
289 | *
290 | * @param shimmer The shimmer configuration.
291 | * @param width The width of the shimmer effect.
292 | * @param height The height of the shimmer effect.
293 | * @return The radial gradient shader.
294 | */
295 | private fun createRadialGradient(shimmer: Shimmer, width: Int, height: Int) =
296 | RadialGradient(
297 | width / 2f,
298 | height / 2f,
299 | (width.coerceAtLeast(height) / sqrt(2.0)).toFloat(),
300 | shimmer.colors,
301 | shimmer.positions,
302 | Shader.TileMode.CLAMP
303 | )
304 | }
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
328 |
329 |
--------------------------------------------------------------------------------