I was trying to implement preferences for an AppCompat app, using support.v7.preference. It took me a couple of days to fiddle through it, since support.v7.preference has some significant differences to the native preferences... which isn't too bad once you know, but unfortunately there's little documentation out there. I thought I'd share my findings so others don't have to go through the same pain.
How do you best implement Preferences for AppCompat apps (with PreferenceFragment and AppCompatAcitivity being incompatible)?
In the Properties window, select the "Android" properties group at left and locate the Library properties at right. Select /path/to/appcompat/ and Click Remove. Click Add to open the Project Selection dialog. From the list of available library projects, select v7 appcompat library and click OK.
When new versions of android are published, Google will have to support the older versions of android. So AppCompat is a set of support libraries which can be used to make the apps developed with newer versions work with older versions.
A PreferenceFragmentCompat is the entry point to using the Preference library. This Fragment displays a hierarchy of Preference objects to the user. It also handles persisting values to the device. To retrieve an instance of android.
PreferenceFragment
with AppCompatActivity
In AndroidStudio, choose File > New Project >...> SettingsActivity. This template uses a workaround that retrofits the native PreferenceFragment
to work with AppCompatActivity
, similar to the support.v4.Fragment
or the support.v7.PreferenceFragmentCompat
.
AppCompat
app. It's a quick approach when using the AS template, and you can stick to the existing Preference docs and workflows.support.v7.preference.PreferenceFragmentCompat
with AppCompatActivity
ColorPicker
or FontPreferences
).Should you choose not to use Solution 1 (I'm still not sure which of the two is more future proof), there are a couple of drawbacks when using support.v7.preference
.
Important drawbacks of using Solution 2 are mentioned below.
Dependencies:
dependencies {
...
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:preference-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1'
}
Theme:
You'll need to define a preferenceTheme
in your styles.xml, otherwise running your app will raise an exception.
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
You might wanna split this into different styles for 7+/14+/21+. A lot of people complain about this being buggy at the time of this writing. There is a very comprehensive answer available here.
Behavior changes: using the native preferences is extremely straight forward: all you need to do is define/maintain your preferences.xml
and use addPreferencesFromResource(R.xml.preferences)
within your PreferenceFragment
. Custom preferences are easily done by sub-classing DialogPreference
, and then just referenced to within the preferences.xml
... bam, works.
Unfortunately, support.v7.preference
has had everything related to dealing with Fragment
stripped out, making it loose a lot of it's built-in functionality. Instead of just maintaining an XML, you now have to sub-class and override a lot of stuff, all of which is unfortunately undocumented.
PreferenceScreens: PreferenceScreens
are no longer managed by the framework. Defining a PreferenceScreen
in your preference.xml
(as described in the docs) will display the entry, but clicking on it does nothing. It's now up to you to deal with displaying and navigating sub-screens. Boring.
There is one approach (described here), adding a PreferenceFragmentCompat.OnPreferenceStartScreenCallback
to your PreferenceFragmentCompat
. While this approach is quickly implemented, it simply swaps the content of the existing preference fragment. Downside is: there is no back navigation, you're always 'at the top', which isn't very intuitive for the user.
In another approach (described here), you'll also have to manage the back stack in order to achieve back navigation as expected. This uses preferenceScreen.getKey()
as a root for each newly created/displayed fragment.
When doing so, you might also stumble over the PreferenceFragments
being transparent by default and adding up oddly on top of each other. People tend to override PreferenceFragmentCompat.onViewCreated()
to add something like
// Set the default white background in the view so as to avoid transparency
view.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.background_material_light));
Custom DialogPreference: Making your own preferences has also gone from trivial to boring. DialogPreference
now has anything that deals with the actual dialog, removed. That bit now lives in PreferenceDialogFragmentCompat
. So you'll have to sub-class both, then deal with creating the dialog and displaying it yourself (explained here).
Looking at the source of PreferenceFragmentCompat.onDisplayPreferenceDialog()
shows that it knows how to deal with exactly 2 dialog preferences (EditTextPreference
, ListPreference
), everything else you'll have to implement yourself using OnPreferenceDisplayDialogCallback
s... one wonders, why there is no functionality to handle sub-class of DialogPreference
!
Here is some code that implements most of these workarounds and boxes them in a lib module:
https://github.com/mstummer/extended-preferences-compat.git
Main intentions were:
Activity
and PreferenceFragment
in each app/projects. preference.xml
is now again the only per-project file to change/maintain.PreferenceScreens
(sub-screens) as expected.DialogPreference
to restore the native behavior.DialogPreference
.Don't think it's clean enough to be just used out of the box, but it might give you some hints when dealing with similar issues. Give it a spin and let me know if you've got any suggestions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With