如何使用 RecyclerView.State 保存 RecyclerView 的滚动位置?

Posted

技术标签:

【中文标题】如何使用 RecyclerView.State 保存 RecyclerView 的滚动位置?【英文标题】:How to save RecyclerView's scroll position using RecyclerView.State? 【发布时间】:2015-03-05 03:45:15 【问题描述】:

我有一个关于 android 的 RecyclerView.State 的问题。

我正在使用 RecyclerView,如何使用 RecyclerView.State 并将其与 RecyclerView.State 绑定?

我的目的是保存 RecyclerView 的滚动位置

【问题讨论】:

看这里:***.com/a/65645664/3904109 【参考方案1】:

更新

recyclerview:1.2.0-alpha02 开始发布StateRestorationPolicy 已被引入。这可能是解决给定问题的更好方法。

android developers medium article 已涵盖此主题。

此外,@rubén-viguera 在下面的答案中分享了更多详细信息。 https://***.com/a/61609823/892500

旧答案

如果你使用的是LinearLayoutManager,它自带了预建的save apilinearLayoutManagerInstance.onSaveInstanceState()和restore apilinearLayoutManagerInstance.onRestoreInstanceState(...)

这样,您可以将返回的包裹保存到您的 outState。例如,

outState.putParcelable("KeyForLayoutManagerState", linearLayoutManagerInstance.onSaveInstanceState());

,并以您保存的状态恢复恢复位置。例如,

Parcelable state = savedInstanceState.getParcelable("KeyForLayoutManagerState");
linearLayoutManagerInstance.onRestoreInstanceState(state);

总而言之,您的最终代码将类似于

private static final String BUNDLE_RECYCLER_LAYOUT = "classname.recycler.layout";

/**
 * This is a method for Fragment. 
 * You can do the same in onCreate or onRestoreInstanceState
 */
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) 
    super.onViewStateRestored(savedInstanceState);

    if(savedInstanceState != null)
    
        Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
        recyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerLayoutState);
    


@Override
public void onSaveInstanceState(Bundle outState) 
    super.onSaveInstanceState(outState);
    outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, recyclerView.getLayoutManager().onSaveInstanceState());

编辑:您也可以使用与 GridLayoutManager 相同的 api,因为它是 LinearLayoutManager 的子类。感谢@wegsehen 的建议。

编辑:请记住,如果您还在后台线程中加载数据,则需要在 onPostExecute/onLoadFinished 方法中调用 onRestoreInstanceState 以在方向更改时恢复位置,例如

@Override
protected void onPostExecute(ArrayList<Movie> movies) 
    mLoadingIndicator.setVisibility(View.INVISIBLE);
    if (movies != null) 
        showMoviePosterDataView();
        mDataAdapter.setMovies(movies);
      mRecyclerView.getLayoutManager().onRestoreInstanceState(mSavedRecyclerLayoutState);
         else 
            showErrorMessage();
        
    

【讨论】:

这显然是最好的答案,为什么不被接受?请注意,任何 LayoutManager 都有,不仅是 LinearLayoutManager,还有 GridLayoutManager,如果不是,它也是一个错误的实现。 linearLayoutManager.onInstanceState() 总是返回 null --> 检查源代码。不幸的是没有工作。 RecyclerView 不会在自己的onSaveInstanceState 中保存其LayoutManager 的状态吗?查看支持库的 v24... 这适用于单个RecyclerView,但如果您在每个页面上都有ViewPagerRecycerView,则不能。 @Ivan,我认为它适用于片段(刚刚检查),但不适用于onCreateView,但在数据加载后。在此处查看@Farmaker 的答案。【参考方案2】:

商店

lastFirstVisiblePosition = ((LinearLayoutManager)rv.getLayoutManager()).findFirstCompletelyVisibleItemPosition();

恢复

((LinearLayoutManager) rv.getLayoutManager()).scrollToPosition(lastFirstVisiblePosition);

如果这不起作用,请尝试

