PreferenceActivity Android 4.0及更早版本

jus*_*ser 36 android preferenceactivity

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

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

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

Leo*_*Leo 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>
Run Code Online (Sandbox Code Playgroud)

和标准preferences.xml(自API水平较低以来没有太大变化):

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="...">
    ...
</PreferenceScreen>
Run Code Online (Sandbox Code Playgroud)

然后你需要一个实现PreferenceFragment:

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

最后,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

和:

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

在您要向用户显示首选项屏幕的位置,您可以决定从哪一个开始:

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

基本上,每个片段都有一个xml文件,您可以手动加载每个xml文件,API级别<11,并且两个活动都使用相同的首选项.

  • 好主意,唯一的问题是当你不是一个开始活动的人时,你就无法决定从哪开始.那,你正在使用API​​级别检测而不是功能检测(使用ClassLoaders来检查是否存在PreferenceFragment).编辑他们的build.prop文件从API 10升级到11(或更高版本)的用户将会崩溃. (3认同)
  • 现在记录在这里:http://developer.android.com/guide/topics/ui/settings.html#BackCompatHeaders (3认同)

Unc*_*key 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) {
        }   
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,您只需要一个活动,AndroidManifest.xml中的一个条目和调用首选项时的一行:

startActivity(new Intent(this, PreferencesActivity.class);
Run Code Online (Sandbox Code Playgroud)

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

  • 您在1.6上遇到的异常是由于Dalvik尝试加载所有方法(或检查它们的存在),即使它们未在运行时调用.这种行为在2.0中已经改变,所以这就是2.3.6中没有例外的原因. (3认同)

sco*_*yab 6

有一个新的库可能会有所帮助.

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


Dav*_*vra 5

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

如果您需要第一个屏幕作为列表,然后是带有首选项的屏幕(例如使用首选项标题),您应该使用官方指南来兼容首选项