在PreferenceActivity中,替代“addPreferencesFromResource”的是什么?

365
我刚刚注意到在Android文档中,方法addPreferencesFromResource(int preferencesResId)被标记为已弃用(参考资料)。
不幸的是,在该方法的描述中没有提供替代方法。
应该使用哪个方法来连接preferenceScreen.xml与对应的PreferenceActivity呢?

2
WannaGetHigh 在 https://dev59.com/lWAg5IYBdhLWcg3wfrEn 提供了非常直接的方法。 - Sabin
那个解决方案仍然使用 addPreferencesFromResource(int preferencesResId)。我有什么遗漏吗? - Jammo
@Jammo 是的,但它已经从 Activity 移动到 Fragment 中以反映新的做法 -> fragment。 - WannaGetHigh
6个回答

334

在方法的描述中没有提供替代方法,因为(自API级别11起)首选方法是实例化PreferenceFragment对象来从资源文件加载您的偏好设置。请参见此处的示例代码:PreferenceActivity


非常感谢您的回答。我只是按照“Video2Brain-Android Development”中过时的说明进行操作,这导致我使用了PreferenceActivity版本的方法。顺便说一句:如果可以的话,我很想将您的答案评为有用。 - mweisz
33
只有如果PreferenceFragment被包含在兼容性包中,才有意义使用它。https://dev59.com/k2035IYBdhLWcg3wTuJu - christoff
1
由于我使用Action Bar Sherlock,我遵循以下博客来解决这个问题,请参见... http://commonsware.com/blog/2012/10/16/conditional-preference-headers.html - Someone Somewhere
2
如果您希望应用程序向后兼容Android 3.0之前的API级别,仍需调用addPreferencesFromResource(int PreferencesID)。但我想您也可以考虑那些旧设备已经过时了。 - Einar Sundgren
6
我是Nexus One手机的主人,可用的最高版本是2.2:你无法阻止我!我永远不会被弃用!凭借格雷史考尔的力量……我有力量! - Niki Romagnoli
请使用AppCompatPreferenceActivity。 - Bart Burg

189

为了给上面正确答案添加更多信息,阅读了Android-er的一个示例后,我发现您可以轻松将您的首选项活动转换为首选项片段。如果您有以下活动:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.my_preference_screen);
    }
}

您需要做出的唯一更改是创建一个内部片段类,将addPreferencesFromResources()移动到该片段中,并从活动中调用该片段,像这样:

唯一需要进行的更改就是创建一个内部碎片类,将addPreferencesFromResources()移动到该碎片中,并且从活动中调用该碎片,如下所示:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
    }

    public static class MyPreferenceFragment extends PreferenceFragment
    {
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.my_preference_screen);
        }
    }
}

制作更复杂的偏好设置可能存在其他微妙之处;如果有的话,我希望有人在这里记录它们。


6
好的,谢谢您。Java以及这种思维方式跟我的舒适的.NET世界相去甚远 :) - Tom
45
为什么他们要弃用addPreferencesFromResources(),转而使用PreferenceFragment?从初学者的角度来看,这似乎是不必要的。 - Howdy_McGee
4
调用需要API级别11。 - mehmet
1
那么如果API在第9级呢?@mehmet - gumuruh
2
太棒了的回答!有没有一种方法可以在不改变android.R.id.content的情况下实现这个?对我来说,这种方式似乎有些不雅观...(如果我错了,请纠正我) - tomer.z
显示剩余3条评论

38

@Garret Wilson非常感谢你!作为一个Android编码的新手,我一直被偏好设置不兼容问题困扰了很多小时,而且我发现他们废弃了一些方法/方法来支持新API,这些新API不支持旧的API,因此必须采用各种解决方法使您的应用程序在各种设备上运行。这真的很令人沮丧!

你的课程非常棒,因为它允许你以过去的方式使用偏好设置来继续在新API中工作,但它不具有向后兼容性。由于我正在尝试覆盖广泛的设备,我稍微调整了一下,以使其在早期API 11设备以及更新的API中都可以工作:

import android.annotation.TargetApi;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;

public class MyPrefsActivity extends PreferenceActivity
{
    private static int prefs=R.xml.myprefs;

    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        try {
            getClass().getMethod("getFragmentManager");
            AddResourceApi11AndGreater();
        } catch (NoSuchMethodException e) { //Api < 11
            AddResourceApiLessThan11();
        }
    }

    @SuppressWarnings("deprecation")
    protected void AddResourceApiLessThan11()
    {
        addPreferencesFromResource(prefs);
    }

    @TargetApi(11)
    protected void AddResourceApi11AndGreater()
    {
        getFragmentManager().beginTransaction().replace(android.R.id.content,
                new PF()).commit();
    }

    @TargetApi(11)
    public static class PF extends PreferenceFragment
    {       
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(MyPrefsActivity.prefs); //outer class
            // private members seem to be visible for inner class, and
            // making it static made things so much easier
        }
    }
}

