如何为Bottom Navigation菜单项设置Lottie动画图标?求可行方案
当然有可行的解决方案!我帮你整理了两种常用的实现方式,完美解决用Lottie动画替换Bottom Navigation菜单项图标的需求。
方案一:借助DataBinding的Binding Adapter实现
如果你的项目已经启用DataBinding,这种方式会很灵活:
1. 先添加Lottie依赖
在你的build.gradle(Module级别)中加入最新版Lottie依赖:
implementation "com.airbnb.android:lottie:6.4.0"
2. 编写Binding Adapter
创建一个Binding Adapter类,用来替换BottomNavigationItemView中的图标为Lottie动画:
import android.view.ViewGroup import android.widget.ImageView import androidx.databinding.BindingAdapter import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieDrawable import com.google.android.material.bottomnavigation.BottomNavigationItemView @BindingAdapter("app:lottieRawRes") fun BottomNavigationItemView.setLottieAnimation(rawRes: Int) { // 找到默认的图标ImageView val iconView = findViewById<ImageView>(com.google.android.material.R.id.icon) ?: return // 移除原有ImageView,替换为LottieAnimationView val parent = iconView.parent as ViewGroup parent.removeView(iconView) val lottieView = LottieAnimationView(context).apply { layoutParams = iconView.layoutParams setAnimation(rawRes) repeatCount = LottieDrawable.INFINITE // 设置无限循环 playAnimation() // 处理选中/未选中状态的动画控制 this@setLottieAnimation.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { speed = 1f playAnimation() } else { speed = 0f pauseAnimation() } } } parent.addView(lottieView) }
3. 自定义Bottom Navigation Item布局
创建一个自定义的item布局layout/bottom_nav_item.xml:
<?xml version="1.0" encoding="utf-8"?> <com.google.android.material.bottomnavigation.BottomNavigationItemView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="match_parent" app:lottieRawRes="@{@raw/my_file}" <!-- 这里绑定你的Lottie资源 --> app:itemTextAppearanceActive="@style/TextAppearance.Material3.Caption" app:itemTextAppearanceInactive="@style/TextAppearance.Material3.Caption" app:itemIconTint="@color/bottom_nav_icon_tint" app:itemTextColor="@color/bottom_nav_text_color"/>
4. 配置Menu和主布局
在你的menu文件(比如menu/bottom_nav_menu.xml)中设置自定义布局:
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/my_navigation" android:title="@string/my_text" android:icon="@drawable/my_icon" <!-- 保留默认图标作为占位,实际会被Lottie替换 --> app:itemLayout="@layout/bottom_nav_item"/> </menu>
最后在主布局中引用BottomNavigationView:
<com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_nav" android:layout_width="match_parent" android:layout_height="wrap_content" app:menu="@menu/bottom_nav_menu"/>
方案二:自定义BottomNavigationView实现
如果不想用DataBinding,自定义View的方式更直接可控:
1. 创建自定义LottieBottomNavigationView
import android.content.Context import android.util.AttributeSet import android.widget.ImageView import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieDrawable import com.google.android.material.bottomnavigation.BottomNavigationView class LottieBottomNavigationView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : BottomNavigationView(context, attrs, defStyleAttr) { init { // 遍历所有菜单项,替换图标为Lottie动画 menu.forEach { menuItem -> // 通过itemId找到对应的BottomNavigationItemView val itemView = findViewById<View>(menuItem.itemId) ?: return@forEach val iconView = itemView.findViewById<ImageView>(com.google.android.material.R.id.icon) ?: return@forEach replaceIconWithLottie(iconView, menuItem) } // 监听选中状态变化,控制动画播放/暂停 setOnItemSelectedListener { item -> val itemView = findViewById<View>(item.itemId) ?: return@setOnItemSelectedListener true val lottieView = itemView.findViewById<LottieAnimationView>(R.id.lottie_icon) ?: return@setOnItemSelectedListener true if (item.isChecked) { lottieView.playAnimation() } else { lottieView.pauseAnimation() } true } } private fun replaceIconWithLottie(iconView: ImageView, menuItem: MenuItem) { val parent = iconView.parent as android.view.ViewGroup val index = parent.indexOfChild(iconView) parent.removeView(iconView) // 从menuItem的tag中获取Lottie资源ID(需要提前在menu中设置tag) val lottieResId = menuItem.getTag(R.id.lottie_res) as? Int ?: return val lottieView = LottieAnimationView(context).apply { id = R.id.lottie_icon layoutParams = iconView.layoutParams setAnimation(lottieResId) repeatCount = LottieDrawable.INFINITE pauseAnimation() // 默认未选中时暂停 } parent.addView(lottieView, index) } }
2. 在menu中设置Lottie资源的Tag
先在values/ids.xml中定义tag的ID:
<item name="lottie_res" type="id"/>
然后在menu文件中给每个需要Lottie的item设置tag:
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/my_navigation" android:title="@string/my_text" android:icon="@drawable/my_icon" android:tag="@raw/my_file"/> <!-- 绑定Lottie资源 --> </menu>
3. 在布局中使用自定义View
<com.yourpackage.LottieBottomNavigationView android:id="@+id/bottom_nav" android:layout_width="match_parent" android:layout_height="wrap_content" app:menu="@menu/bottom_nav_menu"/>
一些注意事项
- Lottie动画资源建议控制大小:避免过大的JSON文件影响性能
- 状态控制:根据选中/未选中状态调整动画的播放/暂停,提升用户体验
- 兼容性:确保Lottie版本与你的Material Components版本兼容
内容的提问来源于stack exchange,提问作者mo22sen




