java - 双 Pane 首选项屏幕的问题

标签 java android android-fragments android-preferences preferenceactivity

问题
将设备从单 Pane 纵向 PreferenceScreen 旋转到两 Pane 横向 PreferenceScreen 会导致横向仅显示为单 Pane 。查看标题屏幕时不会发生。

设置
这仅适用于 ICS 及更高版本。我有一个加载 preference-headersPreferenceActivity。每个 header 都与一个 Fragment 链接,后者又会加载一个 PreferenceScreen。千篇一律。

详情
一切都很顺利,直到我注意到 Android 只会自动切换到某些屏幕的两 Pane 外观。经过一些研究,我从 Commonsware post 中了解到Android 只会为 sw720dp 这样做。如果你问我有点浪费,因为许多设备有足够的空间容纳两个 Pane 。所以我重写了 onIsMultiPane() 方法,为 w600dp 及更高版本返回 true。像魅力一样工作....有点。

给定一个将在纵向显示单 Pane 和在横向显示双 Pane 的设备;以纵向查看标题并旋转到横向,效果很好。但是,如果选择一个标题并以纵向模式加载它的后续屏幕,然后旋转到横向,设备将保持单 Pane 而不是切换回双 Pane 。如果您然后返回导航到标题屏幕,它将返回双 Pane 外观,但不会预先选择标题。因此,详细 Pane 保持空白。

这是预期的行为吗?无论如何要解决它?我也尝试覆盖 onIsHidingHeaders() 但这只是导致所有内容都显示为空白屏幕。

代码
偏好 Activity :

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

@Override
public boolean onIsMultiPane() {
    return getResources().getBoolean(R.bool.pref_prefer_dual_pane);
}
}


首选项 header fragment :

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

public static ExpansionsFragment newInstance() {
    ExpansionsFragment frag = new ExpansionsFragment();

    return frag;
}
}

最佳答案

问题已解决
随着这个问题变得如此受欢迎,我决定再次重新审视这个问题,看看我是否能找到解决方案......我做到了。找到了一个很好的解决方法,可以解决显示单 Pane 而不是双 Pane 的问题,并确保在双 Pane 模式下始终预先选择标题。

如果您不关心解释,您可以直接跳到代码。如果您不关心 ICS,可以删除很多 header 跟踪代码,因为 JB 为 header 数组列表添加了一个 getter。

双 Pane 问题
在单 Pane 模式或双 Pane 模式下查看首选项标题列表时,只会创建一个 PreferenceActivity,并且在两种情况下都是相同的 Activity 。因此,在处理将切换 Pane 模式的屏幕旋转时从来没有问题。

但是,在单 Pane 模式下,当您单击标题时,相应的 fragment 会附加到新的 PreferenceActivity。这个包含 PreferenceActivity 的新 fragment 从不调用 onBuildHeaders()。为什么会这样?它不需要显示它们。这就是问题所在。

当将该 fragment 旋转到双 Pane 模式时,它没有要显示的任何标题列表,因此它只是继续仅显示 fragment 。即使它确实显示了标题的列表,您也会遇到一些 backstack 问题,因为您现在将拥有两个显示标题的 PreferenceActivity 副本。继续点击足够多的标题,您将获得相当长的 Activity 堆栈供用户浏览。结果,答案很简单。只需 finish() Activity 。然后它将加载具有标题列表的原始 PreferenceActivity 并正确显示双 Pane 模式。

自动选择标题
需要解决的下一个问题是使用新修复程序在单 Pane 模式和双 Pane 模式之间切换不会自动选择标题。您只剩下一个标题列表,并且没有加载任何详细信息 fragment 。这个修复并不那么简单。基本上,您只需要跟踪上次单击的标题并确保在 PreferenceActivity 创建期间...始终选择标题。

这最终在 ICS 中有点烦人,因为 API 没有为内部跟踪的 header 列表公开 getter。 Android 确实已经保留了该列表,您可以通过使用相同的私有(private)存储的内部字符串键从技术上检索它,但这只是一个糟糕的设计选择。相反,我建议您自己手动再次持久化它。

如果您不关心 ICS,那么您可以只使用 JB 中公开的 getHeaders() 方法,而不必担心任何保存/恢复状态的东西。

代码

public class SettingsActivity extends PreferenceActivity {
private static final String STATE_CUR_HEADER_POS = "Current Position";
private static final String STATE_HEADERS_LIST   = "Headers List";

private int mCurPos = AdapterView.INVALID_POSITION;  //Manually track selected header position for dual pane mode
private ArrayList<Header> mHeaders;  //Manually track headers so we can select one. Required to support ICS.  Otherwise JB exposes a getter instead.

@Override
public void onBuildHeaders(List<Header> target) {
    loadHeadersFromResource(R.xml.preference, target);
    mHeaders = (ArrayList<Header>) target;  //Grab a ref of the headers list
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //This is the only code required for ensuring a dual pane mode shows after rotation of a single paned preference screen
    if (onIsMultiPane() && onIsHidingHeaders()) {
        finish();
    }
}

@Override
public boolean onIsMultiPane() {
    //Override this if you want dual pane to show up on smaller screens
    return getResources().getBoolean(R.bool.pref_prefer_dual_pane);
}

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);

    //Intercept a header click event to record its position.
    mCurPos = position;
}

@Override
protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);

    //Retrieve our saved header list and last clicked position and ensure we switch to the proper header.
    mHeaders = state.getParcelableArrayList(STATE_HEADERS_LIST);
    mCurPos = state.getInt(STATE_CUR_HEADER_POS);
    if (mHeaders != null) {
        if (mCurPos != AdapterView.INVALID_POSITION) {
            switchToHeader(mHeaders.get(mCurPos));
        } else {
            switchToHeader(onGetInitialHeader());
        }
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    //Persist our list and last clicked position
    if (mHeaders != null && mHeaders.size() > 0) {
        outState.putInt(STATE_CUR_HEADER_POS, mCurPos);
        outState.putParcelableArrayList(STATE_HEADERS_LIST, mHeaders);
    }
}
}

关于java - 双 Pane 首选项屏幕的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18138642/

相关文章:

java - Android:ListView 中 getCount() 和 getChildCount() 之间的区别

android - 如何使用保存的首选项android存储和检索(键,值)类型的数据

java - Android应用设备体积

android - 如何播放仅适用于我的应用程序的推送通知到达的铃声?

android - android 中的 Activity 与 Fragment

android - 嵌套 fragment 没有找到 id 的 View

java - Gradle javaCompile 任务是否总是重新编译所有 java 类(包括未更改的?)

java - 将字符串对象转换为方程

java - 无法从java连接到mysql服务器

android - 从 Fragment 到 Activity 的数据通信不起作用