如何在首选项标题中使用PreferenceFragmentCompat

时间:2019-02-06 14:47:45

标签: android xamarin xamarin.android

我正在尝试学习在Xamarin Android应用程序中构建首选项页面的方法。 我在PreferenceFragment中发现了很多示例,但已将其标记为已弃用,因此在当前阶段很难重写它们。

我创建了表示标题的活动。我添加了IntentFilter,以便可以从设置菜单中的应用程序列表访问此活动。它还具有内部类,可以将一些首选项组合在一起:

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);
            }
        }
    }
}

我的app_preferences.xml无法通过从preference_headers.xml中选择“ Prefs 1”标题来打开:

<?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>

我有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>

我的包裹名称:droid.examples

我认为与android:fragment属性值有关的一个问题。 建立该价值的规则是什么?

我想它必须以“程序包名称”开头。它应该在类名和包名之间包含名称空间吗?

$在属性值中表示什么?它用于标记内部类吗?我在几个地方看到了下一个代码:

android:fragment="com.example.android.apis.preference.PreferenceWithHeaders$Prefs1Fragment"

希望您能帮助我找到我犯错的地方。

Source code from GitHub

2 个答案:

答案 0 :(得分:1)

我花了很多时间来调查这个问题,我想作个总结。

我们必须在SettingsActivity中重写IsValidFragment方法:

protected override bool IsValidFragment(string fragmentName)
{
    return fragmentName == "droid.examples.preferences.SettingsActivity.SettingsFragment";
}

我的SettingsActivity扩展了PreferenceActivity。感谢@Jeremy关于实现IOnPreferenceStartFragmentCallback的建议,我发现基类已经对其进行了扩展。

public abstract class PreferenceActivity ...
{
    ...
    public virtual bool OnPreferenceStartFragment(PreferenceFragment caller, Preference pref);
    ...
}

因此,我可能需要使用PreferenceFragment而不是PreferenceFragmentCompat来使代码一致:

public class SettingsFragment : PreferenceFragment
{
    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        AddPreferencesFromResource(Resource.Xml.app_preferences_for_header);
    }
}

我们还必须在片段中添加Register属性:

[Register("droid.examples.preferences.SettingsActivity.SettingsFragment")]
public class SettingsFragment : PreferenceFragment
{
}

最后,我更新了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属性值可以包含'$',但'+'将不起作用,因为Register不支持它,并且我们会得到编译错误。

感谢所有试图帮助我的人

答案 1 :(得分:0)

看起来您提供的字符串只是发送给您的父级活动的消息。您的家长活动负责实例化正确的Fragment,并执行仪式来呈现它。

platform docs似乎表示这样:

  

当用户点击带有关联片段的首选项时,将调用接口方法PreferenceFragmentCompat.OnPreferenceStartFragmentCallback.onPreferenceStartFragment()

在撰写本文时,该页面上有一个代码段,我为自己的项目翻译了以下代码:

// 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;
   }
}

他们的示例涉及Java API方法getSupportFragmentManager().getFragmentFactory(),该方法似乎不属于V28 Xamarin支持的NuGet软件包。但老实说,我不确定为什么必须使用这种间接级别。我建议您只是实现类似

switch (prefFragmentName)
{
   case "Fragment 1":
      return new Fragment1();
// etc