((LinearLayoutManager) rv.getLayoutManager()).scrollToPositionWithOffset(lastFirstVisiblePosition,0)

把商店放在onPause() 并在onResume()中恢复

【讨论】:

这可行,但您可能需要在恢复后将lastFirstVisiblePosition 重置为0。当您有一个片段/活动在一个 RecyclerView 中加载不同的数据时,这种情况很有用,例如,文件资源管理器应用程序。 太棒了!它既满足从细节活动返回到列表,又满足屏幕旋转保存状态 如果我的recyclerView在SwipeRefreshLayout中怎么办? 为什么第一个恢复选项不起作用,但第二个会起作用? @MehranJalili 好像是RecyclerView【参考方案3】:

您打算如何使用RecyclerView.State 保存上次保存的位置?

您始终可以依靠良好的保存状态。扩展 RecyclerView 并覆盖 onSaveInstanceState() 和 onRestoreInstanceState():

    @Override
    protected Parcelable onSaveInstanceState() 
        Parcelable superState = super.onSaveInstanceState();
        LayoutManager layoutManager = getLayoutManager();
        if(layoutManager != null && layoutManager instanceof LinearLayoutManager)
            mScrollPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
        
        SavedState newState = new SavedState(superState);
        newState.mScrollPosition = mScrollPosition;
        return newState;
    

    @Override
    protected void onRestoreInstanceState(Parcelable state) 
        super.onRestoreInstanceState(state);
        if(state != null && state instanceof SavedState)
            mScrollPosition = ((SavedState) state).mScrollPosition;
            LayoutManager layoutManager = getLayoutManager();
            if(layoutManager != null)
              int count = layoutManager.getItemCount();
              if(mScrollPosition != RecyclerView.NO_POSITION && mScrollPosition < count)
                  layoutManager.scrollToPosition(mScrollPosition);
              
            
        
    

    static class SavedState extends android.view.View.BaseSavedState 
        public int mScrollPosition;
        SavedState(Parcel in) 
            super(in);
            mScrollPosition = in.readInt();
        
        SavedState(Parcelable superState) 
            super(superState);
        

        @Override
        public void writeToParcel(Parcel dest, int flags) 
            super.writeToParcel(dest, flags);
            dest.writeInt(mScrollPosition);
        
        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() 
            @Override
            public SavedState createFromParcel(Parcel in) 
                return new SavedState(in);
            

            @Override
            public SavedState[] newArray(int size) 
                return new SavedState[size];
            
        ;
    

【讨论】:

现在我还有一个问题,如何,不,RecyclerView.State 能做什么? 这真的有效吗?我已经尝试过了,但我不断收到这样的运行时异常: MyRecyclerView$SavedState cannot be cast to android.support.v7.widget.RecyclerView$SavedState 在恢复实例状态时,我们需要将 superState 传递给它的超类(recycler 视图),如下所示: super.onRestoreInstanceState(((ScrollPositionSavingRecyclerView.SavedState) state).getSuperState()); 如果你扩展 RecyclerView 这不起作用,你会得到 BadParcelException。 这些都不是必需的:如果您使用 LinearLayoutManager / GridLayoutManager 之类的东西,“滚动位置”已经为您自动保存。 (它是 LinearLayoutManager.SavedState 中的 mAnchorPosition)。如果您没有看到,那么您重新应用数据的方式有问题。【参考方案4】:

我想在离开我的列表活动时保存 Recycler View 的滚动位置,然后单击返回按钮返回。为这个问题提供的许多解决方案要么比需要的复杂得多,要么不适用于我的配置,所以我想我会分享我的解决方案。

首先将您的实例状态保存在 onPause 中,正如许多人所展示的那样。我认为这里值得强调的是,这个版本的 onSaveInstanceState 是来自 RecyclerView.LayoutManager 类的方法。

private LinearLayoutManager mLayoutManager;
Parcelable state;

        @Override
        public void onPause() 
            super.onPause();
            state = mLayoutManager.onSaveInstanceState();
        

