You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现类似Google Trips行程页的Toolbar:滚动切换菜单图标颜色

实现CollapsingToolbar滚动时切换菜单图标颜色(仿Google Trips)

我刚帮不少开发者搞定过类似的需求,结合Google Trips的Toolbar样式,给你一套能直接复用的方案:

核心思路

CollapsingToolbarLayout滚动时,AppBarLayout会产生偏移量变化。我们可以通过监听这个偏移量,判断Toolbar的折叠状态(完全展开/部分折叠/完全折叠),然后动态切换菜单图标的颜色——展开时白色,折叠后灰色,甚至可以做渐变过渡效果。

具体实现步骤

1. 配置布局文件

首先确保你的布局里包含AppBarLayout、CollapsingToolbarLayout和Toolbar,结构大概是这样:

<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:fitsSystemWindows="true"
            app:contentScrim="@color/white"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <!-- 这里放CollapsingToolbar里的头部内容,比如背景图、标题等 -->
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:src="@drawable/trip_header"
                app:layout_collapseMode="parallax"/>

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <!-- 可滚动的内容区域,比如RecyclerView/NestedScrollView -->
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!-- 内容布局 -->
    </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

注意:CollapsingToolbarLayout的contentScrim设为白色(折叠后的背景),Toolbar的popupTheme用浅色主题,避免菜单弹窗样式冲突。

2. 监听滚动偏移,切换图标颜色

在你的Activity或Fragment里,给AppBarLayout设置偏移监听,根据折叠程度调整图标颜色:

Kotlin代码示例:

class TripActivity : AppCompatActivity() {
    private lateinit var appBarLayout: AppBarLayout
    private lateinit var toolbar: Toolbar
    private val whiteColor by lazy { ContextCompat.getColor(this, R.color.white) }
    private val grayColor by lazy { ContextCompat.getColor(this, R.color.gray_600) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_trip)

        appBarLayout = findViewById(R.id.app_bar)
        toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)

        // 初始化菜单图标为白色
        updateMenuIconColor(whiteColor)

        // 监听AppBar滚动偏移
        appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBar, verticalOffset ->
            // 计算折叠比例:verticalOffset是负数,绝对值越大折叠程度越高
            val collapseRatio = Math.abs(verticalOffset).toFloat() / appBar.totalScrollRange.toFloat()
            
            // 完全折叠时(比例接近1)切换为灰色,否则白色;也可以做渐变过渡
            if (collapseRatio >= 0.9f) { // 接近完全折叠时切换
                updateMenuIconColor(grayColor)
            } else {
                updateMenuIconColor(whiteColor)
            }

            // 如果需要渐变过渡效果,可以用这个:
            // val blendedColor = blendColors(whiteColor, grayColor, collapseRatio)
            // updateMenuIconColor(blendedColor)
        })
    }

    // 更新所有菜单图标的颜色
    private fun updateMenuIconColor(color: Int) {
        toolbar.menu?.let { menu ->
            for (i in 0 until menu.size()) {
                val menuItem = menu.getItem(i)
                menuItem.icon?.let { icon ->
                    DrawableCompat.setTint(icon, color)
                }
            }
        }
    }

    // 可选:颜色渐变混合方法
    private fun blendColors(color1: Int, color2: Int, ratio: Float): Int {
        val inverseRatio = 1f - ratio
        val r = (Color.red(color1) * inverseRatio + Color.red(color2) * ratio).toInt()
        val g = (Color.green(color1) * inverseRatio + Color.green(color2) * ratio).toInt()
        val b = (Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio).toInt()
        return Color.rgb(r, g, b)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.trip_toolbar_menu, menu)
        return true
    }
}

Java代码示例:

public class TripActivity extends AppCompatActivity {
    private AppBarLayout appBarLayout;
    private Toolbar toolbar;
    private int whiteColor;
    private int grayColor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_trip);

        appBarLayout = findViewById(R.id.app_bar);
        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        whiteColor = ContextCompat.getColor(this, R.color.white);
        grayColor = ContextCompat.getColor(this, R.color.gray_600);

        // 初始化图标为白色
        updateMenuIconColor(whiteColor);

        appBarLayout.addOnOffsetChangedListener((appBar, verticalOffset) -> {
            float collapseRatio = Math.abs(verticalOffset) / (float) appBar.getTotalScrollRange();
            if (collapseRatio >= 0.9f) {
                updateMenuIconColor(grayColor);
            } else {
                updateMenuIconColor(whiteColor);
            }
        });
    }

    private void updateMenuIconColor(int color) {
        Menu menu = toolbar.getMenu();
        if (menu != null) {
            for (int i = 0; i < menu.size(); i++) {
                MenuItem menuItem = menu.getItem(i);
                Drawable icon = menuItem.getIcon();
                if (icon != null) {
                    DrawableCompat.setTint(icon, color);
                }
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.trip_toolbar_menu, menu);
        return true;
    }
}

3. 补充说明

  • 菜单图标建议用矢量图(VectorDrawable),这样tint效果更稳定,避免出现颜色失真。
  • 如果你想实现更平滑的渐变过渡,就用上面代码里的blendColors方法,根据折叠比例动态混合白色和灰色,视觉效果更接近Google Trips的原生体验。
  • 调整collapseRatio >= 0.9f的阈值,可以控制图标切换的时机,比如在折叠到90%的时候切换颜色,你可以根据自己的Toolbar高度调整这个值。

内容的提问来源于stack exchange,提问作者pop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:34:48