Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
234 views
in Technique[技术] by (71.8m points)

android - New Preference support library incorrect theme at runtime

I'm trying to use the new Preference v14 Support library. To give the preferences a material style, I use the following style on my Activity:

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

That works fine. My problem is that when I add new Preferences at runtime, they get inflated using an old theme. Here's a screenshot of the result:

WTF

As you can see, the first preference, added via XML, has the new Material style, while the others don't.

Do you have any hint on how to solve the problem?

EDIT Here's an example of code I use to add the Preference at 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: The same behavior occurs with android.support.v7.preference.Preference

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem, you're facing, is related to Context and how its theming works. Your code retrieves a context by passing getActivity() to the constructor, however, this is not the context you want. Here's the solution that applies the correct styles:

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

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

    category.addPreference(p);
}

Explanation

Here's PreferenceFragmentCompat's onCreate(...) method:

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

The important lines:

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

Here the preferenceTheme is being retrieved from the Activity's theme. If it exists (i.e. theme is not 0), PFC (PreferenceFragmentCompat) creates a new theme wrapper which will contain the styling infos:

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

Using this styled context, the PFC can now create the PreferenceManager:

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

This PFC's root style is now the preferenceTheme which contains all the different sub-styles (preferenceStyle for example).

The problem with your solution is that the Preference class is looking for a preferenceStyle attribute in the contructor-passed context. However, it's only defined in your preferenceTheme, not in the Activity's theme.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...