让它正常工作的关键是确保在附加适配器后调用 onRestoreInstanceState,正如其他线程中所指出的那样。然而,实际的方法调用比许多人指出的要简单得多。

private void someMethod() 
    mVenueRecyclerView.setAdapter(mVenueAdapter);
    mLayoutManager.onRestoreInstanceState(state);

【讨论】:

这是正确的解决方案。 :D 对我来说 onSaveInstanceState 没有被调用,所以在 onPause 中保存状态似乎是一个更好的选择。我还弹出回栈以返回片段。 用于状态的加载。我在 onViewCreated 中做的。它似乎只在我处于调试模式并使用断点时才有效?或者如果我添加延迟并将执行放在协程中。【参考方案5】:

我在onCreate()中设置变量,在onPause()中保存滚动位置,在onResume()中设置滚动位置

public static int index = -1;
public static int top = -1;
LinearLayoutManager mLayoutManager;

@Override
public void onCreate(Bundle savedInstanceState)

    //Set Variables
    super.onCreate(savedInstanceState);
    cRecyclerView = ( RecyclerView )findViewById(R.id.conv_recycler);
    mLayoutManager = new LinearLayoutManager(this);
    cRecyclerView.setHasFixedSize(true);
    cRecyclerView.setLayoutManager(mLayoutManager);



@Override
public void onPause()

    super.onPause();
    //read current recyclerview position
    index = mLayoutManager.findFirstVisibleItemPosition();
    View v = cRecyclerView.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - cRecyclerView.getPaddingTop());


@Override
public void onResume()

    super.onResume();
    //set recyclerview position
    if(index != -1)
    
        mLayoutManager.scrollToPositionWithOffset( index, top);
    

【讨论】:

保留项目的偏移量。好的。谢谢!【参考方案6】:

更新:从1.2.0-alpha02 版本开始,有一个新的 API 可以控制何时发生状态恢复(包括滚动位置)。

RecyclerView.Adapter惰性状态恢复:

向 RecyclerView.Adapter 类添加了一个新 API,允许 Adapter 控制何时恢复布局状态。

例如,您可以调用:

myAdapter.setStateRestorationStrategy(StateRestorationStrategy.WHEN_NOT_EMPTY);

让 RecyclerView 等到 Adapter 不为空后再恢复滚动位置。

另见:

RecyclerView.Adapter#setStateRestorationPolicy RecyclerView.Adapter.StateRestorationPolicy

支持库中捆绑的所有布局管理器都已经知道如何保存和恢复滚动位置。

RecyclerView/ScrollView/whatever 需要有一个android:id 才能保存其状态。

默认情况下,当满足以下条件时,会在第一次布局时恢复滚动位置:

一个布局管理器附加到RecyclerView 适配器连接到RecyclerView

通常你在 XML 中设置布局管理器,所以你现在要做的就是

    用数据加载适配器 然后将适配器连接到RecyclerView

您可以随时执行此操作,不限于Fragment.onCreateViewActivity.onCreate

示例:确保每次更新数据时都附加适配器。

viewModel.liveData.observe(this) 
    // Load adapter.
    adapter.data = it

    if (list.adapter != adapter) 
        // Only set the adapter if we didn't do it already.
        list.adapter = adapter
    


保存的滚动位置由以下数据计算得出:

屏幕上第一项的适配器位置 项目顶部距列表顶部的像素偏移(用于垂直布局)

因此,您可能会遇到轻微的不匹配,例如如果您的商品在纵向和横向上具有不同的尺寸。

【讨论】:

这是给我的。我最初设置适配器,然后在可用时更新其数据。如上所述,将其保留到数据可用时,它会自动滚动回原来的位置。 谢谢@Eugen。有没有关于 LayoutManager 保存和恢复滚动位置的文档? @AdamHurwitz 您期望什么样的文档?我描述的是LinearLayoutManager的一个实现细节,请阅读它的源代码。 这是LinearLayoutManager.SavedState的链接:cs.android.com/androidx/platform/frameworks/support/+/… 谢谢,@EugenPechanec。这对我来说适用于设备旋转。我也在使用底部导航视图,当我切换到另一个底部选项卡并返回时,列表又从头开始。关于为什么会这样的任何想法?【参考方案7】:

