PreferenceActivity Android 4.0 and earlier
PreferenceFragment
will not work on 2.2 and 2.3 (only API level 11 and above). If you want to offer the best user experience and still support older Android versions, the best practice here seems to be to implement two PreferenceActivity
classes and to decide at runtime which one to invoke. However, this method still includes calling deprecated APIs, but you can't avoid that.
So for instance, you have a preference_headers.xml
:
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >
<header android:fragment="your.package.PrefsFragment"
android:title="...">
<extra android:name="resource" android:value="preferences" />
</header>
</preference-headers>
and a standard preferences.xml
(which hasn't changed much since lower API levels):
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="...">
...
</PreferenceScreen>
Then you need an implementation of PreferenceFragment
:
public static class PrefsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
And finally, you need two implementations of PreferenceActivity
, for API levels supporting or not supporting PreferenceFragments
:
public class PreferencesActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
addPreferencesFromResource(R.xml.other);
}
}
and:
public class OtherPreferencesActivity extends PreferenceActivity {
@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}
}
At the point where you want to display the preference screen to the user, you decide which one to start:
if (Build.VERSION.SDK_INT < 11) {
startActivity(new Intent(this, PreferencesActivity.class));
} else {
startActivity(new Intent(this, OtherPreferencesActivity.class));
}
So basically, you have an xml file per fragment, you load each of these xml files manually for API levels < 11, and both Activities use the same preferences.
Android - Should I keep using PreferenceActivity?
Just because it is deprecated does not mean it stops working - newer devices still support PreferenceActivity
without issue. However, on larger devices (such as 10" tablets), the dual pane support of PreferenceFragment
/PreferenceHeaders
looks much better. Until Google adds them to the Support Library, you can use third party libraries such as UnifiedPreferences to use a single API for all versions of Android.
How could I use the same set of preference screens for all Android versions from 2.X to 4.X?
If you are on the latest ADT plugin, there is an option to easily create a preference Activity that supports most older Android versions as well as all the new ones.
Right click on your project -> Other -> Android Activity
Then choose SettingsActivity
The Activity created will take take care of working with both high and low API versions since it uses if statements to choose the appropriate method of displaying the preferences.
EDIT
A good point was brought up: Phone-Sized devices, regardless of API version use the old PreferenceActivity
methods.
The quickest way to get API 11+ devices to use Fragments
is to remove !isXLargeTablet(context);
from isSimplePreferences()
private static boolean isSimplePreferences(Context context) {
return ALWAYS_SIMPLE_PREFS
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
}
However, now the user has more navigation to do.
This is because onBuildHeaders()
is called.
To get rid of this, we will need to make our own PreferenceFragment that adds each xml resource.
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class AllPreferencesFragment extends PreferenceFragment{
@Override
public void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_general);
// Add 'notifications' preferences, and a corresponding header.
PreferenceCategory fakeHeader = new PreferenceCategory(getActivity());
fakeHeader.setTitle(R.string.pref_header_notifications);
getPreferenceScreen().addPreference(fakeHeader);
addPreferencesFromResource(R.xml.pref_notification);
// Add 'data and sync' preferences, and a corresponding header.
fakeHeader = new PreferenceCategory(getActivity());
fakeHeader.setTitle(R.string.pref_header_data_sync);
getPreferenceScreen().addPreference(fakeHeader);
addPreferencesFromResource(R.xml.pref_data_sync);
// Bind the summaries of EditText/List/Dialog/Ringtone preferences to
// their values. When their values change, their summaries are updated
// to reflect the new value, per the Android Design guidelines.
bindPreferenceSummaryToValue(findPreference("example_text"));
bindPreferenceSummaryToValue(findPreference("example_list"));
bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
bindPreferenceSummaryToValue(findPreference("sync_frequency"));
}
}
If you can determine the screen size from outside the Activity
that launches the settings, you can specify a fragment for it to launch via EXTRA_SHOW_FRAGMENT
i.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, "com.example.test.SettingsActivity$AllPreferencesFragment");
Or you can have the SettingsActivity
determine whether or not to show this Fragment (assuming you're happy with the isXLargeTablet()
method.
Change onBuildHeaders()
to:
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target) {
if (!isSimplePreferences(this) && isXLargeTablet(this)) {
loadHeadersFromResource(R.xml.pref_headers, target);
}
}
Add this method:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupNewApiPhoneSizePreferences() {
if (!isXLargeTablet(this) && Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB){
getFragmentManager().beginTransaction().replace(android.R.id.content, new AllPreferencesFragment()).commit();
}
}
And in onPostCreate()
add the method call.
setupNewApiPhoneSizePreferences();
This should now use non-deprecated calls from API 11 onwards.
How can I show a preferences screen on older Android devices i.e. Gingerbread and Froyo?
Neither ActionBarSherlock nor the Android Support Library provides support for Preference Fragments.
You need to revert to Perference Activities as explained in the Settings Documentation.
There are a few old questions that discuss the same topic:
- PreferenceActivity Android 4.0 and earlier
- Was PreferenceFragment intentionally excluded from the compatibility package?
PreferenceFragment support all API issue
Finally it works perfectly :
i found the solution in the preference theme which already refered in manifest as bellow :
<activity android:name=".Prefs"
android:label="@string/app_name"
android:theme="@style/PreferencesTheme">
PreferencesTheme.XML :
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="CustomWindowTitleBackground" />
<style name="PreferencesTheme" parent="android:Theme">
<item name="android:windowTitleSize">35dp</item>
<item name="android:windowTitleBackgroundStyle">@style/CustomWindowTitleBackground</item>
</style>
</resources>
What to use instead of addPreferencesFromResource in a PreferenceActivity?
No alternative method is provided in the method's description because the preferred approach (as of API level 11) is to instantiate PreferenceFragment objects to load your preferences from a resource file. See the sample code here: PreferenceActivity
What is the right method implementing Android (4.0+) Preferences?
The answer is, it depends on what platform you are developing for. The PreferenceFragment is for 3.0+ and is now considered the proper way to do it. Unfortunately, there is not an equivalent in the support library for the older platforms, so for those you need to use a PreferenceActivity.
See here for a more in-depth explanation and a link to an example implementing both (uses the proper type depending on device platform).
Keep the actionbar displayed in when changing PreferenceScreen
I finally managed to find a way to do this.
It's kind of ugly but it works.
First I add an the same Intent to every PreferenceScreen definition in my preferences.xml file (make sure to update the value of the extra parameter)
<PreferenceScreen
android:key="pref1"
android:summary="Summary1"
android:title="Title1" >
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="my.package"
android:targetClass="my.package.activity.PreferencesActivity" >
<extra android:name="page" android:value="pref1" />
</intent>
...
</PreferenceScreen>
BTW my.package.activity.PreferencesActivity is my current Preference Activity
Then I add an intent-filter in the Manifest
<activity
android:name=".activity.PreferencesActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/settings" >
<intent-filter android:label="Pref" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.PREFERENCE" />
</intent-filter>
</activity>
I add some code in the PreferenceActivity to handle this
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences_activity);
this.fragment = new PreferencesFragment();
this.fragment.setActivityIntent(getIntent());
getFragmentManager().beginTransaction()
.replace(R.id.container, this.fragment).commit();
}
Finally I add the following code in my PreferencesFragment class
public void setActivityIntent(final Intent activityIntent) {
if (activityIntent != null) {
if (Intent.ACTION_VIEW.equals(activityIntent.getAction())) {
if (intent.getExtras() != null) {
final String page = intent.getExtras().getString("page");
if (!TextUtils.isEmpty(page)) {
openPreferenceScreen(page);
}
}
}
}
private void openPreferenceScreen(final String screenName) {
final Preference pref = findPreference(screenName);
if (pref instanceof PreferenceScreen) {
final PreferenceScreen preferenceScreen = (PreferenceScreen) pref;
((PreferencesActivity) getActivity()).setTitle(preferenceScreen.getTitle());
setPreferenceScreen((PreferenceScreen) pref);
}
}
Related Topics
Toggle Button Using Two Image on Different State
Android Type_Linear_Acceleration Sensor - What Does It Show
Proguard: Can't Find Referenced Class Com.Google.Android.Gms.R
Progressbar Togther with Asynctask
How to Change the Color of a Switchcompat from Appcompat Library
Handle Button Click Inside a Row in Recyclerview
Bluetooth and Wifi Printing for Android
Show and Hide a View With a Slide Up/Down Animation
Gradle Build Fails on Lint Task
Compiling the Latest Openssl for Android
How to Get Android Device Screen Size
Android:Configchanges="Orientation" Does Not Work with Fragments
How to Share a Sharedpreferences File Across Two Different Android Apps
Supporting Multiple Screen Size - Android