问题
I'm trying to learn ways to build preference pages in the Xamarin Android application. I found a lot of examples with PreferenceFragment but it was marked as deprecated and it is difficult for me to rewrite them on the current stage.
I've created activity to represent headers. I added IntentFilter so I can access this activity from apps list in the settings menu. Also it has internal class to group some preferences together:
namespace droid.examples.Preferences
{
[Activity(Label = "Settings activity", Theme = "@style/AppTheme", Name = "droid.examples.Preferences.SettingsActivity")]
[IntentFilter(new string[] { "android.intent.action.APPLICATION_PREFERENCES" })]
public class SettingsActivity : PreferenceActivity
{
public override void OnBuildHeaders(IList<Header> target)
{
base.OnBuildHeaders(target);
LoadHeadersFromResource(Resource.Xml.preference_headers, target);
}
public class SettingsFragment : PreferenceFragmentCompat
{
public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
{
// Load the Preferences from the XML file
SetPreferencesFromResource(Resource.Xml.app_preferences, rootKey);
}
}
}
}
My app_preferences.xml which I can't open by selecting "Prefs 1" header from preference_headers.xml:
<?xml version="1.0" encoding="utf-8" ?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Category">
<CheckBoxPreference
android:key="checkbox_preference"
android:title="Developer mode"
android:summary="Allow user to see detailed messages" />
</PreferenceCategory>
</PreferenceScreen>
I have preference_headers.xml. It opens when I click on gear wheel near application name. It looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="droid.examples.Preferences.SettingsActivity.SettingsFragment"
android:title="Prefs 1"
android:summary="An example of some preferences." />
</preference-headers>
My package name: droid.examples
I think that one problem related to the android:fragment attribute value. What is the rules to build that value?
I suppose that it must start from 'package name'. Should it contain namespace between class name and package name?
What does $ mean in the attribute value? Is it used to mark internal class? I saw in the several places next code:
android:fragment="com.example.android.apis.preference.PreferenceWithHeaders$Prefs1Fragment"
I hope you can help me find where I made a mistakes.
Source code from GitHub
回答1:
I spend a lot of time to investigate that issue and I want to make a summary.
We have to override IsValidFragment method in the SettingsActivity:
protected override bool IsValidFragment(string fragmentName)
{
return fragmentName == "droid.examples.preferences.SettingsActivity.SettingsFragment";
}
My SettingsActivity extends PreferenceActivity. Thanks to @Jeremy advice about implementation of IOnPreferenceStartFragmentCallback I find out that base class already extends it.
public abstract class PreferenceActivity ...
{
...
public virtual bool OnPreferenceStartFragment(PreferenceFragment caller, Preference pref);
...
}
So, I probably need to use PreferenceFragment instead of PreferenceFragmentCompat to make code consistent:
public class SettingsFragment : PreferenceFragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
AddPreferencesFromResource(Resource.Xml.app_preferences_for_header);
}
}
Also we have to add Register attribute to our fragment:
[Register("droid.examples.preferences.SettingsActivity.SettingsFragment")]
public class SettingsFragment : PreferenceFragment
{
}
Finally I updated preference_headers.xml
<?xml version="1.0" encoding="utf-8" ?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="droid.examples.preferences.SettingsActivity.SettingsFragment"
android:title="Prefs 1"
android:summary="An example of some preferences." />
</preference-headers>
android:fragment attribute value can contains '$' but '+' won't work because Register doesn't support it and we will get compilation error.
Thanks everyone who tried to help me
回答2:
Looks like the string you provide is just a message to send to your parent activity. Your parent activity is responsible for instantiating the right Fragment, and performing the ceremony to render it.
The platform docs seem to indicate as such:
When a user taps a Preference with an associated Fragment, the interface method PreferenceFragmentCompat.OnPreferenceStartFragmentCallback.onPreferenceStartFragment() is called
At time of writing, there's a code snippet on that page, which I've translated for my own project more-or-less as follows:
// This has to go in the activity which hosts the PreferenceFragment
// which owns the Preference that has the `android:fragment` attribute.
using Android.Support.V7.Preferences;
using Android.Support.V4.App;
partial class MyActivity :
PreferenceFragmentCompat.IOnPreferenceStartFragmentCallback
{
Fragment GetFragmentForPrefString(string prefFragment)
{
// you implement this
}
const int fragmentContainerId = Resource.Id.whatever;
public bool OnPreferenceStartFragment(
PreferenceFragmentCompat caller, Preference pref)
{
string prefString = pref.Fragment;
var transaction = SupportFragmentManager.BeginTransaction();
transaction.Replace(fragmentContainerId,
GetFragmentForPrefString(prefString));
// you'll probably also want to add it to the back stack,
// but it's not strictly necessary I guess.
transaction.Commit();
return true;
}
}
Their sample involves the Java API method getSupportFragmentManager().getFragmentFactory() which doesn't appear to be part of the V28 Xamarin support NuGet packages. But honestly I'm not sure why that level of indirection is necessary; I'd suggest you simply implement something like
switch (prefFragmentName)
{
case "Fragment 1":
return new Fragment1();
// etc
来源:https://stackoverflow.com/questions/54556298/how-to-use-preferencefragmentcompat-in-the-preference-header