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:
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
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.