Skip to content

Commit cd92542

Browse files
committed
feat: introduces animated icons in navigation
1 parent 51af955 commit cd92542

20 files changed

Lines changed: 645 additions & 96 deletions

app/src/main/kotlin/com/androidvip/sysctlgui/core/navigation/TopLevelRoute.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import androidx.compose.runtime.Immutable
1010
* route information to be associated with the top-level destination.
1111
* @property name The human-readable name of the top-level destination, used for labels.
1212
* @property route The actual [UiRoute] object that defines the navigation destination.
13-
* @property selectedIconRes The icon to display when this top-level route is currently selected.
14-
* @property unselectedIconRes The icon to display when this top-level route is not selected.
13+
* @property selectedAnimatedIconRes The icon to display when this top-level route is currently selected.
14+
* @property unselectedAnimatedIconRes The icon to display when this top-level route is not selected.
1515
*/
1616
@Immutable
1717
data class TopLevelRoute<T : UiRoute>(
1818
val name: String,
1919
val route: T,
20-
@param:DrawableRes val selectedIconRes: Int,
21-
@param:DrawableRes val unselectedIconRes: Int
20+
@param:DrawableRes val selectedAnimatedIconRes: Int,
21+
@param:DrawableRes val unselectedAnimatedIconRes: Int
2222
)

app/src/main/kotlin/com/androidvip/sysctlgui/ui/main/MainNavBar.kt

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.androidvip.sysctlgui.ui.main
22