您不必再自己保存和恢复状态。如果您在 xml 中设置唯一 ID,并且 recyclerView.setSaveEnabled(true)(默认为 true),系统将自动执行此操作。 以下是有关此的更多信息:http://trickyandroid.com/saving-android-view-state-correctly/

【讨论】:

【参考方案8】:

从 androidx recyclerView 库的 1.2.0-alpha02 版本开始,它现在是自动管理的。只需添加:

implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"

并使用:

adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY

StateRestorationPolicy 枚举有 3 个选项:

ALLOW — 默认状态,在下一个布局过程中立即恢复 RecyclerView 状态 PREVENT_WHEN_EMPTY — 仅在适配器不为空时恢复 RecyclerView 状态(adapter.getItemCount() > 0)。如果您的数据是异步加载的,则 RecyclerView 会等到数据加载完毕,然后才会恢复状态。如果您有默认项目,如标题或加载进度指示器作为适配器的一部分,那么您应该使用 PREVENT 选项,除非使用 MergeAdapter 添加默认项目。 MergeAdapter 等待其所有适配器准备就绪,然后才恢复状态。 PREVENT — 延迟所有状态恢复,直到您设置 ALLOW 或 PREVENT_WHEN_EMPTY。

请注意,在此回答时,recyclerView 库仍处于 alpha03,但 alpha 阶段不适合生产目的

【讨论】:

节省了很多过度工作。否则,我们必须保存列表点击索引、恢复元素、防止在 onCreateView 中重绘、应用 scrollToPosition(或执行其他答案),但用户在回来后可能会注意到差异 另外,如果我想在通过应用程序导航后保存 RecyclerView 的位置......有什么工具吗? @RubenViguera 为什么 alpha 不用于生产目的?我的意思是这真的很糟糕吗? @StayCool "另外,如果我想在通过应用程序导航后保存 RecyclerView 位置......有什么工具吗?"然后我猜你的片段没有被导航库正确恢复。您应该检查您的代码,因为您的片段可能会在每次移动时重新创建,而不是恢复。 @StayCool “为什么 alpha 不用于生产目的?我的意思是这真的很糟糕吗?” Alpha 是用于表示正在开发的软件的术语,它的 API 适合更改,并且极有可能出现错误(不稳定)。 Beta 术语用于处于测试状态的软件。我的意思是,是一个处于稳定过程中的软件,没有开发新功能,只是修复了错误。 Release Candidate 术语是指处于最终测试状态的软件。如果不再出现严重错误,则候选成为下一个稳定(生产)版本。【参考方案9】:

我也有同样的要求,但由于 recyclerView 的数据源,这里的解决方案并没有让我完全理解。

我在 onPause() 中提取 RecyclerViews 的 LinearLayoutManager 状态

private Parcelable state;

Public void onPause() 
    state = mLinearLayoutManager.onSaveInstanceState();

可打包的state 保存在onSaveInstanceState(Bundle outState) 中,并在savedInstanceState != null 时再次提取到onCreate(Bundle savedInstanceState)

但是,recyclerView 适配器由对 Room 数据库的 DAO 调用返回的 ViewModel LiveData 对象填充和更新。

mViewModel.getAllDataToDisplay(mSelectedData).observe(this, new Observer<List<String>>() 
    @Override
    public void onChanged(@Nullable final List<String> displayStrings) 
        mAdapter.swapData(displayStrings);
        mLinearLayoutManager.onRestoreInstanceState(state);
    
);

我最终发现,在适配器中设置数据后直接恢复实例状态将保持滚动状态跨越旋转。

我怀疑这是因为当数据库调用返回数据时,我恢复的 LinearLayoutManager 状态被覆盖,或者恢复的状态对“空”LinearLayoutManager 毫无意义。

