对于从2.X到4.X的所有Android版本,我如何使用相同的首选项屏幕?

ecv*_*ecv 13 compatibility android backwards-compatibility android-preferences

注意:请节省一些时间并参考接受的答案,无需阅读所有问题.
您可以阅读问题的其余部分以及我提供的替代(尽管不太复杂)方法的答案.
此外,您可能希望通过将相关的代码片段添加到您的首选项活动类中来利用Android 2.X中背景故障的修复程序.

背景
作为Android编码的新手,但在其他编程语言/框架方面有一定经验,我期待我的Android应用程序编码将是一个相当愉快的编程.就是这样,直到我偶然发现了这个问题:

Android项目的Eclipse向导表明,如果我将最小API设置为8(Android 2.2),我可以达到95%的设备.无论如何我都不需要用我的应用做任何花哨的事情,所以我想,"当然,为什么不呢?".一切都还可以,除了偶尔我会发现在最近的API版本中不推荐使用的几个方法/类,所以我不得不设法继续使用旧设备的旧方法,并尝试尽可能多地使用新设备更新Android版本的方法.这是一个这样的场合.

在使用Eclipse向导创建首选项活动之后,我意识到Eclipse预编译器/解析器/检查器(或其他任何名称)Lint会抱怨无法使用在旧API版本中创建/管理首选项的新方法.所以我想,"好吧,拧紧新的方法.让我们以旧的方式去做,因为新的API版本应该向后兼容,它应该没问题",但事实并非如此.旧方法使用标记为已弃用的方法/类; 对我来说,这意味着,即使它们仍然在当前的API中工作,它们也会在将来的版本中停止工作.

所以我开始寻找正确的方法,最后点击此页面:在PreferenceActivity中使用什么而不是"addPreferencesFromResource"?Garret Wilson解释了一种以与新方式兼容的方式使用旧的首选屏幕资源的方法.这很棒,最后我觉得我可以继续使用我的应用程序编码,除非它在定位旧API时不起作用,因为它使用了更新的API代码.因此,我必须设计一种方法,使其适用于旧的API和更新的API.经过一段时间的修补后,我设法找到了一种方法,通过使用预编译器(或其他任何名称)注释和伟大的getClass().getMethod()以及异常.

在创建首选项子屏幕之前,一切似乎都完美无瑕.它在较新的Android版本中正确显示,但是当我尝试使用旧版本时,我只能看到黑屏.经过大量搜索后,我发现此页面解释了这个问题:http://code.google.com/p/android/issues/detail?id = 4611这显然是一个众所周知的故障,它已经在几个Android版本中出现了很长时间.我阅读了整个帖子并找到了几个问题的解决方案,但我真的不完全喜欢它们中的任何一个.就我而言,我更愿意尽可能避免使用静态内容,并以编程方式执行操作.我更喜欢自动化而非重复性工作.一些解决方案建议将子屏幕创建为父屏幕,然后将它们添加到清单文件中,并通过意图从父屏幕调用它们.我真的很讨厌跟踪那些事情:清单中的条目,分离的屏幕资源文件,意图......所以这对我来说是禁忌.我一直在寻找并找到了一种我更喜欢的程序化方法......却发现它不起作用.它包括迭代首选屏幕的整个视图树并为偏好子屏幕分配适当的背景,但它只是不起作用,因为我后来在经过大量调试后发现,偏好子屏幕视图不是偏好屏幕视图的子项.我必须找到一种方法来实现这一目标.我尝试了许多我能想到的东西,研究和研究无济于事.我几次濒临放弃,但经过两周的持续努力和大量调试后,我找到了一个解决方法,我在评论#35中发布了这个.

意见
这真的不是完美的解决方案/方法,我知道它的一些缺点,但它是有效的,所以我决定分享它.希望我的热情并不是太荒谬,因为我知道任何经验丰富的编码员都可以解决这个问题并不是很重要.但是,嘿,我认为分享知识会让我好一点,无论我吹嘘多少,都比一个经验丰富的编码员能让自己保持一切.只是分享我的意见,因为我无法相信以前没有人遇到过这个问题,但我确实相信很多人已经拥有它并且没有费心去分享他们的知识.

