36

例如,在 Android 4.0 的 ApiDemos 中尝试不同的偏好活动,我在代码中看到某些方法在 PreferencesFromCode.java 中已被弃用。

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

如果是这样,我应该使用什么也适用于 2.2 和 2.3?

4

6 回答 6

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最后,对于支持或不支持的 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 文件,为 API 级别 < 11 手动加载每个 xml 文件,并且两个活动都使用相同的首选项。

于 2012-04-21T10:59:21.017 回答
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 不会很快删除已弃用的方法。如果它真的发生了,你甚至不再需要这个类,因为你将被迫只针对较新的设备。不推荐使用的机制会警告您在最新的 API 版本上有更好的方法来处理某些事情,但是一旦您考虑了它,您就可以放心地忽略该警告。删除对不推荐使用的方法的所有调用只会导致您的代码仅在较新的设备上运行 - 因此完全不需要向后兼容。

于 2012-07-04T22:42:09.327 回答
6

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

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

于 2013-03-01T15:55:17.900 回答
5

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

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

于 2012-10-14T12:15:50.637 回答
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"
/>
于 2014-01-25T14:55:31.250 回答
1

我正在使用这个库,它有一个AARin,mavenCentral因此如果您正在使用它,您可以轻松地包含它Gradle

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

于 2014-03-17T18:45:10.623 回答