PreferenceActivity Android 4.0及更早版本

时间:2012-04-17 07:06:34

标签: android preferenceactivity

在ApiDemos for Android 4.0中尝试不同的首选项活动,我在代码中看到,例如,在PreferencesFromCode.java中不推荐使用某些方法。

所以我的问题是:如果我使用PreferenceFragment,它是适用于所有版本还是仅适用于3.0或4.0及更高版本?

如果是这样,我应该使用哪种方法适用于2.2和2.3?

6 个答案:

答案 0 :(得分:59)

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的实现,用于支持或不支持PreferenceFragments的API级别:

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文件,你可以为API级别手动加载每个xml文件&lt; 11,两个活动都使用相同的首选项。

答案 1 :(得分:18)

@Mef您的答案可以进一步简化,因此您不需要PreferencesActivity和OtherPreferencesActivity(2个PrefsActivities是PITA)。

我发现您可以将onBuildHeaders()方法放入PreferencesActivity中,并且v11之前的Android版本不会抛出任何错误。在onBuildHeaders中使用loadHeadersFromResource()并没有在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年10月更新: Eclipse / Lint将警告您使用已弃用的方法,但只是忽略该警告。我们只在必要时使用该方法,即只要我们没有v11 +样式首选项并且必须使用它,这是可以的。当你考虑到它时,不要害怕被弃用的代码,Android不会很快删除已弃用的方法。如果它确实发生了,你将不再需要这个类,因为你将被迫只针对更新的设备。 Deprecated机制是为了警告您有一种更好的方法来处理最新的API版本,但是一旦你考虑到它,你就可以安全地忽略那时的警告。删除对已弃用方法的所有调用只会导致强制您的代码仅在较新的设备上运行 - 从而无需向后兼容。

答案 2 :(得分:6)

有一个newish lib可能会有所帮助。

  

UnifiedPreference是一个用于处理所有版本的库   API v4及更高版本的Android Preference包。

答案 3 :(得分:5)

之前的答案问题是它会将所有首选项堆叠到Honecomb设备上的单个屏幕上(由于多次调用addPreferenceFromResource())。

如果您需要第一个屏幕作为列表,然后是带有首选项的屏幕(例如使用首选项标题),则应使用Official guide to compatible preferences

答案 4 :(得分:2)

我想指出的是,如果您从http://developer.android.com/guide/topics/ui/settings.html#PreferenceHeaders开始,一直到“支持带有首选项标题的旧版本”这一部分,那就更有意义了。该指南非常有用,并且运作良好。以下是他们指南的明确示例:

首先在HoneyComb之前为android系统启动文件 preference_header_legacy.xml

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

接下来为使用HoneyComb +的Android系统创建文件 preference_header.xml

<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"
/>

答案 5 :(得分:1)

我正在使用this library,其AAR中有mavenCentral,因此如果您使用的是Gradle,则可以轻松将其包含在内。

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