Preferenceactivity Android 4.0 and Earlier

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
enter image description here

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.

Headers as root

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



Leave a reply



Submit