带有自定义布局的 PreferenceFragmentCompat 在 API 上找不到工具栏 28+
PreferenceFragmentCompat with custom layout not finding toolbar on API 28+
我有一个 activity 应用程序,它使用带有自定义布局(通过覆盖 PreferenceThemeOverlay
样式设置)的设置片段 (PreferenceFragmentCompat
)。从片段的 onViewCreated
方法中,我使用 setSupportActionBar
来引用该布局中存在的工具栏。在 API 28+ 上运行之前,它工作正常,此时对 toolbarSettings
的引用为 null 并引发错误。
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.app.ActionBar.setTitle(java.lang.CharSequence)' on a null object reference
at androidx.navigation.ui.ActionBarOnDestinationChangedListener.setTitle(ActionBarOnDestinationChangedListener.java:48)
at androidx.navigation.ui.AbstractAppBarOnDestinationChangedListener.onDestinationChanged(AbstractAppBarOnDestinationChangedListener.java:104)
at androidx.navigation.NavController.addOnDestinationChangedListener(NavController.java:204)
at androidx.navigation.ui.NavigationUI.setupActionBarWithNavController(NavigationUI.java:228)
at androidx.navigation.ui.ActivityKt.setupActionBarWithNavController(Activity.kt:74)
at androidx.navigation.ui.ActivityKt.setupActionBarWithNavController$default(Activity.kt:89)
at com.simplenotes.notes.presentation.ui.SettingsFragment.setupActionBar(SettingsFragment.kt:39)
at com.simplenotes.notes.presentation.ui.SettingsFragment.onViewCreated(SettingsFragment.kt:20)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:332)
我在这个版本中看到了对自定义首选项片段的更改,但是看不到任何特定于此行为的内容。有谁知道解决方案或可以指出哪里做错了吗?
SettingsFragment.kt
class SettingsFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupActionBar()
}
private fun setupActionBar() {
setHasOptionsMenu(true)
val hostActivity = requireActivity() as AppCompatActivity
hostActivity.setSupportActionBar(toolbarSettings)
hostActivity.setupActionBarWithNavController(findNavController())
val actionBar = hostActivity.supportActionBar
actionBar?.title = resources.getString(R.string.title_settings)
actionBar?.setDisplayHomeAsUpEnabled(true)
}
}
fragment_settings.xml
<LinearLayout 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"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbarSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbarSettings"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
styles.xml
<resources>
<style name="Theme.MaterialComponents.DayNight.NoActionBar.Bridge" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge" />
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
<item name="preferenceTheme">@style/AppTheme.PreferenceThemeOverlay</item>
</style>
<style name="AppTheme.PreferenceThemeOverlay" parent="PreferenceThemeOverlay">
<item name="android:layout">@layout/fragment_settings</item>
</style>
</resources>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.simplenotes.notes">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".presentation.ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
依赖关系
implementation 'androidx.preference:preference:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
万一其他人遇到这个问题我发现了问题。
除了问题中的 styles.xml
文件外,还有一个 styles-v27.xml
文件用于覆盖 windowLightNavigationBar 属性...
<resources>
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>
在 API v28 上,但这意味着 preferenceTheme 值未被提取。当设置片段膨胀时,它实际上没有自定义布局,因此没有创建工具栏。
在 styles-v27.xml
中,我只是像下面那样重复了 preferenceTheme 属性,嘿,它在 v28 及更高版本上工作正常。
<resources>
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
<item name="android:windowLightNavigationBar">false</item>
<item name="preferenceTheme">@style/AppTheme.PreferenceThemeOverlay</item>
</style>
</resources>
我有一个 activity 应用程序,它使用带有自定义布局(通过覆盖 PreferenceThemeOverlay
样式设置)的设置片段 (PreferenceFragmentCompat
)。从片段的 onViewCreated
方法中,我使用 setSupportActionBar
来引用该布局中存在的工具栏。在 API 28+ 上运行之前,它工作正常,此时对 toolbarSettings
的引用为 null 并引发错误。
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.app.ActionBar.setTitle(java.lang.CharSequence)' on a null object reference
at androidx.navigation.ui.ActionBarOnDestinationChangedListener.setTitle(ActionBarOnDestinationChangedListener.java:48)
at androidx.navigation.ui.AbstractAppBarOnDestinationChangedListener.onDestinationChanged(AbstractAppBarOnDestinationChangedListener.java:104)
at androidx.navigation.NavController.addOnDestinationChangedListener(NavController.java:204)
at androidx.navigation.ui.NavigationUI.setupActionBarWithNavController(NavigationUI.java:228)
at androidx.navigation.ui.ActivityKt.setupActionBarWithNavController(Activity.kt:74)
at androidx.navigation.ui.ActivityKt.setupActionBarWithNavController$default(Activity.kt:89)
at com.simplenotes.notes.presentation.ui.SettingsFragment.setupActionBar(SettingsFragment.kt:39)
at com.simplenotes.notes.presentation.ui.SettingsFragment.onViewCreated(SettingsFragment.kt:20)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:332)
我在这个版本中看到了对自定义首选项片段的更改,但是看不到任何特定于此行为的内容。有谁知道解决方案或可以指出哪里做错了吗?
SettingsFragment.kt
class SettingsFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupActionBar()
}
private fun setupActionBar() {
setHasOptionsMenu(true)
val hostActivity = requireActivity() as AppCompatActivity
hostActivity.setSupportActionBar(toolbarSettings)
hostActivity.setupActionBarWithNavController(findNavController())
val actionBar = hostActivity.supportActionBar
actionBar?.title = resources.getString(R.string.title_settings)
actionBar?.setDisplayHomeAsUpEnabled(true)
}
}
fragment_settings.xml
<LinearLayout 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"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbarSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbarSettings"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
styles.xml
<resources>
<style name="Theme.MaterialComponents.DayNight.NoActionBar.Bridge" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge" />
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
<item name="preferenceTheme">@style/AppTheme.PreferenceThemeOverlay</item>
</style>
<style name="AppTheme.PreferenceThemeOverlay" parent="PreferenceThemeOverlay">
<item name="android:layout">@layout/fragment_settings</item>
</style>
</resources>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.simplenotes.notes">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".presentation.ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
依赖关系
implementation 'androidx.preference:preference:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
万一其他人遇到这个问题我发现了问题。
除了问题中的 styles.xml
文件外,还有一个 styles-v27.xml
文件用于覆盖 windowLightNavigationBar 属性...
<resources>
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>
在 API v28 上,但这意味着 preferenceTheme 值未被提取。当设置片段膨胀时,它实际上没有自定义布局,因此没有创建工具栏。
在 styles-v27.xml
中,我只是像下面那样重复了 preferenceTheme 属性,嘿,它在 v28 及更高版本上工作正常。
<resources>
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge">
<item name="android:windowLightNavigationBar">false</item>
<item name="preferenceTheme">@style/AppTheme.PreferenceThemeOverlay</item>
</style>
</resources>