我在答案中提出了一个建议的类,用于Android的几个版本,以及一些有关其用法的建议.我愿意接受讨论和贡献,以使其成为更好的课程.

已知的问题:

  • 父屏幕装饰视图背景克隆到子屏幕装饰视图背景,这显然不是正常行为.
    状态:被解雇,直到有人提出解决此问题的充分理由
  • 屏幕旋转时崩溃
    状态:已修复.
    可能与较新的API实现(内部类PF)的资源可见性相关.
    来自preferenceFragment的显然继承的类需要使其所有成员都是静态的.我想如果你每次需要使用新片段时都应该继承,这是有道理的

A--*_*--C 18

如果您使用的是最新的ADT插件,则可以选择轻松创建支持大多数旧版Android以及所有新版本的首选项.

右键单击您的项目 - >其他 - > Android Activity

然后选择SettingsActivity 在此输入图像描述

创建的Activity将负责处理高和低API版本,因为它使用if语句来选择显示首选项的适当方法.


编辑
提出了一个很好的观点:电话大小的设备,无论API版本使用旧PreferenceActivity方法.

获得API 11+设备使用最快的方法Fragments是删除!isXLargeTablet(context);isSimplePreferences()

private static boolean isSimplePreferences(Context context) {
    return ALWAYS_SIMPLE_PREFS
            || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
}
Run Code Online (Sandbox Code Playgroud)

但是,现在用户有更多导航要做.
标题为root

这是因为onBuildHeaders()被称为.

要摆脱这种情况,我们需要创建自己的PreferenceFragment来添加每个xml资源.

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class AllPreferencesFragment extends PreferenceFragment{
        @Override
        public void onCreate (Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.pref_general);

            // Add 'notifications' preferences, and a corresponding header.
            PreferenceCategory fakeHeader = new PreferenceCategory(getActivity());
            fakeHeader.setTitle(R.string.pref_header_notifications);
            getPreferenceScreen().addPreference(fakeHeader);
            addPreferencesFromResource(R.xml.pref_notification);

            // Add 'data and sync' preferences, and a corresponding header.
            fakeHeader = new PreferenceCategory(getActivity());
            fakeHeader.setTitle(R.string.pref_header_data_sync);
            getPreferenceScreen().addPreference(fakeHeader);
            addPreferencesFromResource(R.xml.pref_data_sync);

            // Bind the summaries of EditText/List/Dialog/Ringtone preferences to
            // their values. When their values change, their summaries are updated
            // to reflect the new value, per the Android Design guidelines.
            bindPreferenceSummaryToValue(findPreference("example_text"));
            bindPreferenceSummaryToValue(findPreference("example_list"));
            bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
            bindPreferenceSummaryToValue(findPreference("sync_frequency"));
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果您可以从Activity启动设置的外部确定屏幕大小,则可以指定要通过其启动的片段EXTRA_SHOW_FRAGMENT

i.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, "com.example.test.SettingsActivity$AllPreferencesFragment");
Run Code Online (Sandbox Code Playgroud)

或者您SettingsActivity可以确定是否显示此片段(假设您对该isXLargeTablet()方法感到满意.

更改onBuildHeaders()到:

@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target) {
    if (!isSimplePreferences(this) && isXLargeTablet(this)) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }
}
Run Code Online (Sandbox Code Playgroud)

添加此方法:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupNewApiPhoneSizePreferences() {
    if (!isXLargeTablet(this) && Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB){
            getFragmentManager().beginTransaction().replace(android.R.id.content, new AllPreferencesFragment()).commit();
    }
}
Run Code Online (Sandbox Code Playgroud)

onPostCreate()添加方法调用.

setupNewApiPhoneSizePreferences();
Run Code Online (Sandbox Code Playgroud)

现在应该使用API​​ 11以后的非弃用调用.