使用support.v7.preference时,偏好设置子屏幕无法打开

26

我正在尝试使用AppCompatActivity和support.v7.preference实现具有子屏幕的首选项。

根据文档,每个在另一个PreferenceScreen中的PreferenceScreen都作为子屏幕,在单击时框架会处理其显示。 http://developer.android.com/guide/topics/ui/settings.html#Subscreens

<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- opens a subscreen of settings -->
    <PreferenceScreen
        android:key="button_voicemail_category_key"
        android:title="@string/voicemail"
        android:persistent="false">
        <ListPreference
            android:key="button_voicemail_provider_key"
            android:title="@string/voicemail_provider" ... />
        <!-- opens another nested subscreen -->
        <PreferenceScreen
            android:key="button_voicemail_setting_key"
            android:title="@string/voicemail_settings"
            android:persistent="false">
            ...
        </PreferenceScreen>
        <RingtonePreference
            android:key="button_voicemail_ringtone_key"
            android:title="@string/voicemail_ringtone_title"
            android:ringtoneType="notification" ... />
        ...
    </PreferenceScreen>
    ...
</PreferenceScreen>

使用原生的Activity和PreferenceFragment可以正常工作,但使用AppCompatActivity和PreferenceFragmentCompat时,单击首选项元素仅会将其突出显示,但不会打开子屏幕。

在阅读文档和代码时我找不到任何有关此问题的信息...我需要实现任何其他回调函数吗?


编辑:只是为了完整性...

以下内容可以正常工作并打开子屏幕:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction()
                    .replace(android.R.id.content, new DemoPreferenceFragment())
                    .commit();
        }
    }

    static public class DemoPreferenceFragment extends PreferenceFragment {

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);
        }
    }
}

这不起作用/无法打开子屏幕:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(android.R.id.content, new DemoPreferenceFragment())
                    .commit();
        }
    }

    static public class DemoPreferenceFragment extends PreferenceFragmentCompat {

        @Override
        public void onCreatePreferences(Bundle bundle, String s) {
            addPreferencesFromResource(R.xml.preferences);
        }
    }
}

编辑:2016年1月25日

我研究了几天的support.v7.preference,在这里总结了我的发现,希望能帮助其他人: 如何使用支持v7偏好与AppCompat及其潜在缺点

5个回答

19

看起来这是PreferenceFragmentCompat的一个错误,或者是文档不足。它有一个onNavigateToScreen方法,在你点击PreferenceScreen项目时被调用。

但是默认情况下,getCallbackFragment()方法返回null,所以你需要在你的fragment中覆盖它并返回this。另外,你需要实现PreferenceFragmentCompat.OnPreferenceStartScreenCallback接口。

public class SettingsFragment extends PreferenceFragmentCompat implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback {

    public static SettingsFragment newInstance() {
        return new SettingsFragment();
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        addPreferencesFromResource(R.xml.news_settings);
    }

    @Override
    public Fragment getCallbackFragment() {
        return this;
    }

    @Override
    public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
        preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
        return true;
    }
}

但是当你无法回到初始的PreferenceScreen时,它会导致另一个问题。

另一种方法是替换片段,具体方法在这里描述:如何从首选项子屏幕返回主屏幕(在PreferenceFragmentCompat中)?


谢谢你的帮助!我已经自己摸索了一下,并在这里发布了一个关于处理support.v7.preference的经验总结:https://dev59.com/DFsW5IYBdhLWcg3wbm0O#34983933 也许你可以看一下,看看是否有什么需要补充的... - maxdownunder

16

这是一个完整的工作示例,希望对某些人有所帮助。它涵盖了打开首选项子屏幕并返回主设置屏幕的内容。

我在Android开源问题跟踪器中关注了这个问题 - 点击这里

官方文档缺少加载首选项子屏幕的文档 - 请参阅官方文档---

主高级设置屏幕有2个复选框和一个禁用的子屏幕标题(自定义模式设置):

subscreen title disabled

一旦我们勾选了自定义复选框,子屏幕标题就会启用。 subscreen title enabled

单击自定义模式设置后,子屏幕将在新屏幕中打开

enter image description here

以下是带有文档的示例代码:--

在res/xml/preferences.xml文件中:--

    <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    android:summary="Trying intro text">
    <PreferenceCategory android:title="Settings">
        <CheckBoxPreference
            android:defaultValue="true"
            android:key="defaultPress"
            android:title="Default settings" />
        <CheckBoxPreference
            android:defaultValue="false"
            android:key="customKey"
            android:title="Custom" />
        <PreferenceScreen
            android:key="customPrefKey"
            android:title="Custom Pattern Settings">
            <PreferenceCategory
                android:key="customSettingsKey"
                android:title="Custom Settings">
                <ListPreference
                    android:defaultValue="4"
                    android:entries="@array/initialClickArray"
                    android:entryValues="@array/initialClickValues"
                    android:key="initialClicks"
                    android:summary="initialClicksSummary"
                    android:title="No. Of Clicks" />
                <ListPreference
                    android:defaultValue="5"
                    android:entries="@array/initialTimeArray"
                    android:entryValues="@array/initialTimeValues"
                    android:key="initialTimeKey"
                    android:summary="Time to complete clicks"
                    android:title="Time to complete" />
            </PreferenceCategory>
        </PreferenceScreen>
    </PreferenceCategory>
