返回

如何拯救你的列表:保存和恢复 RecyclerView 的滚动位置

Android

如何保存和恢复 ListView 的滚动位置

引言

在使用 ListView 或 RecyclerView 时,保持用户返回列表时的滚动位置非常重要。这可以提高用户体验并让应用程序更加直观。本文将深入探讨使用 Android 的 RecyclerView 实现滚动位置持久化的两种方法:使用 Parcelable 和使用 SavedState。

问题:滚动位置丢失

当用户从 ListView 或 RecyclerView 导航到其他屏幕时,滚动位置通常会重置为顶部。这可能会令人沮丧,尤其是在用户已滚动到特定位置时。

解决方案:使用 Parcelable

什么是 Parcelable?

Parcelable 是一个 Android 接口,允许将对象状态序列化为一个可传输的对象。通过实现 Parcelable,我们可以存储滚动位置和其他相关信息,以便在需要时还原。

步骤:

  1. 创建一个实现 Parcelable 接口的类来存储滚动位置和相关信息。
  2. onSaveInstanceState() 方法中,将 Parcelable 对象保存在 Bundle 中。
  3. onRestoreInstanceState() 方法中,从 Bundle 中恢复 Parcelable 对象。
  4. 使用 Parcelable 对象中的信息,将 RecyclerView 滚动到恢复的滚动位置。

解决方案:使用 SavedState

什么是 SavedState?

SavedState 是 Android API 中的一个类,它提供了一种比 Parcelable 更简单的方法来保存和恢复 UI 状态。与 Parcelable 类似,它允许存储滚动位置和其他信息。

步骤:

  1. 创建一个扩展 RecyclerView.SavedState() 的 SavedState 提供程序类来存储滚动位置和相关信息。
  2. onSaveInstanceState() 方法中,使用 RecyclerView.onSaveInstanceState() 保存 SavedState。
  3. onRestoreInstanceState() 方法中,使用 RecyclerView.onRestoreInstanceState() 恢复 SavedState。
  4. 使用 SavedState 对象中的信息,将 RecyclerView 滚动到恢复的滚动位置。

比较:Parcelable 与 SavedState

  • Parcelable 是一种更通用的方法,它可以用于保存和恢复任何类型的对象,而不局限于 UI 状态。
  • SavedState 是一种专门为保存和恢复 UI 状态而设计的类,它提供了更简洁的 API。

代码示例

Parcelable 示例:

// 自定义 Parcelable 类
class ScrollPositionParcelable implements Parcelable {
    private int scrollPosition;

    // Parcelable 方法
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(scrollPosition);
    }

    public static final Creator<ScrollPositionParcelable> CREATOR = new Creator<ScrollPositionParcelable>() {
        @Override
        public ScrollPositionParcelable createFromParcel(Parcel in) {
            return new ScrollPositionParcelable(in);
        }

        @Override
        public ScrollPositionParcelable[] newArray(int size) {
            return new ScrollPositionParcelable[size];
        }
    };

    // 构造函数
    public ScrollPositionParcelable(int scrollPosition) {
        this.scrollPosition = scrollPosition;
    }

    // 从 Parcel 中恢复对象
    private ScrollPositionParcelable(Parcel in) {
        scrollPosition = in.readInt();
    }
}

// 保存和恢复 Parcelable 对象
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable("scroll_position", new ScrollPositionParcelable(mRecyclerView.computeVerticalScrollOffset()));
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState != null) {
        ScrollPositionParcelable scrollPosition = savedInstanceState.getParcelable("scroll_position");
        if (scrollPosition != null) {
            mRecyclerView.scrollToPosition(scrollPosition.scrollPosition);
        }
    }
}

SavedState 示例:

// 自定义 SavedState 提供程序类
class ScrollPositionSavedState extends RecyclerView.SavedState {
    private int scrollPosition;

    // SavedState 方法
    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeInt(scrollPosition);
    }

    public static final Creator<ScrollPositionSavedState> CREATOR = new Creator<ScrollPositionSavedState>() {
        @Override
        public ScrollPositionSavedState createFromParcel(Parcel in) {
            return new ScrollPositionSavedState(in);
        }

        @Override
        public ScrollPositionSavedState[] newArray(int size) {
            return new ScrollPositionSavedState[size];
        }
    };

    // 构造函数
    private ScrollPositionSavedState(Parcel in) {
        super(in);
        scrollPosition = in.readInt();
    }

    public ScrollPositionSavedState(Parcelable superState, int scrollPosition) {
        super(superState);
        this.scrollPosition = scrollPosition;
    }
}

// 保存和恢复 SavedState 对象
@Override
public Parcelable onSaveInstanceState() {
    return new ScrollPositionSavedState(super.onSaveInstanceState(), mRecyclerView.computeVerticalScrollOffset());
}

@Override
public void onRestoreInstanceState(Parcelable state) {
    if (state instanceof ScrollPositionSavedState) {
        ScrollPositionSavedState scrollState = (ScrollPositionSavedState) state;
        super.onRestoreInstanceState(scrollState.getSuperState());
        mRecyclerView.scrollToPosition(scrollState.scrollPosition);
    } else {
        super.onRestoreInstanceState(state);
    }
}

常见问题解答

1. 为什么需要保存滚动位置?
答:保存滚动位置可以提高用户体验,让用户在返回列表时继续从上次离开的位置查看列表。

2. 什么时候应该保存滚动位置?
答:在 onSaveInstanceState() 方法中保存滚动位置,在 onRestoreInstanceState() 方法中恢复滚动位置。

3. 如何判断是否需要恢复滚动位置?
答:检查 savedInstanceState 是否为 null。如果 savedInstanceState 不为 null,则需要恢复滚动位置。

4. 哪个方法更好:Parcelable 还是 SavedState?
答:Parcelable 是一种更通用的方法,而 SavedState 是一种专门用于保存和恢复 UI 状态的方法。根据你的具体需求选择一种方法。

5. 是否可以同时使用 Parcelable 和 SavedState?
答:可以,但通常没有必要。SavedState 已经提供了一个方便的方法来保存和恢复 UI 状态。

结论

通过使用 Parcelable 或 SavedState,你可以轻松地保留 ListView 或 RecyclerView 的滚动位置,即使在用户返回到先前屏幕然后再次打开列表时也是如此。这将增强用户体验并使你的应用程序更加用户友好。根据你的特定需求和偏好,选择最适合你的方法。