如果适配器数据直接可用(即不依赖于数据库调用),则可以在 recyclerView 上设置适配器后在 LinearLayoutManager 上恢复实例状态。

这两种场景之间的区别让我久久不能释怀。

【讨论】:

这实际上仍然可以使用,即使在使用 recyclerview 策略的更新之后,如果您需要手动保存 recyclerview 的状态,例如如果您在垂直 recyclerview 中有水平 recyclerview,则内部 recyclerview 永远不会保存其滚动位置,因此您需要在答案中手动执行。并将状态保存在 onViewRecycled 中并在 onBindViewHolder 中再次设置。 这里是如何将 recyclerview 包含在其他具有不同布局管理器方向的链接 medium.com/@gsaillen95/…【参考方案10】:

这是我保存它们并在片段中保持 recyclerview 状态的方法。 首先,在您的父活动中添加配置更改,如下代码。

android:configChanges="orientation|screenSize"

然后在你的 Fragment 中声明这些实例方法的

private  Bundle mBundleRecyclerViewState;
private Parcelable mListState = null;
private LinearLayoutManager mLinearLayoutManager;

在@onPause 方法中添加以下代码

@Override
public void onPause() 
    super.onPause();
    //This used to store the state of recycler view
    mBundleRecyclerViewState = new Bundle();
    mListState =mTrailersRecyclerView.getLayoutManager().onSaveInstanceState();
    mBundleRecyclerViewState.putParcelable(getResources().getString(R.string.recycler_scroll_position_key), mListState);

添加 onConfigartionChanged 方法,如下所示。

@Override
public void onConfigurationChanged(Configuration newConfig) 
    super.onConfigurationChanged(newConfig);
    //When orientation is changed then grid column count is also changed so get every time
    Log.e(TAG, "onConfigurationChanged: " );
    if (mBundleRecyclerViewState != null) 
        new Handler().postDelayed(new Runnable() 

            @Override
            public void run() 
                mListState = mBundleRecyclerViewState.getParcelable(getResources().getString(R.string.recycler_scroll_position_key));
                mTrailersRecyclerView.getLayoutManager().onRestoreInstanceState(mListState);

            
        , 50);
    
    mTrailersRecyclerView.setLayoutManager(mLinearLayoutManager);

此方法将解决您的问题。 如果您有不同的布局管理器,则只需替换上层布局管理器。

【讨论】:

您可能不需要 Handler 来持久化数据。生命周期方法足以保存/加载。 @sayaMahi 你能用适当的方式描述一下吗?我还看到 onConfigurationChanged 阻止调用 destroy 方法。所以这不是一个合适的解决方案。【参考方案11】:

在 Android API 级别 28 上,我只需确保在我的 Fragment#onCreateView 方法中设置了我的 LinearLayoutManagerRecyclerView.Adapter,以及所有 Just Worked™️。我不需要做任何onSaveInstanceStateonRestoreInstanceState 的工作。

Eugen Pechanec's answer 解释了为什么会这样。

【讨论】:

【参考方案12】:

由于我发现大多数选项都太长或太复杂,这是我的简短 Kotlin 选项,其中包含 viewBindingviewModel 用于实现此目的:

如果你想让你的 RecyclerView 状态生命周期感知,把这段代码放在你的 ViewModel 中,否则你可以使用一个基本的 Repository 并从那里开始工作:

private lateinit var state: Parcelable
fun saveRecyclerViewState(parcelable: Parcelable)  state = parcelable 
fun restoreRecyclerViewState() : Parcelable = state
fun stateInitialized() : Boolean = ::state.isInitialized

这在你的 onPause()

binding.recyclerView.layoutManager?.onSaveInstanceState()?.let  viewModel.saveRecyclerViewState(it) 

最后在你的 onResume()

if (viewModel.stateInitialized()) 
        binding.recyclerView.layoutManager?.onRestoreInstanceState(
            viewModel.restoreRecyclerViewState()
        )
    

