我正在尝试学习在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"
我希望你能帮助我找到我犯错误的地方。
我花了很多时间来调查这个问题,我想做一个总结。
我们必须在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不支持它,我们将得到编译错误。
谢谢所有试图帮助我的人
看起来您提供的字符串只是要发送到父活动的消息。您的父活动负责实例化正确的Fragment
,并执行仪式以呈现它。
platform docs似乎表明如下:
当用户使用关联的片段点击Preference时,将调用接口方法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