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

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

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

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

WTF

Как видите, первое предпочтение, добавленное через XML, имеет новый стиль Материала, а остальные — нет.

У вас есть подсказка, как решить проблему?

EDIT Вот пример кода, который я использую для добавления настройки во время выполнения:

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


comment
Каков уровень API вашего устройства? Кроме того, не могли бы вы опубликовать код, который вы используете для добавления новых настроек?   -  person Gergely Kőrössy    schedule 08.09.2015
comment
Я тестировал это в API 22 и 23. Такое же поведение. См. обновленный ответ для кода.   -  person Spotlight    schedule 08.09.2015
comment
Я вижу, изучаю проблему прямо сейчас...   -  person Gergely Kőrössy    schedule 08.09.2015


Ответы (1)


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

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

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

    category.addPreference(p);
}

Объяснение

Вот метод onCreate(...) PreferenceFragmentCompat:

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 извлекается из темы Activity. Если он существует (т. е. 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, а не в теме Activity.

person Gergely Kőrössy    schedule 08.09.2015
comment
Я добавлю немного больше объяснений, просто хотел дать вам быстрое решение. - person Gergely Kőrössy; 08.09.2015
comment
Нет проблем, я добавил этот интересный случай в мой проект support preference-v7. - person Gergely Kőrössy; 08.09.2015