ActionBar 中带有向上导航(向后箭头)的 MvxPreferenceFragmentCompat

MvxPreferenceFragmentCompat with Up Navigation (back arrow) in ActionBar

我正在尝试实现从抽屉菜单调用的设置视图。

设置视图是使用继承自 MvxPreferenceFragmentCompat 的 Fragment 实现的。代码如下:

[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame)]
[Register(nameof(SettingsFragment))]
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel> 
{
    public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
    {
        SetPreferencesFromResource(Resource.Xml.preferences, rootKey);
    }
}

我的preferences.xml如下图:

<?xml version="1.0" encoding="utf-8" ?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/pref_debug_info_title"
        android:key="pref_key_debug_info">
        <CheckBoxPreference
            android:key="pref_key_provide_debug_info"
            android:title="@string/pref_title_provide_debug_info"
            android:summary="@string/pref_summary_provide_debug_info"
            android:defaultValue="false" />
        <CheckBoxPreference
            android:key="pref_key_provide_debug_info_over_wifi"
            android:title="@string/pref_title_debug_info_over_wifi"
            android:summary="@string/pref_summary_debug_info_over_wifi"
            android:defaultValue="true" />
    </PreferenceCategory>
</PreferenceScreen>

片段布局如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:id="@+id/fragment_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        layout="@layout/include_toolbar_actionbar_addlistitem" />

    <fragment
        android:name="SettingsFragment"
        android:id="@+id/settings_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <include
        layout="@layout/include_floatingactionbutton" />

</android.support.design.widget.CoordinatorLayout>

最后,主要 activity XML 在这里:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true">

    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_content_frame"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true" />

    <FrameLayout
      android:id="@+id/navigation_frame"
      android:layout_height="match_parent"
      android:layout_width="wrap_content"
      android:layout_gravity="left|start" />

</android.support.v4.widget.DrawerLayout>

我的首选项显示正常,但我没有看到带有标题(我可以设置)的 AppBar,也没有导航回 Activity 的箭头。当我按下 "hardware button" 一次后退时,没有任何反应。当我第二次按下它时,应用程序 "minimizes" 当我把它带回来时,我又回到了主界面 activity.

我已经使用从 MvxFragment 继承的 "regular" 片段完成了此功能,但似乎我无法使用 MvxPreferenceFragmentCompat 完成此功能。

更新 1

根据 Trevor 的意见,我更新了 fragment_settings.xml 和 SettingsFragment.cs,如下所示:

fragment_settings.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:id="@+id/fragment_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        layout="@layout/include_toolbar_actionbar" />

    <!-- Required ViewGroup for PreferenceFragmentCompat -->
    <FrameLayout
        android:id="@android:id/list_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        local:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <include
        layout="@layout/include_floatingactionbutton" />

</android.support.design.widget.CoordinatorLayout>

SettingsFragment.cs

[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame)]
[Register(nameof(SettingsFragment))]
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener
{
    #region properties

    protected Toolbar Toolbar { get; set; }
    protected AppCompatActivity ParentActivity { get; set; }

    #endregion

    #region Fragment lifecycle overrides

    public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
    {
        AddPreferencesFromResource(Resource.Xml.preferences);
    }

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        var view = base.OnCreateView(inflater, container, savedInstanceState);

        ParentActivity = ((MainActivity)Activity);

        Toolbar = view.FindViewById<Toolbar>(Resource.Id.main_tool_bar);
        ParentActivity.SetSupportActionBar(Toolbar);
        ParentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true);
        ParentActivity.SupportActionBar.SetDisplayShowHomeEnabled(true);

        ...

        // TODO: Pull it from a resource
        Toolbar.Title = Resources.GetText(Resource.String.settings_view_title);

        return view;
    }

    #endregion

    #region View.IOnClickListener implementation

    public void OnClick(View v)
    {
        ParentActivity.OnBackPressed(); 
    }

    #endregion
}

但是我在行 ParentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true) 上仍然得到 null,因为我的 SupportActionBar 是 null(但 Toolbar 不是)。怎么来的?

更新 2

好的,我说得更远了。我得到了我的 AppBar 和向后箭头,外观和感觉就像我预期的那样。仍然有问题,但下面会详细介绍。

有什么帮助? This post.

简而言之,我需要为我的偏好声明一个自定义样式,并在其中引用我的偏好视图的布局。这是位:

Styles.xml

<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="preferenceTheme">@style/AppTheme.Preference</item>
</style>

<!-- Custom Preference Theme -->
<style name="AppTheme.Preference"
       parent="@style/PreferenceThemeOverlay.v14.Material">
    <item name="preferenceFragmentCompatStyle">
        @style/AppPreferenceFragmentCompatStyle
    </item>
</style>

<!-- Custom Style for PreferenceFragmentCompat -->
<style name="AppPreferenceFragmentCompatStyle"
        parent="@style/PreferenceFragment.Material">
    <item name="android:layout">@layout/fragment_settings</item>
</style>

对我来说,这是 SettingsFragment 中的视图声明和布局资源之间缺少的 link。

剩下的唯一问题是能够从该片段返回到主要 activity。从 "regular" MvxFragment(从抽屉中调用),我使用 IOnClickListener 的 OnClick 方法调用 ParentActivity.OnBackPressed()。

SettingsFragment.cs

public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener
{
    ...
    public void OnClick(View v)
    {            
        ParentActivity.OnBackPressed(); 
    }
}

但这不适用于此 MvxPreferenceFragmentCompat。仍在寻找答案。

更新 3

最后一块缺失的拼图是 class 顶部的 AddToBackStack 声明,如下所示。一旦到位,OnBackPressed() 就会正常工作。

SettingsFragment.cs

[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame, AddToBackStack = true)]
[Register(nameof(SettingsFragment))]
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener

您缺少一些设置 AppCompatActivity.SupportActionBar 的代码,就像在任何其他片段中一样。这是我解决问题的方法:

这是 SettingsFragment 布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.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_height="match_parent"
  android:layout_width="match_parent"
  android:fitsSystemWindows="true"
  tools:context=".Activities.MainActivity">
  <include
    layout="@layout/toolbar_actionbar" />
  <!-- Required ViewGroup for PreferenceFragmentCompat -->
  <FrameLayout
    android:id="@android:id/list_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>

这里是 SettingsFragment 实现:

public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>,
    View.IOnClickListener
{
    private Toolbar _toolbar;
    private View _view;

    private MainActivity MainActivity => (MainActivity)Activity;

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        // Since the view is created in PreferenceFragmentCompat's OnCreateView we don't use BindingInflate like a typical MvxFragment.
        _view = base.OnCreateView(inflater, container, savedInstanceState);

        // TODO: Setup MvvmCross Databinding manually since we didn't use BindingInflate like a typical MvxFragment.

        _toolbar = _view.FindViewById<Toolbar>(Resource.Id.toolbar);
        if (_toolbar != null)
        {
            MainActivity.SetSupportActionBar(_toolbar);
            MainActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true);
            MainActivity.SupportActionBar.SetDisplayShowHomeEnabled(true);
            _toolbar.SetNavigationOnClickListener(this);

            // TODO: Bind the Toolbar.Title
        }
        return _view;
    }

    public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
    {
        AddPreferencesFromResource(Resource.Xml.preferences);
    }

    public async void OnClick(View v)
    {
        // Toolbar was clicked
        await ViewModel.CloseCommand.ExecuteAsync().ConfigureAwait(false);
    }
}