享受:)

【讨论】:

【参考方案13】:

当您需要使用 AsyncTaskLoader 从互联网重新加载数据时,这就是我在旋转后使用 GridLayoutManager 恢复 RecyclerView 位置的方法。

制作 Parcelable 和 GridLayoutManager 的全局变量和静态最终字符串:

private Parcelable savedRecyclerLayoutState;
private GridLayoutManager mGridLayoutManager;
private static final String BUNDLE_RECYCLER_LAYOUT = "recycler_layout";

在onSaveInstance()中保存gridLayoutManager的状态

 @Override
        protected void onSaveInstanceState(Bundle outState) 
            super.onSaveInstanceState(outState);
            outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, 
            mGridLayoutManager.onSaveInstanceState());
        

在 onRestoreInstanceState 中恢复

@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) 
        super.onRestoreInstanceState(savedInstanceState);

        //restore recycler view at same position
        if (savedInstanceState != null) 
            savedRecyclerLayoutState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
        
    

然后,当加载器从互联网获取数据时,您在 onLoadFinished() 中恢复 recyclerview 位置

if(savedRecyclerLayoutState!=null)
                mGridLayoutManager.onRestoreInstanceState(savedRecyclerLayoutState);
            

..当然你必须在 onCreate 中实例化 gridLayoutManager。 干杯

【讨论】:

【参考方案14】:

对我来说,问题是我每次更改适配器时都设置了一个新的布局管理器,从而失去了 recyclerView 上的滚动位置。

【讨论】:

【参考方案15】:

您可以使用recyclerview:1.2.0-alpha02 中介绍的adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY

https://medium.com/androiddevelopers/restore-recyclerview-scroll-position-a8fbdc9a9334

但它存在一些问题,例如无法使用内部 RecyclerView,以及您可以在中型帖子的评论部分查看的其他一些问题。

或者您可以将ViewModelSavedStateHandle 一起使用,这适用于内部RecyclerView、屏幕旋转和进程死亡

使用saveStateHandle 创建一个ViewModel

val scrollState=
    savedStateHandle.getLiveData<Parcelable?>(KEY_LAYOUT_MANAGER_STATE)

使用 Parcelable scrollState 来保存和恢复在其他帖子中回答的状态或通过向 RecyclerView 添加滚动侦听器和

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() 

        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) 
            super.onScrollStateChanged(recyclerView, newState)
            if (newState == RecyclerView.SCROLL_STATE_IDLE) 
               scrollState.value = mLayoutManager.onSaveInstanceState()
            
        

【讨论】:

【参考方案16】:

我想分享一下我最近遇到的 RecyclerView 困境。我希望任何遇到同样问题的人都能从中受益。

我的项目要求: 所以我有一个 RecyclerView 列出了我的主要活动(活动-A)中的一些可点击项目。单击该项目时,将显示一个带有项目详细信息 (Activity-B) 的新 Activity。

我在 Manifest 文件中实现了 Activity-A 是 Activity-B 的父级,这样我在 Activity-B 的 ActionBar 上有一个返回或主页按钮 p>

问题: 每次我按下 Activity-B ActionBar 中的 Back 或 Home 按钮时,Activity-A 都会从 onCreate() 开始进入完整的 Activity 生命周期

虽然我实现了一个 onSaveInstanceState() 来保存 RecyclerView 的 Adapter 列表,但当 Activity-A 启动它的生命周期时,在 onCreate() 方法中 saveInstanceState 始终为 null。

在互联网上进一步挖掘,我遇到了同样的问题,但该人注意到 Anroid 设备下方的返回或主页按钮(默认返回/主页按钮),Activity-A 没有进入活动生命周期.

