如何在安卓上创建Preference Activity和Preference Fragment?

44
我在按照一份旧教程(Créez des applications pour Android -> openclassroom)学习时,遇到了PreferenceActivity类中废弃的方法addPreferencesFromResource(int id)无法使用的问题。
我的问题是:
新的创建Android偏好设置的方法是什么?

1
最好以问答形式发布此帖子。这意味着将问题作为问题提出,并将答案放在答案中,而不是将所有内容都放在问题中。另请参见self-answer - donfuxx
很抱歉,我没有足够的声望来完成这个任务;(我会尽快完成它的 :) - WannaGetHigh
这可能是一个有用的资源,但它不是一个可以回答的问题。你可能想把它撤下来,直到你能够自我回答。一旦你能够自我回答,你就可以把它重新发布。 - ugo
好的,但是你怎么获得声誉呢?回答问题?评论?对不起,问了这么多问题,但这是我的第一篇帖子,你可能已经注意到了 :) - WannaGetHigh
@Ugo 谢谢你的回答,我会这样做! - WannaGetHigh
显示剩余2条评论
2个回答

103
我发现了这篇帖子(What to use instead of “addPreferencesFromResource” in a PreferenceActivity?),它帮助我理解必须通过PreferenceFragment来实现。

在下面的解释中,我使用your.package.来说明您必须输入包名。每个人都有自己的包名,请用您的包名替换它。

开始:


1. 偏好设置 Fragment

  • Create your PreferenceFragment class

    MyPreferenceFragment

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


  • Then the associated xml resource

    fragment_preference.xml (in the folder res/xml of your project)

    <?xml version="1.0" encoding="utf-8"?>
    
    <PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <PreferenceCategory 
            android:title="FOO">
    
            <CheckBoxPreference
                android:key="checkBoxPref"
                android:title="check it out"
                android:summary="click this little box"/>
    
        </PreferenceCategory>
    
    </PreferenceScreen>
    

    That's all for the Fragment part.



2. 首选项活动

  • Create the PreferenceActivity class

    MyPreferenceActivity

    public class MyPreferenceActivity extends PreferenceActivity
    {
        @Override
        public void onBuildHeaders(List<Header> target)
        {
            loadHeadersFromResource(R.xml.headers_preference, target);
        }
    
        @Override
        protected boolean isValidFragment(String fragmentName)
        {
            return MyPreferenceFragment.class.getName().equals(fragmentName);
        }
    }
    

    Do not forget to override isValidFragment(String fragmentName) method as you will get punched in the face by your application ! ;) More seriously I have no idea why you need to do this but it is needed. If someone has an explanation about this I'd gladly read it :)

    EDIT :


    Thanks to kirtan403 I now know why it is needed : it has to be set because of an (android framework fragment injection).


    As you can see in the onBuildHeaders(List<Header> target) we load another xml file that contain the headers of the preference. In short, headers are the left part of the preference and the fragment are the right part (for tablet). For a phone you will first have the headers and when you click on an item the corresponding fragment will be put on top of the headers list.

    Read this article (Multi-pane development in Android with Fragments - Tutorial) the images explain themselves.


  • Then the associated xml resource

    headers_preference.xml (in the folder res/xml of your project)

    <?xml version="1.0" encoding="utf-8"?>
    
    <preference-headers
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <header 
            android:fragment="your.package.MyPreferenceFragment"
            android:title="Goto: Preference fragment"
            android:summary="An example of some preferences." />
    
    </preference-headers>
    

    As you may have noticed in the header section you have :

    android:fragment="your.package.MyPreferenceFragment"

    This will act as a Link to the fragment you want to show. On Tablet it will load on the right part and on the phone it will load on top of the current view.



3. Android清单文件

现在你需要做的是将你的Activity添加到AndroidManifest.xml文件中。

application部分中加入以下代码:

<activity
    android:name="your.package.MyPreferenceActivity"
    android:label="Preferences">
</activity>

你可能会告诉我:
“亲爱的,你忘记在你的活动中添加android:launchMode=“singleTask”了。”
不要这样做,否则你的片段将永远无法加载到手机上。这个错误是由一个伟大的人解决的!这是他博客的链接(Android header preferences on small screen/phone)。

4. 从菜单启动首选项

