Как создать пользовательские настройки с помощью библиотеки android.support.v7.preference?
Я хочу поддерживать хотя бы api 10, я хочу хорошо стилизовать свои настройки, я хочу иметь заголовки (или показать PreferenceScreen
s). Похоже, что PreferenceActivity
, не полностью поддерживаемый раскраской AppCompat
, не подойдет. Поэтому я пытаюсь использовать AppCompatActivity
и PreferenceFragmentCompat
.
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" />
Но теперь в android.support.v7.preference.DialogPreference
нет onPrepareDialogBuilder
. Вместо этого он был перенесен на 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
В этот момент я уже слишком глубоко погружаюсь в беспорядок и не хочу копать дальше. Google ничего не знает об этом исключении. В любом случае, этот метод кажется слишком сложным. Итак, какой лучший способ создать пользовательские настройки с помощью библиотеки android.support.v7.preference?
Ответы
Ответ 1
Важное примечание:В настоящее время (v23.0.1 библиотеки v7) по-прежнему существует множество тематических проблем с "PreferenceThemeOverlay" (см. этот вопрос). Например, на Lollipop вы попадаете в заголовки категорий в стиле Холо.
После некоторых разочаровывающих часов мне наконец удалось создать пользовательский вариант v7. Создание собственного Preference
кажется сложнее, чем вы думаете. Поэтому не забудьте сделать некоторое время.
Сначала вам может быть интересно, почему вы найдете как DialogPreference
, так и PreferenceDialogFragmentCompat
для каждого типа предпочтений. Как оказалось, первая является фактическим предпочтением, вторая - DialogFragment
, где будет отображаться предпочтение. К сожалению, вам необходимо подклассифицировать их обоих.
Не волнуйтесь, вам не нужно будет менять какой-либо фрагмент кода. Вам нужно только переместить некоторые методы:
- Все методы редактирования предпочтений (например,
setTitle()
или persist*()
) могут быть найдены в классе DialogPreference
.
- Все методы диалога (-editing) (
onBindDialogView(View)
и 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
должен обрабатывать "ключ":
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: введите имя класса и нажмите Ctrl + b, чтобы увидеть декомпилированный класс). Надеюсь, что это поможет.
Ответ 2
Исключение возникает, когда ваш FontPreferenceFragment
не реализует DialogPreference.TargetFragment. Вам нужно убедиться, что ваш фрагмент реализует этот интерфейс.