PreferenceActivity Android 4.0 и более ранних версий

Попробовав различные действия в ApiDemos для Android 4.0, я вижу в коде, что некоторые методы устарели в PreferencesFromCode.java, например.

Итак, мой вопрос: если я использую PreferenceFragment, будет ли он работать для всех версий или только 3.0 или 4.0 и выше?

Если да, то что я должен использовать, это работает и для 2.2 и 2.3?

Ответ 1

PreferenceFragment не будет работать с 2.2 и 2.3 (только для уровня API 11 и выше). Если вы хотите предложить лучший пользовательский интерфейс и по-прежнему поддерживать старые версии Android, лучшей практикой здесь является реализация двух классов PreferenceActivity и решение во время выполнения, которое нужно вызвать. Однако этот метод по-прежнему включает вызывать устаревшие API, но вы не можете этого избежать.

Так, например, у вас есть 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>

и стандартный preferences.xml (который не сильно изменился с более низких уровней API):

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="...">
    ...
</PreferenceScreen>

Затем вам понадобится реализация PreferenceFragment:

public static class PrefsFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
}

И, наконец, вам нужны две реализации PreferenceActivity, для уровней API, поддерживающих или не поддерживающих PreferenceFragments:

public class PreferencesActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
        addPreferencesFromResource(R.xml.other);
    }
}

и

public class OtherPreferencesActivity extends PreferenceActivity {
    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.preference_headers, target);
    }
}

В тот момент, когда вы хотите отобразить экран предпочтений для пользователя, вы решаете, с чего начать:

if (Build.VERSION.SDK_INT < 11) {
    startActivity(new Intent(this, PreferencesActivity.class));
} else {
    startActivity(new Intent(this, OtherPreferencesActivity.class));
}

Итак, в основном, у вас есть xml файл на фрагмент, вы загружаете каждый из этих xml файлов вручную для уровней API < 11, и обе действия используют те же настройки.

Ответ 2

@Mef. Ваш ответ может быть упрощен еще больше, так что вам не нужны оба PreferencesActivity и OtherPreferencesActivity (с 2 PrefsActivities - PITA).

Я обнаружил, что вы можете поместить метод onBuildHeaders() в свою PreferencesActivity, и никакие ошибки не будут выпущены версиями Android до версии v11. Наличие loadHeadersFromResource() внутри onBuildHeaders не выбрасывал и исключение на 2.3.6, а делал на Android 1.6. Однако после некоторого вмешательства я обнаружил, что следующий код будет работать во всех версиях, так что требуется только одно действие (что значительно упрощает).

public class PreferencesActivity extends PreferenceActivity {
    protected Method mLoadHeaders = null;
    protected Method mHasHeaders = null;

