Нестандартная тема библиотеки поддержки предпочтений во время выполнения

Я пытаюсь использовать новую библиотеку поддержки предпочтений v14. Чтобы придать предпочтения материальному стилю, я использую следующий стиль для своей деятельности:

<style name="PreferenceTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
    <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style>

Это прекрасно работает. Моя проблема в том, что когда я добавляю новые настройки во время выполнения, они накачиваются с использованием старой темы. Вот скриншот результата:

WTF

Как вы можете видеть, первое предпочтение, добавленное через XML, имеет новый стиль Material, а другие - нет.

Есть ли у вас какой-либо намек на то, как решить проблему?

ИЗМЕНИТЬ Вот пример кода, который я использую для добавления Предпочтения в Runtime:

import android.support.v7.preference.ListPreference;

for (...) {
        final ListPreference p = new ListPreference(getActivity());
        p.setTitle(name);
        p.setSummary(langname);
        p.setEntryValues(langEntryValues);
        p.setEntries(langDisplayValues);
        p.setDialogTitle(R.string.select_language);

        category.addPreference(p);
    }

PS: То же самое происходит с android.support.v7.preference.Preference

Ответ 1

Проблема, с которой вы сталкиваетесь, связана с Context и тем, как работает ее тематика. Ваш код извлекает контекст, передавая getActivity() конструктору, однако это не тот контекст, который вы хотите. Здесь решение, которое применяет правильные стили:

final Context ctx = getPreferenceManager().getContext();

for (...) {
    final ListPreference p = new ListPreference(ctx);
    // [...]

    category.addPreference(p);
}

Описание

Здесь PreferenceFragmentCompat onCreate(...) метод:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    TypedValue tv = new TypedValue();
    this.getActivity().getTheme().resolveAttribute(attr.preferenceTheme, tv, true);
    int theme = tv.resourceId;
    if(theme <= 0) {
        throw new IllegalStateException("Must specify preferenceTheme in theme");
    } else {
        this.mStyledContext = new ContextThemeWrapper(this.getActivity(), theme);
        this.mPreferenceManager = new PreferenceManager(this.mStyledContext);
        // [...]

        this.onCreatePreferences(savedInstanceState, rootKey);
    }
}

Важные строки:

this.getActivity().getTheme().resolveAttribute(attr.preferenceTheme, tv, true);
int theme = tv.resourceId;

Здесь preferenceTheme извлекается из темы "Активность". Если он существует (т.е. theme не равен 0), PFC (PreferenceFragmentCompat) создает новую обертку темы, которая будет содержать информацию о стиле:

this.mStyledContext = new ContextThemeWrapper(this.getActivity(), theme);

Используя этот стиль, PFC теперь может создать PreferenceManager:

this.mPreferenceManager = new PreferenceManager(this.mStyledContext);

Этот корневой стиль PFC теперь представляет собой preferenceTheme, который содержит все различные подтипы (например, preferenceStyle).

Проблема с вашим решением заключается в том, что класс Preference ищет атрибут preferenceStyle в контр-переданном контексте. Однако он определен только в вашем preferenceTheme, а не в теме "Активность".