在两个模拟器(2.2和4.2)上测试成功。

为什么我的代码看起来这么糟糕:

我是一个Android编程新手,也不是很喜欢Java。

为了避免过时警告并强制Eclipse允许我编译,我不得不使用注释,但这似乎只影响类或方法,所以我不得不将代码移到两个新方法上以利用此功能。

我不想每次将类复制粘贴到新的PreferenceActivity中时都要写两次xml资源ID,因此我创建了一个新变量来存储这个值。

我希望这对其他人有用。

P.S.:对于我的偏见观点,我感到抱歉,但当你新来临并发现这样的障碍时,你就不得不感到沮丧!


哦,好吧...我刚刚注意到每次复制和粘贴时都必须更改外部类名称两次。实际上,有一种方法可以避免这种情况,即通过将prefs传递给内部类。您可能不会创建一个接受prefs作为参数的内部类构造函数,因为这显然不推荐用于PreferenceFragment派生类。此外,您可能不会在内部类中创建一个获取prefs并立即调用addPreferencesFromResource的方法,因为addPreferencesFromResource需要在super.onCreate被调用后才能被调用,而onCreate并不会在PreferenceFragment派生类之后立即被调用... - ecv
已经被实例化。因此,您需要在内部类中创建一个新变量,在内部类中创建一个公共设置方法,在调用super.onCreate后将addPreferencesFromResource保留在原地,在onCreate中实例化内部类,设置prefs,并在调用getFragmentManager()时使用它...与以前一样。 - ecv
2
你的代码真是救了我一命!!我一直在尝试兼容新旧手机,浪费了三天时间。而答案却如此简单。谢谢! - Devdatta Tengshe

22

我的方法非常接近Garret Wilson的(谢谢,我已经给你投票了;)

此外,它与 Android < 3 向下兼容。

我刚才发现,我的解决方案甚至更接近Kevin Remo的解决方案。只是稍微更干净一点(因为它不依赖于"expection"反模式)。

public class MyPreferenceActivity extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            onCreatePreferenceActivity();
        } else {
            onCreatePreferenceFragment();
        }
    }

    /**
     * Wraps legacy {@link #onCreate(Bundle)} code for Android < 3 (i.e. API lvl
     * < 11).
     */
    @SuppressWarnings("deprecation")
    private void onCreatePreferenceActivity() {
        addPreferencesFromResource(R.xml.preferences);
    }

    /**
     * Wraps {@link #onCreate(Bundle)} code for Android >= 3 (i.e. API lvl >=
     * 11).
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void onCreatePreferenceFragment() {
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new MyPreferenceFragment ())
                .commit();
    }
}

对于一个“真实”的(但更加复杂的)例子,请参见NusicPreferencesActivityNusicPreferencesFragment


@SuppressLint("NewApi") - 避免 - 更加精确。你是否在低版本API上运行过它?它会抛出VerifyError。 - Mr_and_Mrs_D
我在运行API级别为10的模拟器上测试了以上内容,APK是使用SDK版本19构建的。您用哪个SDK版本进行构建?您在哪个设备API级别上运行它?您是在模拟器上还是在物理设备上运行它?错误是在什么时候发生的?如果您正在使用SDK <= 16进行构建,请参见此回答 - schnatterer
那么,在这种特定情况下,如何避免使用@SuppressLint("NewApi") - schnatterer
@TargetApi(Build.VERSION_CODES.HONEYCOMB) - 不是所有API的警告都会显示 :) - Mr_and_Mrs_D
@M 现在这是一个具体的改进建议!谢谢,我已经更新了帖子。 - schnatterer

6

不要使用异常,改用以下方式:

if (Build.VERSION.SDK_INT >= 11)

并使用

@SuppressLint("NewApi")

抑制警告信息。


1
因为它除了崩溃什么也做不了 :D - Ishtiaq
1
如果它崩溃了...那就尝试捕获它 :D - gumuruh

0

不要使用 PreferenceActivity 直接加载偏好设置,而是使用 AppCompatActivity 或等效的方式来加载一个 PreferenceFragmentCompat 来加载您的偏好设置。这是支持库(现在是 Android Jetpack)的一部分,并提供向后兼容性到 API 14。

在你的 build.gradle 中,添加偏好设置支持库的依赖:

dependencies {
    // ...
    implementation "androidx.preference:preference:1.0.0-alpha1"
}

注意:我们将假设您已经创建了您的首选项XML。
对于您的活动,请创建一个新的活动类。如果您正在使用材料主题,您应该扩展一个AppCompatActivity,但您可以灵活处理这个问题:
public class MyPreferencesActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_preferences_activity)
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, MyPreferencesFragment())
                    .commitNow()
        }
    }
}

现在是重要的部分:创建一个片段,从XML中加载您的偏好设置:
public class MyPreferencesFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.my_preferences_fragment); // Your preferences fragment
    }
}

如需更多信息,请阅读Android开发者文档,了解PreferenceFragmentCompat


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接