解决方案:

    我在 Activity-B 的清单中删除了父 Activity 的标记

    我在 Activity-B 上启用了主页或返回按钮

    在 onCreate() 方法下添加这一行 supportActionBar?.setDisplayHomeAsUpEnabled(true)

    在overide fun onOptionsItemSelected() 方法中,我根据id 检查了item.ItemId 是哪个项目被点击的。后退按钮的 ID 是

    android.R.id.home

    然后在android.R.id.home里面实现一个finish()函数调用

    这将结束 Activity-B 并带来 Activity-A 而无需经历整个生命周期。

对于我的要求,这是迄今为止最好的解决方案。

     override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_show_project)

    supportActionBar?.title = projectName
    supportActionBar?.setDisplayHomeAsUpEnabled(true)




 override fun onOptionsItemSelected(item: MenuItem?): Boolean 

    when(item?.itemId)

        android.R.id.home -> 
            finish()
        
    

    return super.onOptionsItemSelected(item)

【讨论】:

【参考方案17】:

我遇到了同样的问题,但有一点点不同,我使用的是 Databinding,我在绑定方法中设置了 recyclerView 适配器和 layoutManager。

问题是数据绑定是在 onResume 之后发生的,所以它在 onResume 之后重置了我的 layoutManager,这意味着它每次都在滚动回原来的位置。 因此,当我停止在数据绑定适配器方法中设置 layoutManager 时,它又可以正常工作了。

【讨论】:

你是怎么做到的?【参考方案18】:

在我的例子中,我在 XML 和 onViewCreated 中都设置了 RecyclerView 的 layoutManager。删除onViewCreated 中的分配修复了它。

with(_binding.list) 
//  layoutManager =  LinearLayoutManager(context)
    adapter = MyAdapter().apply 
        listViewModel.data.observe(viewLifecycleOwner,
            Observer 
                it?.let  setItems(it) 
            )
    

【讨论】:

【参考方案19】:

另一种方式watch this,

首先,定义变量,

private LinearLayoutManager mLayoutManager;
int lastPosition;

public static final String MyPREFERENCES__DISPLAY = "MyPrefs_display" ;
SharedPreferences sharedpreferences_display;
SharedPreferences.Editor editor_display;

那么,

mLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(mLayoutManager);

recyclerView.scrollToPosition(sharedpreferences_display.getInt("last_saved_position", 0));

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() 
    @Override
    public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) 
        super.onScrollStateChanged(recyclerView, newState);
        lastPosition = mLayoutManager.findFirstVisibleItemPosition();
    
);

如果你想通过按钮保存位置,

btn.setOnClickListener(v -> 
    editor_display.putInt("last_saved_position", lastPosition).apply();
    Toast.makeText(Arama_Fav_Activity.this, "Saved", Toast.LENGTH_SHORT).show();
);

【讨论】:

【参考方案20】:

Activity.java:

public RecyclerView.LayoutManager mLayoutManager;

Parcelable state;

    mLayoutManager = new LinearLayoutManager(this);


// Inside `onCreate()` lifecycle method, put the below code :

    if(state != null) 
    mLayoutManager.onRestoreInstanceState(state);

     

    @Override
    protected void onResume() 
        super.onResume();

        if (state != null) 
            mLayoutManager.onRestoreInstanceState(state);
        
       

   @Override
    protected void onPause() 
        super.onPause();

        state = mLayoutManager.onSaveInstanceState();

       

为什么我在onPause() 中使用OnSaveInstanceState() 的意思是,当切换到另一个活动时会调用onPause。它将保存滚动位置并在我们从另一个活动返回时恢复该位置。

【讨论】:

公共静态字段不好。全局数据和单例都不好。 @weston 我现在明白了。我将 savestate 更改为 onpause 以删除全局。

以上是关于如何使用 RecyclerView.State 保存 RecyclerView 的滚动位置?的主要内容,如果未能解决你的问题,请参考以下文章

如何将栅格保存在包中

如何使用 Swift 将游戏的高分保存在排行榜上?

即使我离开然后使用Jquery返回页面,如何将内容保留在页面上

如何通过SpringBoot表单将html内容保存在mongodb中

如何将 cookie 永久保存在 Android webview 中?

iOS后台保活