为何点击Preference控件时AppBarLayout的elevation会改变?
问题:点击Preference时AppBarLayout出现闪烁
我仅在Android Studio的Basic Views Activity模板中添加了一个PreferenceScreen,点击Preference时AppBarLayout会出现闪烁现象,搭配其他可点击元素时不会出现。通过快照检查发现,原因是点击Preference时elevation发生变化:向下滚动状态下,点击Preference时elevation变为0.0,再次选中时又会快速改变。

相关代码
activity_main.xml
<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:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context=".MainActivity"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" /> </com.google.android.material.appbar.AppBarLayout> <include layout="@layout/content_main" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginEnd="@dimen/fab_margin" android:layout_marginBottom="16dp" app:srcCompat="@android:drawable/ic_dialog_email" android:contentDescription="@string/fab_description" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
content_main.xml
<androidx.constraintlayout.widget.ConstraintLayout 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" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <fragment android:id="@+id/nav_host_fragment_content_main" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_graph" /> </androidx.constraintlayout.widget.ConstraintLayout>
SettingsFragment.kt
class SettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences, rootKey) } }
preferences.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <SwitchPreferenceCompat android:key="switch_preference_1" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_2" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_3" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_4" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_5" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_6" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_7" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_8" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_9" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_10" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_11" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_12" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_13" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_14" android:title="Switch Preference" android:summary="An example that uses a switch" /> <SwitchPreferenceCompat android:key="switch_preference_15" android:title="Switch Preference" android:summary="An example that uses a switch" /> </PreferenceScreen>
MainActivity.kt
class MainActivity : AppCompatActivity() { private lateinit var appBarConfiguration: AppBarConfiguration private lateinit var binding: ActivityMainBinding private var isMenuVisible = true // 默认显示菜单 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.toolbar) val navController = findNavController(R.id.nav_host_fragment_content_main) appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) navController.addOnDestinationChangedListener { _, destination, _ -> // 根据当前页面判断是否显示菜单 val shouldMenuBeVisible = destination.id != R.id.SettingsFragment if (shouldMenuBeVisible != isMenuVisible) { isMenuVisible = shouldMenuBeVisible invalidateOptionsMenu() // 触发菜单更新 } } binding.fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } } override fun onCreateOptionsMenu(menu: Menu): Boolean { // 仅在需要时加载菜单 if (isMenuVisible) { menuInflater.inflate(R.menu.menu_main, menu) } return true } override fun onPrepareOptionsMenu(menu: Menu): Boolean { super.onPrepareOptionsMenu(menu) // 清空现有菜单并重新加载 menu.clear() if (isMenuVisible) { menuInflater.inflate(R.menu.menu_main, menu) } return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.action_settings -> { findNavController(R.id.nav_host_fragment_content_main) .navigate(R.id.action_global_settingsFragment) true } else -> super.onOptionsItemSelected(item) } } override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.nav_host_fragment_content_main) return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } }
解决方案
这个问题是Preference点击事件触发了系统窗口过渡动画,导致AppBarLayout的elevation被意外修改。以下是几种可行的解决方法:
方法1:固定AppBarLayout的elevation并禁用状态动画
在AppBarLayout的XML属性中添加以下配置,强制固定elevation值,并禁用状态变化时的动画:
<com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" app:elevation="4dp" android:stateListAnimator="@null">
方法2:自定义Preference点击逻辑
在SettingsFragment中为所有Preference设置自定义点击监听器,消费掉默认事件,避免触发影响AppBar的动画:
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences, rootKey) preferenceScreen.forEach { preference -> preference.setOnPreferenceClickListener { // 处理开关逻辑(如果是SwitchPreferenceCompat) if (it is SwitchPreferenceCompat) { it.isChecked = !it.isChecked } true // 返回true表示消费事件,阻止默认行为 } } }
方法3:使用自定义Preference主题
在styles.xml中定义一个禁用焦点动画的Preference主题:
<style name="AppPreferenceTheme" parent="@style/PreferenceThemeOverlay"> <item name="android:selectableItemBackground">@null</item> <item name="android:stateListAnimator">@null</item> </style>
然后在SettingsFragment中应用该主题:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = super.onCreateView(inflater, container, savedInstanceState) view?.setTheme(R.style.AppPreferenceTheme) return view }
内容的提问来源于stack exchange,提问作者Brendan B
相关产品推荐
相关产品推荐