    /**
     * Checks to see if using new v11+ way of handling PrefFragments.
     * @return Returns false pre-v11, else checks to see if using headers.
     */
    public boolean isNewV11Prefs() {
        if (mHasHeaders!=null && mLoadHeaders!=null) {
            try {
                return (Boolean)mHasHeaders.invoke(this);
            } catch (IllegalArgumentException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {
            }
        }
        return false;
    }

    @Override
    public void onCreate(Bundle aSavedState) {
        //onBuildHeaders() will be called during super.onCreate()
        try {
            mLoadHeaders = getClass().getMethod("loadHeadersFromResource", int.class, List.class );
            mHasHeaders = getClass().getMethod("hasHeaders");
        } catch (NoSuchMethodException e) {
        }
        super.onCreate(aSavedState);
        if (!isNewV11Prefs()) {
            addPreferencesFromResource(R.xml.preferences);
            addPreferencesFromResource(R.xml.other);
        }
    }

    @Override
    public void onBuildHeaders(List<Header> aTarget) {
        try {
            mLoadHeaders.invoke(this,new Object[]{R.xml.pref_headers,aTarget});
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        } catch (InvocationTargetException e) {
        }   
    }
}

Таким образом вам нужно только одно действие, одна запись в вашем AndroidManifest.xml и одна строка при вызове ваших настроек:

startActivity(new Intent(this, PreferencesActivity.class);

ОБНОВЛЕНИЕ октябрь 2013: Eclipse/Lint предупредит вас об использовании устаревшего метода, но просто игнорирует предупреждение. Мы используем этот метод только тогда, когда мы должны это делать, когда у нас нет предпочтений стиля v11 + и он должен его использовать, и это нормально. Не бойтесь устаревшего кода, когда вы его учли, Android не будет удалять устаревшие методы в ближайшее время. Если это когда-либо происходило, вам больше не понадобится этот класс, так как вы будете вынуждены использовать только новые устройства. Устаревший механизм заключается в том, чтобы предупредить вас, что есть лучший способ обработать что-то в последней версии API, но после того, как вы учли его, вы можете смело игнорировать это предупреждение. Удаление всех вызовов на устаревшие методы приведет только к тому, что ваш код будет работать только на более новых устройствах, тем самым отрицая необходимость обратной совместимости вообще.

Ответ 3

Там новый lib, который может помочь.

UnifiedPreference - это библиотека для работы со всеми версиями Android Preference от API v4 и выше.

Ответ 4

Проблема с предыдущими ответами заключается в том, что он будет складывать все настройки на один экран на устройствах pre-Honecomb (из-за нескольких вызовов addPreferenceFromResource()).

Если вам нужен первый экран в виде списка, а затем экран с настройками (например, с использованием заголовков предпочтений), вы должны использовать Официальное руководство по совместимым настройкам

Ответ 5

Я хотел бы указать, что если вы начнете с http://developer.android.com/guide/topics/ui/settings.html#PreferenceHeaders и проделаете свой путь до раздела "Поддержка старых версий с предпочтением заголовки", это будет иметь больше смысла. Руководство там очень полезно и хорошо работает. Вот явный пример, следующий за их руководством:

Итак, начните с файла preference_header_legacy.xml для систем Android до HoneyComb

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference 
    android:title="OLD Test Title"
    android:summary="OLD Test Summary"  >
    <intent 
        android:targetPackage="example.package"
        android:targetClass="example.package.SettingsActivity"
        android:action="example.package.PREFS_ONE" />
</Preference>

Далее создайте файл preference_header.xml для систем Android с помощью HoneyComb +

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header 
    android:fragment="example.package.SettingsFragmentOne"
    android:title="NEW Test Title"
    android:summary="NEW Test Summary" />
</preference-headers>

Далее создайте файл preferences.xml, чтобы сохранить ваши настройки...

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
        <CheckBoxPreference
        android:key="pref_key_auto_delete"
        android:summary="@string/pref_summary_auto_delete"
        android:title="@string/pref_title_auto_delete"
        android:defaultValue="false" />
</PreferenceScreen>

Далее создайте файл SettingsActivity.java

package example.project;
import java.util.List;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceActivity;

public class SettingsActivity extends PreferenceActivity{
final static String ACTION_PREFS_ONE = "example.package.PREFS_ONE";

@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    String action = getIntent().getAction();
    if (action != null && action.equals(ACTION_PREFS_ONE)) {
        addPreferencesFromResource(R.xml.preferences);
    }
    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        // Load the legacy preferences headers
        addPreferencesFromResource(R.xml.preference_header_legacy);
    }
}

@SuppressLint("NewApi")
@Override
public void onBuildHeaders(List<Header> target) {
    loadHeadersFromResource(R.xml.preference_header, target);
}
}

Далее создайте класс SettingsFragmentOne.java

package example.project;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.preference.PreferenceFragment;

@SuppressLint("NewApi")
public class SettingsFragmentOne extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.preferences);
}
}

AndroidManifest.xml, добавил этот блок между тегами <application>

<activity 
   android:label="@string/app_name"
   android:name="example.package.SettingsActivity"
   android:exported="true">
</activity>

и, наконец, для тега <wallpaper>...

<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/description"
android:thumbnail="@drawable/ic_thumbnail"
android:settingsActivity="example.package.SettingsActivity"
/>

Ответ 6

Я использую эту библиотеку, которая имеет AAR в mavenCentral, поэтому вы можете легко включить ее, если используете Gradle.

compile 'com.github.machinarius:preferencefragment:0.1.1'