3-
import androidx.compose.animation.AnimatedContent
3+
import androidx.compose.animation.graphics.res.animatedVectorResource
4+
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
5+
import androidx.compose.animation.graphics.vector.AnimatedImageVector
46
import androidx.compose.material3.Icon
57
import androidx.compose.material3.NavigationBar
68
import androidx.compose.material3.NavigationBarItem
@@ -9,7 +11,6 @@ import androidx.compose.runtime.Composable
911
import androidx.compose.runtime.getValue
1012
import androidx.compose.runtime.remember
1113
import androidx.compose.ui.platform.LocalContext
12-
import androidx.compose.ui.res.painterResource
1314
import androidx.compose.ui.text.style.TextOverflow
1415
import androidx.compose.ui.tooling.preview.PreviewDynamicColors
1516
import androidx.compose.ui.tooling.preview.PreviewLightDark
@@ -35,20 +36,15 @@ internal fun MainNavBar(navController: NavHostController = rememberNavController
3536
?.hierarchy
3637
?.any { it.hasRoute(route.route::class) } == true
3738

39+
val iconRes = if (selected) route.selectedAnimatedIconRes else route.unselectedAnimatedIconRes
40+
val imageVector = AnimatedImageVector.animatedVectorResource(id = iconRes)
41+
val animatedPainter = rememberAnimatedVectorPainter(
42+
animatedImageVector = imageVector,
43+
atEnd = selected
44+
)
45+
3846
NavigationBarItem(
39-
icon = {
40-
AnimatedContent(targetState = selected) { selectedState ->
41-
val iconRes = if (selectedState) {
42-
route.selectedIconRes
43-
} else {
44-
route.unselectedIconRes
45-
}
46-
Icon(
47-
painter = painterResource(iconRes),
48-
contentDescription = route.name,
49-
)
50-
}
51-
},
47+
icon = { Icon(painter = animatedPainter, contentDescription = route.name) },
5248
label = {
5349
Text(
5450
text = route.name,

app/src/main/kotlin/com/androidvip/sysctlgui/ui/main/MainNavRail.kt

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.androidvip.sysctlgui.ui.main
22

3-
import androidx.compose.animation.AnimatedContent
3+
import androidx.compose.animation.graphics.res.animatedVectorResource
4+
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
5+
import androidx.compose.animation.graphics.vector.AnimatedImageVector
46
import androidx.compose.material3.Icon
57
import androidx.compose.material3.NavigationRail
68
import androidx.compose.material3.NavigationRailItem
@@ -9,7 +11,6 @@ import androidx.compose.runtime.Composable
911
import androidx.compose.runtime.getValue
1012
import androidx.compose.runtime.remember
1113
import androidx.compose.ui.platform.LocalContext
12-
import androidx.compose.ui.res.painterResource
1314
import androidx.compose.ui.text.style.TextOverflow
1415
import androidx.compose.ui.tooling.preview.PreviewLightDark
1516
import androidx.navigation.NavDestination.Companion.hasRoute
@@ -34,20 +35,16 @@ internal fun MainNavRail(navController: NavHostController = rememberNavControlle
3435
?.hierarchy
3536
?.any { it.hasRoute(route.route::class) } == true
3637

38+
val iconRes = if (selected) route.selectedAnimatedIconRes else route.unselectedAnimatedIconRes
39+
val imageVector = AnimatedImageVector.animatedVectorResource(id = iconRes)
40+
val animatedPainter = rememberAnimatedVectorPainter(
41+
animatedImageVector = imageVector,
42+
atEnd = selected
43+
)
44+
3745
NavigationRailItem(
38-
icon = {
39-
AnimatedContent(targetState = selected) { selectedState ->
40-
val iconRes = if (selectedState) {
41-
route.selectedIconRes
42-
} else {
43-
route.unselectedIconRes
44-
}
45-
Icon(
46-
painterResource(iconRes),
47-
contentDescription = route.name,
48-
)
49-
}
50-
},
46+
icon = { Icon(painter = animatedPainter, contentDescription = route.name) },
47+
5148
label = {
5249
Text(
5350
text = route.name,

app/src/main/kotlin/com/androidvip/sysctlgui/ui/main/TopLevelRouteProvider.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,26 @@ object TopLevelRouteProvider {
1111
TopLevelRoute(
1212
name = context.getString(R.string.browse),
1313
route = UiRoute.BrowseParams,
14-
selectedIconRes = R.drawable.ic_home_filled,
15-
unselectedIconRes = R.drawable.ic_home
14+
selectedAnimatedIconRes = R.drawable.avd_home_off,
15+
unselectedAnimatedIconRes = R.drawable.avd_home_on
1616
),
1717
TopLevelRoute(
1818
name = context.getString(R.string.presets),
1919
route = UiRoute.Presets,
20-
selectedIconRes = R.drawable.ic_build_filled,
21-
unselectedIconRes = R.drawable.ic_build
20+
selectedAnimatedIconRes = R.drawable.avd_build_off,
21+
unselectedAnimatedIconRes = R.drawable.avd_build_on
2222
),
2323
TopLevelRoute(
2424
name = context.getString(R.string.favorites),
2525
route = UiRoute.Favorites,
26-
selectedIconRes = R.drawable.ic_favorite,
27-
unselectedIconRes = R.drawable.ic_favorite_outlined
26+
selectedAnimatedIconRes = R.drawable.avd_favorite_off,
27+
unselectedAnimatedIconRes = R.drawable.avd_favorite_on
2828
),
2929
TopLevelRoute(
3030
name = context.getString(R.string.settings),
3131
route = UiRoute.Settings,
32-
selectedIconRes = R.drawable.ic_settings_filled,
33-
unselectedIconRes = R.drawable.ic_settings
32+
selectedAnimatedIconRes = R.drawable.avd_settings_off,
33+
unselectedAnimatedIconRes = R.drawable.avd_settings_on
3434
)
3535
)
3636
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<animated-vector
2+
xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:aapt="http://schemas.android.com/aapt">
4+
<aapt:attr name="android:drawable">
5+
<vector
6+
android:name="vector"
7+
android:width="24dp"
8+
android:height="24dp"
9+
android:viewportWidth="960"
10+
android:viewportHeight="960">
11+
<group
12+
android:name="group"
13+
android:pivotX="360"
14+
android:pivotY="360">
15+
<path
16+
android:name="path"
17+
android:pathData="M 360 600 Q 260 600 190 530 Q 120 460 120 360 Q 120 340 123 320 Q 126 300 134 282 Q 139 272 146.5 267 Q 154 262 163 260 Q 172 258 181.5 260.5 Q 191 263 199 271 L 304 376 L 376 304 L 271 199 Q 263 191 260.5 181.5 Q 258 172 260 163 Q 262 154 267 146.5 Q 272 139 282 134 Q 300 126 320 123 Q 340 120 360 120 Q 460 120 530 190 Q 600 260 600 360 Q 600 383 596 403.5 Q 592 424 584 444 L 786 644 Q 815 673 815 715 Q 815 757 786 786 Q 757 815 715 815 Q 673 815 644 785 L 444 584 Q 424 592 403.5 596 Q 383 600 360 600 Z M 360 520 Q 386 520 412 512 Q 438 504 459 487 L 702 730 Q 707 735 715.5 734.5 Q 724 734 729 729 Q 734 724 734 715.5 Q 734 707 729 702 L 486 460 Q 504 440 512 413.5 Q 520 387 520 360 Q 520 300 481.5 255.5 Q 443 211 386 202 L 460 276 Q 472 288 472 304 Q 472 320 460 332 L 332 460 Q 320 472 304 472 Q 288 472 276 460 L 202 386 Q 211 443 255.5 481.5 Q 300 520 360 520 Z M 469 469 Z"
18+
android:fillColor="#000000"/>
19+
<path
20+
android:name="path_fill"
21+
android:pathData="M 360 600 Q 260 600 190 530 Q 120 460 120 360 Q 120 340 123 320 Q 126 300 134 282 Q 139 272 146.5 267 Q 154 262 163 260 Q 172 258 181.5 260.5 Q 191 263 199 271 L 304 376 L 376 304 L 271 199 Q 263 191 260.5 181.5 Q 258 172 260 163 Q 262 154 267 146.5 Q 272 139 282 134 Q 300 126 320 123 Q 340 120 360 120 Q 460 120 530 190 Q 600 260 600 360 Q 600 383 596 403.5 Q 592 424 584 444 L 786 644 Q 815 673 815 715 Q 815 757 786 786 Q 757 815 715 815 Q 673 815 644 785 L 444 584 Q 424 592 403.5 596 Q 383 600 360 600 Z"
22+
android:fillColor="#000000"/>
23+
</group>
24+
</vector>
25+
</aapt:attr>
26+
<target android:name="group">
27+
<aapt:attr name="android:animation">
28+
<set>
29+
<objectAnimator
30+
android:propertyName="scaleY"
31+
android:duration="250"
32+
android:valueFrom="1"
33+
android:valueTo="0.95"
34+
android:valueType="floatType"
35+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
36+
<objectAnimator
37+
android:propertyName="scaleX"
38+
android:duration="250"
39+
android:valueFrom="1"
40+
android:valueTo="0.95"
41+
android:valueType="floatType"
42+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
43+
<objectAnimator
44+
android:propertyName="rotation"
45+
android:duration="250"
46+
android:valueFrom="0"
47+
android:valueTo="20"
48+
android:valueType="floatType"
49+
android:interpolator="@android:anim/overshoot_interpolator"/>
50+
<objectAnimator
51+
android:propertyName="rotation"
52+
android:startOffset="250"
53+
android:duration="250"
54+
android:valueFrom="20"
55+
android:valueTo="0"
56+
android:valueType="floatType"
57+
android:interpolator="@android:anim/overshoot_interpolator"/>
58+
<objectAnimator
59+
android:propertyName="scaleY"
60+
android:startOffset="250"
61+
android:duration="250"
62+
android:valueFrom="0.95"
63+
android:valueTo="1"
64+
android:valueType="floatType"
65+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
66+
<objectAnimator
67+
android:propertyName="scaleX"
68+
android:startOffset="250"
69+
android:duration="250"
70+
android:valueFrom="0.95"
71+
android:valueTo="1"
72+
android:valueType="floatType"
73+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
74+
<objectAnimator
75+
android:propertyName="translateY"
76+
android:duration="250"
77+
android:valueFrom="0"
78+
android:valueTo="30"
79+
android:valueType="floatType"
80+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
81+
<objectAnimator
82+
android:propertyName="translateY"
83+
android:startOffset="250"
84+
android:duration="250"
85+
android:valueFrom="30"
86+
android:valueTo="0"
87+
android:valueType="floatType"
88+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
89+
</set>
90+
</aapt:attr>
91+
</target>
92+
<target android:name="path_fill">
93+
<aapt:attr name="android:animation">
94+
<objectAnimator
95+
android:propertyName="fillAlpha"
96+
android:startOffset="152"
97+
android:duration="348"
98+
android:valueFrom="1"
99+
android:valueTo="0"
100+
android:valueType="floatType"
101+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
102+
</aapt:attr>
103+
</target>
104+
</animated-vector>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<animated-vector
2+
xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:aapt="http://schemas.android.com/aapt">
4+
<aapt:attr name="android:drawable">
5+
<vector
6+
android:name="vector"
7+
android:width="24dp"
8+
android:height="24dp"
9+
android:viewportWidth="960"
10+
android:viewportHeight="960">
11+
<group
12+
android:name="group"
13+
android:pivotX="110"
14+
android:pivotY="360">
15+
<path
16+
android:name="path"
17+
android:pathData="M 360 600 Q 260 600 190 530 Q 120 460 120 360 Q 120 340 123 320 Q 126 300 134 282 Q 139 272 146.5 267 Q 154 262 163 260 Q 172 258 181.5 260.5 Q 191 263 199 271 L 304 376 L 376 304 L 271 199 Q 263 191 260.5 181.5 Q 258 172 260 163 Q 262 154 267 146.5 Q 272 139 282 134 Q 300 126 320 123 Q 340 120 360 120 Q 460 120 530 190 Q 600 260 600 360 Q 600 383 596 403.5 Q 592 424 584 444 L 786 644 Q 815 673 815 715 Q 815 757 786 786 Q 757 815 715 815 Q 673 815 644 785 L 444 584 Q 424 592 403.5 596 Q 383 600 360 600 Z M 360 520 Q 386 520 412 512 Q 438 504 459 487 L 702 730 Q 707 735 715.5 734.5 Q 724 734 729 729 Q 734 724 734 715.5 Q 734 707 729 702 L 486 460 Q 504 440 512 413.5 Q 520 387 520 360 Q 520 300 481.5 255.5 Q 443 211 386 202 L 460 276 Q 472 288 472 304 Q 472 320 460 332 L 332 460 Q 320 472 304 472 Q 288 472 276 460 L 202 386 Q 211 443 255.5 481.5 Q 300 520 360 520 Z M 469 469 Z"
18+
android:fillColor="#000000"/>
19+
<path
20+
android:name="path_fill"
21+
android:pathData="M 360 600 Q 260 600 190 530 Q 120 460 120 360 Q 120 340 123 320 Q 126 300 134 282 Q 139 272 146.5 267 Q 154 262 163 260 Q 172 258 181.5 260.5 Q 191 263 199 271 L 304 376 L 376 304 L 271 199 Q 263 191 260.5 181.5 Q 258 172 260 163 Q 262 154 267 146.5 Q 272 139 282 134 Q 300 126 320 123 Q 340 120 360 120 Q 460 120 530 190 Q 600 260 600 360 Q 600 383 596 403.5 Q 592 424 584 444 L 786 644 Q 815 673 815 715 Q 815 757 786 786 Q 757 815 715 815 Q 673 815 644 785 L 444 584 Q 424 592 403.5 596 Q 383 600 360 600 Z"
22+
android:fillColor="#000000"
23+
android:fillAlpha="0"/>
24+
</group>
25+
</vector>
26+
</aapt:attr>
27+
<target android:name="group">
28+
<aapt:attr name="android:animation">
29+
<set>
30+
<objectAnimator
31+
android:propertyName="scaleY"
32+
android:duration="250"
33+
android:valueFrom="1"
34+
android:valueTo="1.05"
35+
android:valueType="floatType"
36+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
37+
<objectAnimator
38+
android:propertyName="scaleX"
39+
android:duration="250"
40+
android:valueFrom="1"
41+
android:valueTo="1.05"
42+
android:valueType="floatType"
43+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
44+
<objectAnimator
45+
android:propertyName="rotation"
46+
android:duration="250"
47+
android:valueFrom="0"
48+
android:valueTo="-20"
49+
android:valueType="floatType"
50+
android:interpolator="@android:anim/overshoot_interpolator"/>
51+
<objectAnimator
52+
android:propertyName="rotation"
53+
android:startOffset="250"
54+
android:duration="250"
55+
android:valueFrom="-20"
56+
android:valueTo="0"
57+
android:valueType="floatType"
58+
android:interpolator="@android:anim/overshoot_interpolator"/>
59+
<objectAnimator
60+
android:propertyName="scaleY"
61+
android:startOffset="250"
62+
android:duration="250"
63+
android:valueFrom="1.05"
64+
android:valueTo="1"
65+
android:valueType="floatType"
66+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
67+
<objectAnimator
68+
android:propertyName="scaleX"
69+
android:startOffset="250"
70+
android:duration="250"
71+
android:valueFrom="1.05"
72+
android:valueTo="1"
73+
android:valueType="floatType"
74+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
75+
<objectAnimator
76+
android:propertyName="translateY"
77+
android:duration="250"
78+
android:valueFrom="0"
79+
android:valueTo="30"
80+
android:valueType="floatType"
81+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
82+
<objectAnimator
83+
android:propertyName="translateY"
84+
android:startOffset="250"
85+
android:duration="250"
86+
android:valueFrom="30"
87+
android:valueTo="0"
88+
android:valueType="floatType"
89+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
90+
</set>
91+
</aapt:attr>
92+
</target>
93+
<target android:name="path_fill">
94+
<aapt:attr name="android:animation">
95+
<objectAnimator
96+
android:propertyName="fillAlpha"
97+
android:startOffset="152"
98+
android:duration="348"
99+
android:valueFrom="0"
100+
android:valueTo="1"
101+
android:valueType="floatType"
102+
android:interpolator="@android:interpolator/fast_out_slow_in"/>
103+
</aapt:attr>
104+
</target>
105+
</animated-vector>

0 commit comments

Comments
 (0)