</PreferenceScreen>

MainActivity.java应实现接口PreferenceFragmentCompat.OnPreferenceStartScreenCallback,然后重写方法-- onPreferenceStartScreen

public class MainActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback {

    private static final String TAG = MainActivity.class.getName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = null;
        if (savedInstanceState == null) {
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragment = new AdvancedSettingsFragment().newInstance("Advanced Setting");
            fragmentTransaction.add(R.id.fragment_container, fragment);
            fragmentTransaction.commit();
        }
    }

    @Override
        public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat,
                                           PreferenceScreen preferenceScreen) {
        Log.d(TAG, "callback called to attach the preference sub screen");
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        AdvancedSettingsSubScreenFragment fragment = AdvancedSettingsSubScreenFragment.newInstance("Advanced Settings Subscreen");
        Bundle args = new Bundle();
        //Defining the sub screen as new root for the  subscreen
        args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey());
        fragment.setArguments(args);
        ft.replace(R.id.fragment_container, fragment, preferenceScreen.getKey());
        ft.addToBackStack(null);
        ft.commit();
        return true;
    }

主要设置界面(片段):

public class AdvancedSettingsFragment extends PreferenceFragmentCompat {
    private static final String TAG = AdvancedSettingsFragment.class.getName();
    public static final String PAGE_ID = "page_id";

    public static AdvancedSettingsFragment newInstance(String pageId) {
        AdvancedSettingsFragment f = new AdvancedSettingsFragment();
        Bundle args = new Bundle();
        args.putString(PAGE_ID, pageId);
        f.setArguments(args);
        return (f);
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
        final CheckBoxPreference customPreference = (CheckBoxPreference) findPreference("customKey");
        final Preference customSettings = (Preference) findPreference("customPrefKey");
        // First time loading the preference screen, we check the saved settings and enable/disable the custom settings, based on the custom check box
        //get the customSettings value from shared preferences
        if (getCustomSettings(getActivity())) {
            customPreference.setChecked(true);
            customSettings.setEnabled(true);
        } else {
            customPreference.setChecked(false);
            customSettings.setEnabled(false);
        }
        customPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object selectedValue) {
                Log.d(TAG, "Inside on preference change of custom checkbox selection " + selectedValue.getClass());
                if ((Boolean) selectedValue) {
                    customSettings.setEnabled(true);
                }else{
                    customSettings.setEnabled(false);
                }
                return true;
            }
        });
    }
    private boolean getCustomSettings(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("customKey", false);
    }
}

最后,为了加载子屏幕:

public class AdvancedSettingsSubScreenFragment extends PreferenceFragmentCompat {
    private static final String TAG = AdvancedSettingsSubScreenFragment.class.getName();
    public static final String PAGE_ID = "page_id";

    public static AdvancedSettingsSubScreenFragment newInstance(String pageId) {
        AdvancedSettingsSubScreenFragment f = new AdvancedSettingsSubScreenFragment();
        Bundle args = new Bundle();
        args.putString(PAGE_ID, pageId);
        f.setArguments(args);
        return (f);
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        // rootKey is the name of preference sub screen key name , here--customPrefKey
        setPreferencesFromResource(R.xml.preferences, rootKey);
        Log.d(TAG, "onCreatePreferences of the sub screen " + rootKey);
    }
}

优美的解决方案 - Moti Bartov
有没有办法为每个子设置屏幕使用不同的XML文件?我尝试过但失败了。在这里写了一篇文章:https://dev59.com/1a7la4cB1Zd3GeqPl_nA - android developer
1
感谢您提供的解决方案,它也适用于多个首选项屏幕。巧合的是,这个想法还可以管理启用子屏幕,因为旧的依赖属性对我没有起作用。 - aphelion

2

你需要记住一件非常重要的事情:

你的PreferenceScreen必须包含:

android:key="name_a_unique_key"

否则,它将无法正常工作。我已经花了几个小时在这上面。

0

你能在AndroidX Preference中使用子屏幕吗? - user158
@user158 我还没有尝试过新的androidx preference。我正在等待它变得稳定。 - Dewey Reed

0
覆盖 PreferenceFragmentCompat.OnPreferenceStartScreenCallback,并在我的首选项片段中添加以下内容解决了我的问题。
@Override
public Fragment getCallbackFragment() {
    return this;
}

@Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
    caller.setPreferenceScreen(pref);
    return true;
}

我偏爱的版本是

compile 'com.android.support:preference-v7:25.0.0'

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