最后,你需要添加显示此首选项的功能!为此,你需要三件事情:
  • The Menu

    menu.xml (in the folder res/menu of your project)

    <?xml version="1.0" encoding="utf-8"?>
    
    <menu 
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item 
            android:id="@+id/preferences"
            android:title="Preferences" />
    
    </menu>
    


  • Loading this Menu in your Main activity (not the PreferenceActivity) under the method onCreateOptionsMenu(Menu menu)

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }
    


  • Starting the MyPreferenceActivity Activity when you click on that button.

    For that you will need to override the onOptionsItemSelected(MenuItem item) method in your Main activity.

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch(item.getItemId())
        {
            case R.id.preferences:
            {
                Intent intent = new Intent();
                intent.setClassName(this, "your.package.MyPreferenceActivity");
                startActivity(intent);
                return true;
            }
        }
    
        return super.onOptionsItemSelected(item);
    }
    



大家好!

我还没有测试过这段代码。我从自己的代码中复制并修改了它,可能没复制好。如果你遇到错误,请告诉我,我会试着找出问题并修复。

希望这篇文章能帮助到一些人 :D

干杯!


1
FWIW,我已经添加了你的链接(请包括链接名称,以防以后该链接失效,这样就可以在谷歌上搜索该页面),并添加了一些格式设置,使其更易于阅读。同时根据本站的最佳实践进行了一些小修改。随意更改我的任何编辑-毕竟这是你的问题。哦,你现在可以删掉上面的评论了,因为它们现在已经过时了。祝好运! - Richard Le Mesurier
1
@RichardLeMesurier 非常感谢您的帮助!阅读起来确实更容易,也感谢您的建议,我会记住它们,以备将来在这个网站上回答/提问时使用!继续保持您的热情! :) - WannaGetHigh
3
@WannaGetHigh提到了isValidFragment()方法的使用,想知道为什么要使用它。你可以在这里了解更多相关信息。由于存在漏洞,该方法被引入到Android 4.4 Kitkat中。 - kirtan403
1
你好,这非常有帮助!有没有快速提示如何仅显示片段,而不显示中间的任何其他屏幕? - DorD
您需要重写isValidFragment函数以防止Fragment注入漏洞。 - PrashanD
显示剩余2条评论

19

我喜欢这篇帖子中的解决方案:http://alvinalexander.com/android/android-tutorial-preferencescreen-preferenceactivity-preferencefragment

因为它对于只需要快速启动基本功能的人来说似乎是最紧凑的。它只有一个 .java 文件和两个小的 xml 文件。

活动配置提醒

在将这3个文件添加到您的项目后,不要忘记:

A) 将 Prefs Activity 添加到 Manifest 文件中
B) 添加一些方法来启动 Prefs Activity…例如按钮或菜单项

将以下文件添加到您的项目中。按照列出的顺序进行以避免编译错误。

  1. 添加 /res/values/array.xml

<resources>
    <string-array name="listArray">
        <item>Ace</item>
        <item>Club</item>
    </string-array>

    <string-array name="listValues">
        <item>Ace</item>
        <item>Club</item>
    </string-array>
</resources>
  • 添加/res/xml/preferences.xml

  • <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
        <EditTextPreference android:title="Your Name"
                            android:key="username"
                            android:summary="Please provide your username"></EditTextPreference>
        <CheckBoxPreference android:title="Application Updates"
                            android:defaultValue="false"
                            android:summary="This option if selected will allow the application to check for latest versions."
                            android:key="applicationUpdates" />
        <ListPreference     android:title="Download Details"
                            android:summary="Select the kind of data that you would like to download"
                            android:key="downloadType"
                            android:defaultValue="Ace"
                            android:entries="@array/listArray"
                            android:entryValues="@array/listValues" />
    </PreferenceScreen>
    
  • 添加活动代码

  • public class AppPreferenceActivity extends PreferenceActivity
    {
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
    
            checkValues();
        }
    
        public static class MyPreferenceFragment extends PreferenceFragment
        {
            @Override
            public void onCreate(final Bundle savedInstanceState)
            {
                super.onCreate(savedInstanceState);
                addPreferencesFromResource(R.xml.preferences);
            }
        }
    
        private void checkValues()
        {
            SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
            String strUserName = sharedPrefs.getString("username", "NA");
            boolean bAppUpdates = sharedPrefs.getBoolean("applicationUpdates",false);
            String downloadType = sharedPrefs.getString("downloadType","1");
    
            String msg = "Cur Values: ";
            msg += "\n userName = " + strUserName;
            msg += "\n bAppUpdates = " + bAppUpdates;
            msg += "\n downloadType = " + downloadType;
    
            Toaster.shortDebug(msg);
        }
    }
    

    我想将设置界面显示为一个片段(而不是一个活动)...在片段中是否有可能这样做呢? - Vishal Kumar

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