I am trying to implement a settings view invoked from a drawer menu.
Settings view is implemented using a Fragment inheriting from MvxPreferenceFragmentCompat. Code is below:
[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);
}
}
My preferences.xml is shown below:
<?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>
Fragment layout is below:
<?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>
And finally, main activity XML is here:
<?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>
My preferences show up fine but I don't see the AppBar with a title (that I could set) and there is no arrow to navigate back to the Activity. When I press the back "hardware button" once, nothing happens. When I press it the second time, the app "minimizes" and when I bring it back, I am back at the main activity.
I have done this functionality with "regular" Fragments inheriting from MvxFragment but it seems like I am unable to do so with MvxPreferenceFragmentCompat.
Update 1
Based on input from Trevor, I have update fragment_settings.xml and SettingsFragment.cs as shown below:
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
}
But I am still getting null on line ParentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true) because my SupportActionBar is null (but Toolbar is not). How come?
Update 2
OK, I got a bit further. I got my AppBar and arrow back and the look and feel is like I have expected it. Still have a problem but more about it below.
What helped? This post.
In short, I needed to declare a custom style for my preference and in it, reference the layout of my preference view. Here are the bit:
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>
For me, this has been the missing link between the view declaration in the SettingsFragment and the layout resource.
The only problem that remains is ability to go back from this fragment back to main activity. From a "regular" MvxFragment (that is invoked from a drawer), I use OnClick method of the IOnClickListener to call ParentActivity.OnBackPressed().
SettingsFragment.cs
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener
{
...
public void OnClick(View v)
{
ParentActivity.OnBackPressed();
}
}
But this does not work from this MvxPreferenceFragmentCompat. Still searching for that answer.
Update 3
Last missing piece of the puzzle was the AddToBackStack declaration atop the class as shown below. Once this was in place, OnBackPressed() worked correctly.
SettingsFragment.cs
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.main_content_frame, AddToBackStack = true)]
[Register(nameof(SettingsFragment))]
public class SettingsFragment : MvxPreferenceFragmentCompat<SettingsViewModel>, View.IOnClickListener
app:MvxBind="Title Title; Subtitle Subtitle"
– Trevor Balcom