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

时间:2015-09-17 01:50:29

标签: android android-support-library android-appcompat android-preferences

我想支持至少api 10,我希望能够很好地设置我的偏好,我希望能够有标题(或显示PreferenceScreen s)。似乎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...
        }
    }
}

现在,我想创建一个自定义首选项,它将提供字体选择。使用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);
    }
}

并使用此类xml来显示它

<my.app.FontPreference android:title="Choose font" android:summary="Unnecessary summary" />

但现在,onPrepareDialogBuilder中没有android.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);
}

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

java.lang.IllegalStateException: Target fragment must implement TargetFragment interface

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

3 个答案:

答案 0 :(得分:36)

  

重要提示:   目前(v7库的v23.0.1)仍有很多主题问题与“#PreferenceThemeOverlay”#(见this issue)。例如,在棒棒糖上,你最终得到了Holo风格的类别标题。

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

首先,您可能想知道为什么您会为每种偏好设置类型找到DialogPreferencePreferenceDialogFragmentCompat。事实证明,第一个是实际首选项,第二个是DialogFragment,其中将显示首选项。遗憾的是,您需要继承两者

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

  • 所有偏好编辑方法(如setTitle()persist*())均可在DialogPreference课程中找到。
  • 所有对话框(编辑)方法(onBindDialogView(View)&amp; onDialogClosed(boolean))已移至PreferenceDialogFragmentCompat

您可能希望现有的课程延伸到第一个课程,这样您就不必改变我想的。自动填充应该可以帮助您找到缺失的方法。

完成上述步骤后,就可以将这两个类绑定在一起了。在xml文件中,您将引用首选项部分。但是,当您的自定义偏好需要时,Android还不知道它必须膨胀的Fragment。因此,您需要覆盖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);
}

并且您的DialogFragment需要处理&#39;:

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;
}

这应该可以解决问题。如果您遇到问题,请尝试查看现有的子类并查看Android如何解决它(在Android Studio中:键入一个类&#39;名称并按Ctrl + b查看反编译的类)。希望它有所帮助。

答案 1 :(得分:1)

有一个很好的教程和Github项目,详细解释了如何制作扩展支持首选项库的自定义首选项类:

关键点是:

  • 您将需要一个自定义ListPreferenceDialogPreference,以控制首选项行的外观和功能。 (它也可以包含对应该在启动的对话框中显示的布局的引用)。将此PreferenceDialogFragmentCompat添加到您的XML首选项文件中。

  • 您将需要一个自定义onBindDialogView(),该自定义onDisplayPreferenceDialog()用于在单击首选项行时控制对话框的启动。您可以在PreferenceDialogFragmentCompat中配置对话框的视图。

  • 在扩展了PreferenceFragmentCompat的首选项屏幕中,覆盖androidx.preference.EditTextPreference以启动您的自定义android.preference.EditTextPreference

  • 您只能扩展支持类,而不能扩展平台类。例如,扩展input[type=radio] { vertical-align: middle; margin: 0 10px 0 0; }而不是<p>Choose your education level:</p> <input type="radio" name="education" value="1">High School degree <br> <input type="radio" name="education" value="2">College degree <br> <input type="radio" name="education" value="3">Masters degree <br> <input type="radio" name="education" value="4">PhD degree

答案 2 :(得分:0)

FontPreferenceFragment未实施DialogPreference.TargetFragment时,会导致异常。您需要确保您的片段实现该接口。