从Fragment B返回时CollapsingToolbarLayout显示异常问题
问题:从Fragment B返回时CollapsingToolbarLayout出现闪烁异常
Fragment A包含CollapsingToolbarLayout,页面滚动时顶部Toolbar会折叠。点击页面底部按钮跳转到Fragment B,从B返回A时,B会从导航栈移除,A会回到之前滚动到底部的位置,但此时本该隐藏的Toolbar会先显示再折叠,出现闪烁异常。
Fragment A布局代码
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/coordinator" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true"> <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" style="?attr/collapsingToolbarLayoutLargeStyle" android:layout_width="match_parent" android:layout_height="280dp" android:fitsSystemWindows="true" app:contentScrim="?android:colorBackground" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" app:scrimVisibleHeightTrigger="70dp" app:statusBarScrim="?android:colorBackground"> <View android:layout_width="match_parent" android:layout_height="300dp" android:background="#ff0000" android:fitsSystemWindows="true" /> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/topAppBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:elevation="0dp" android:fitsSystemWindows="false" app:layout_collapseMode="pin" app:layout_scrollFlags="scroll|enterAlways" app:navigationIcon="@drawable/chevron_back" app:navigationIconTint="#00ff00" /> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.core.widget.NestedScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" android:fillViewport="true" android:orientation="vertical" android:paddingHorizontal="@dimen/content_padding" android:paddingTop="0dp" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/short_description" style="@style/body_gray" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginVertical="8dp" android:text="@string/lorem_ipsum" /> <Button android:id="@+id/next" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginVertical="8dp" android:text="Next" /> </LinearLayout> </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout>
Fragment A代码
class FragmentA: Fragment() { private var _binding: FragmentABinding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentABinding.inflate(inflater, container, false) binding.topAppBar.setNavigationOnClickListener { findNavController().popBackStack() } return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.next.setOnClickListener { findNavController().navigate(R.id.action_A_to_B) } } }
导航动作配置
<action android:id="@+id/action_A_to_B" app:destination="@id/fragmentb" app:enterAnim="@anim/slide_in" app:exitAnim="@anim/slide_out" app:popEnterAnim="@anim/pop_in" app:popExitAnim="@anim/pop_out" app:launchSingleTop="true"/>
解决方案
方案1:手动保存并恢复AppBar滚动状态
在Fragment A中监听AppBar偏移量,返回时直接恢复状态,避免过渡动画期间的状态异常:
private var appBarOffset = 0 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 记录AppBar的滚动偏移 binding.appbar.addOnOffsetChangedListener { _, offset -> appBarOffset = offset } // 监听从B返回的事件,恢复AppBar状态 findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Boolean>("backFromB")?.observe(viewLifecycleOwner) { binding.appbar.setExpanded(false, false) binding.appbar.setSelectedTabScrollPosition(appBarOffset) } binding.next.setOnClickListener { findNavController().navigate(R.id.action_A_to_B) } }
同时在Fragment B的返回逻辑中传递状态通知:
// Fragment B的返回按钮点击事件 binding.back.setOnClickListener { findNavController().previousBackStackEntry?.savedStateHandle?.set("backFromB", true) findNavController().popBackStack() }
方案2:移除CollapsingToolbarLayout的snap标志
snap标志会让AppBar自动调整到完全展开或折叠状态,过渡动画时容易引发闪烁,移除后可避免该问题:
app:layout_scrollFlags="scroll|exitUntilCollapsed"
方案3:在Resume时强制AppBar保持折叠状态
在Fragment A的onResume方法中,强制AppBar处于折叠状态,跳过过渡动画:
override fun onResume() { super.onResume() binding.appbar.post { binding.appbar.setExpanded(false, false) } }
内容的提问来源于stack exchange,提问作者P1x
相关产品推荐
相关产品推荐

