You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为Bottom Navigation菜单项设置Lottie动画图标?求可行方案

在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

火山引擎 最新活动