如何使用android.support.v7.preference库创建自定义首选项?

squ*_*rel 23 android android-appcompat android-preferences android-support-library

我想支持至少api 10,我希望能够很好地设置我的偏好,我希望能够有标题(或显示PreferenceScreens).似乎PreferenceActivity不完全支持AppCompat着色,不适合.所以我正在尝试使用AppCompatActivityPreferenceFragmentCompat.

public class Prefs extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null)
            getSupportFragmentManager().beginTransaction()
                    .replace(android.R.id.content, new PreferencesFragment())
                    .commit();
    }

    public static class PreferencesFragment extends PreferenceFragmentCompat {
        @Override public void onCreate(final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);
        }

        @Override
        public void onDisplayPreferenceDialog(Preference preference) {
            // the following call results in a dialogue being shown
            super.onDisplayPreferenceDialog(preference);
        }

        @Override public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
            // I can probably use this to go to to a nested preference screen
            // I'm not sure...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我想创建一个自定义首选项,它将提供字体选择.有了PreferenceActivity,我可以干脆做

import android.preference.DialogPreference;

public class FontPreference extends DialogPreference {

    public FontPreference(Context context, AttributeSet attrs) {super(context, attrs);}

    @Override protected void onPrepareDialogBuilder(Builder builder) {
        super.onPrepareDialogBuilder(builder);
        // do something with builder and make a nice cute dialogue, for example, like this
        builder.setSingleChoiceItems(new FontAdapter(), 0, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用这样的xml来显示它

<my.app.FontPreference android:title="Choose font" android:summary="Unnecessary summary" />
Run Code Online (Sandbox Code Playgroud)

但是现在,没有onPrepareDialogBuilderandroid.support.v7.preference.DialogPreference.相反,它已被转移到PreferenceDialogFragmentCompat.我发现很少有关于如何使用该东西的信息,我不知道如何从xml转到显示它.v14首选项片段具有以下代码:

public void onDisplayPreferenceDialog(Preference preference) {
    ...

    final DialogFragment f;
    if (preference instanceof EditTextPreference)
        f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
    ...
    f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}
Run Code Online (Sandbox Code Playgroud)

我尝试了子类化android.support.v7.preference.DialogPreferenceonDisplayPreferenceDialog使用类似的代码来实例化一个虚拟FontPreferenceFragment但它失败并出现以下异常.

java.lang.IllegalStateException: Target fragment must implement TargetFragment interface
Run Code Online (Sandbox Code Playgroud)

在这一点上,我已经太深入了解混乱,不想再深入挖掘.谷歌对这个例外一无所知.无论如何,这种方法似乎过于复杂.那么,使用android.support.v7.preference库创建自定义首选项的最佳方法是什么?

小智 39

重要说明: 目前(v7库的v23.0.1),"PreferenceThemeOverlay"仍存在很多主题问题(请参阅此问题).例如,在Lollipop上,你最终得到了Holo风格的类别标题.

经过一段令人沮丧的时间后,我终于成功创建了一个自定义的v7 Preference.创建自己的东西Preference似乎比你认为需要的更难.所以一定要花些时间.

首先,您可能想知道为什么您会找到每种首选项类型的a DialogPreference和a PreferenceDialogFragmentCompat.事实证明,第一个是实际的喜好,二是DialogFragment如喜好将显示在.不幸的是,你需要继承两个人.

别担心,您不需要更改任何代码.您只需要重新定位一些方法:

  • 所有偏好编辑方法(如setTitle()persist*())都可以在DialogPreference课堂上找到.
  • 所有对话框(编辑)方法(onBindDialogView(View)&onDialogClosed(boolean))都已移至PreferenceDialogFragmentCompat.

您可能希望现有的类扩展第一个类,这样您就不必更改我认为的更多.自动填充应该可以帮助您找到丢失的方法.

完成上述步骤后,就可以将这两个类绑定在一起了.在xml文件中,您将引用首选项部分.但是,Fragment当您的自定义偏好需要时,Android还不知道它必须膨胀.因此,您需要覆盖onDisplayPreferenceDialog(Preference):

@Override
public void onDisplayPreferenceDialog(Preference preference) {
    DialogFragment fragment;
    if (preference instanceof LocationChooserDialog) {
        fragment = LocationChooserFragmentCompat.newInstance(preference);
        fragment.setTargetFragment(this, 0);
        fragment.show(getFragmentManager(),
                "android.support.v7.preference.PreferenceFragment.DIALOG");
    } else super.onDisplayPreferenceDialog(preference);
}
Run Code Online (Sandbox Code Playgroud)

以及你DialogFragment处理'钥匙'的需要:

public static YourPreferenceDialogFragmentCompat newInstance(Preference preference) {
    YourPreferenceDialogFragmentCompat fragment = new YourPreferenceDialogFragmentCompat();
    Bundle bundle = new Bundle(1);
    bundle.putString("key", preference.getKey());
    fragment.setArguments(bundle);
    return fragment;
}
Run Code Online (Sandbox Code Playgroud)

这应该够了吧.如果您遇到问题,请尝试查看现有的子类并查看Android如何解决它(在Android Studio中:键入一个类的名称,然后按Ctrl + b查看反编译的类).希望能帮助到你.

  • 几乎是完美的解决方案!我花了一段时间才意识到onDisplayPreferenceDialog需要在首选项片段中重写(它继承了PreferenceFragmentCompat).非常感谢! (4认同)
  • 对于阅读此内容的人来说只是一个注释:如果你在PreferenceDialogFragmentCompat.java:57/58中得到ClassCastException,请确保你的bundle.putString("key",preference.getKey()); line如上所述说"key".否则,getArguments().getString(ARG_KEY)将因ClassCastException而失败. (2认同)