Android开发:如何随ViewPager滑动为BottomNavigationView图标实现部分着色?
嘿,这个需求我太熟了!Chrome那种滑动时底部导航图标渐变着色的效果,实现起来其实挺直观的,咱们一步步来搞定:
核心思路
通过监听ViewPager的滑动进度,计算页面切换的偏移量,然后利用颜色渐变算法,动态修改BottomNavigationView中对应图标的着色效果,实现从选中色到未选中色的平滑过渡。
具体实现步骤
1. 准备支持着色的图标资源
注意:图标必须是支持着色的资源,比如VectorDrawable或者纯白色的PNG(带透明通道),否则后续的颜色过滤不会生效。你可以在res/drawable下放置矢量图标,或者确保PNG图标本身是无底色的。
2. 基础交互关联
先把ViewPager和BottomNavigationView的基础联动做好:
- 点击BottomNavigationView的菜单项时,ViewPager切换到对应页面
- ViewPager切换页面时,BottomNavigationView选中对应菜单项
代码示例(Kotlin + ViewPager2)
// 关联底部导航点击事件 bottomNavigationView.setOnItemSelectedListener { menuItem -> val targetPosition = bottomNavigationView.menu.indexOf(menuItem) viewPager.currentItem = targetPosition true } // 关联ViewPager页面选中事件 viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) bottomNavigationView.selectedItemId = bottomNavigationView.menu.getItem(position).itemId // 页面选中时重置所有图标颜色,避免过渡残留 resetBottomNavIcons() } })
3. 实现滑动时的图标渐变着色
核心是监听ViewPager的滑动偏移量,用ArgbEvaluator计算过渡颜色,给当前和下一个菜单项的图标设置动态颜色过滤。
新增颜色过渡处理函数
private fun updateBottomNavIcons(currentPosition: Int, offset: Float) { val selectedColor = ContextCompat.getColor(this, R.color.bottom_nav_selected) val unselectedColor = ContextCompat.getColor(this, R.color.bottom_nav_unselected) val colorEvaluator = ArgbEvaluator() // 处理当前页面的图标:从选中色过渡到未选中色 val currentMenuItem = bottomNavigationView.menu.getItem(currentPosition) val currentTransitionColor = colorEvaluator.evaluate(offset, selectedColor, unselectedColor) as Int // 必须mutate(),避免共享Drawable实例导致所有图标颜色同步变化 val currentIcon = currentMenuItem.icon.mutate() currentIcon.colorFilter = PorterDuffColorFilter(currentTransitionColor, PorterDuff.Mode.SRC_IN) currentMenuItem.icon = currentIcon // 处理下一个页面的图标:从未选中色过渡到选中色(如果存在下一页) if (currentPosition < bottomNavigationView.menu.size() - 1) { val nextMenuItem = bottomNavigationView.menu.getItem(currentPosition + 1) val nextTransitionColor = colorEvaluator.evaluate(offset, unselectedColor, selectedColor) as Int val nextIcon = nextMenuItem.icon.mutate() nextIcon.colorFilter = PorterDuffColorFilter(nextTransitionColor, PorterDuff.Mode.SRC_IN) nextMenuItem.icon = nextIcon } } // 重置所有图标到正确的选中/未选中状态 private fun resetBottomNavIcons() { val selectedColor = ContextCompat.getColor(this, R.color.bottom_nav_selected) val unselectedColor = ContextCompat.getColor(this, R.color.bottom_nav_unselected) for (i in 0 until bottomNavigationView.menu.size()) { val menuItem = bottomNavigationView.menu.getItem(i) val targetColor = if (i == bottomNavigationView.selectedItemPosition) selectedColor else unselectedColor val icon = menuItem.icon.mutate() icon.colorFilter = PorterDuffColorFilter(targetColor, PorterDuff.Mode.SRC_IN) menuItem.icon = icon } }
绑定滑动监听
在之前的OnPageChangeCallback中添加滑动监听:
override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int ) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) // 实时更新图标颜色 updateBottomNavIcons(position, positionOffset) }
4. 适配旧版ViewPager(可选)
如果你用的是旧版ViewPager而非ViewPager2,只需把监听换成OnPageChangeListener即可:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { updateBottomNavIcons(position, positionOffset); } @Override public void onPageSelected(int position) { bottomNavigationView.setSelectedItemId(bottomNavigationView.getMenu().getItem(position).getItemId()); resetBottomNavIcons(); } @Override public void onPageScrollStateChanged(int state) {} });
额外提示
- 如果你还想给底部导航的文字也加上渐变效果,逻辑和图标完全一致,只需修改
TextView的setTextColor即可 - 可以根据需求调整过渡的缓动效果,比如给
offset加上动画插值器,让渐变更自然
内容的提问来源于stack exchange,提问作者Soheil
相关产品推荐
相关产品推荐

