如何保存和恢复RecyclerView的滚动确切位置?

时间:2015-09-12 09:32:31

标签: android listview android-recyclerview

抱歉我的英语不好。

我想要确切的位置。在活动/片段销毁之后(无论我销毁它还是系统销毁它),我重新打开活动/片段,RecyclerView可以也是相同的位置,因为它已被销毁。

"相同" 并不代表"项目位置",因为该项目可能只是上次部分显示。

我希望恢复完全相同的位置。

我在下面尝试了一些方法,但没有人是完美的。

有人可以帮忙吗?

1.第一种方式。

我使用onScrolled来计算滚动的确切位置,滚动停止时,保存位置。当微调器更改数据集片段onCreat 时,请恢复所选数据集的位置。某些数据集可能包含许多行。

它可以在应用程序被销毁后保存和恢复,但可能有太多的计算?

会导致

Skipped 60 frames! The application may be doing too much work on its main thread. attempt to finish an input event but the input event receiver has already been disposed. android total arena pages for jit.

如果scrollY很大,如111111,则在打开片段时,RecyclerView将首先显示从第一个项开始的列表,并在一段延迟后滚动到{{1位置。

如何让它没有延迟?

scrollY

2.第二种方式。

这完全运行,但它只能在片段生命周期中运行。

我尝试使用 recyclerView.addOnScrollListener(new OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, final int dy) { super.onScrolled(recyclerView, dx, dy); handler.post(new Runnable() { @Override public void run() { if (isTableSwitched) { scrollY = 0; isTableSwitched = false; } scrollY = scrollY + dy; Log.i("dy——scrollY——table", String.valueOf(dy) + "——" + String.valueOf(scrollY) + tableName); } }); } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); switch (newState) { case RecyclerView.SCROLL_STATE_IDLE: saveRecyclerPosition(tableName, scrollY); break; case RecyclerView.SCROLL_STATE_DRAGGING: break; case RecyclerView.SCROLL_STATE_SETTLING: break; } } }); public void saveRecyclerPosition(String tableName, int y) { prefEditorSettings.putInt(KEY_SCROLL_Y + tableName, y); prefEditorSettings.commit(); } public void restoreRecyclerViewState(final String tableName) { handler.post(new Runnable() { @Override public void run() { recyclerView.scrollBy(0, prefSettings.getInt(KEY_SCROLL_Y + tableName, 0)); } }); } //use Spinner to choose dataset spnWordbook.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { isTableSwitched = true; tableName= parent.getItemAtPosition(position).toString(); prefEditorSettings.putString(KEY_SPINNER_SELECTED_TABLENAME, tableName); prefEditorSettings.commit(); wordsList = WordsManager.getWordsList(tableName); wordCardAdapter.updateList(wordsList); restoreRecyclerViewState(tableName); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); ObjectOutputStream来保存和恢复ObjectInputStream,但它不起作用。

如何将其保存在内存中?

HashMap

3.第三种方式。

我改变第二种方式,使用private HashMap <String, Parcelable> hmRecyclerViewState = new HashMap<String, Parcelable>(); public void saveRecyclerPosition(String tableName) { recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState(); hmRecyclerViewState.put(tableName, recyclerViewState); } public void restoreRecyclerViewState(final String tableName) { if ( hmRecyclerViewState.get(tableName) != null) { recyclerViewState = hmRecyclerViewState.get(tableName); recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState); recyclerViewState = null; } 代替Bundle,但它有相同的问题,在片段生命周期之外不起作用。

我使用manually serialize/deserialize android bundleHashMap保存在内存中,但在恢复时,它无法获得有效的bundle

=============================================== ======================== 解决方案

谢谢大家!

最后我解决了这个问题,您可以在my code中看到它,只看到这两种方法: saveRecyclerViewPosition restoreWordRecyclerViewPosition

1 个答案:

答案 0 :(得分:7)

所以现在我有时间浪费;)这是一个完整的答案:

基本布局。是的,它可以优化。这不是重点;)

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

您要做的是获取第一个可见项目及其位置。如果你有固定的id,你可以添加逻辑来在id中查找适配器中的id / position。

一些基本适配器:

public class TestAdapter extends RecyclerView.Adapter {
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false)) {
        };
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((TextView) holder.itemView).setText("Position " + position);
    }

    @Override
    public int getItemCount() {
        return 30;
    }
}

根据您的喜好保存状态,我使用了SharedPreferences,并在开始时再次阅读。我postDelayed滚动,因为它滚动到位置后立即跟随它不起作用。它只是一个基本的例子,如果你更多地使用LayoutManager,可能会有更好的方法。

import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(new TestAdapter());

    }

    @Override
    protected void onResume() {
        super.onResume();
        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        recyclerView.scrollToPosition(preferences.getInt("position", 0));
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                recyclerView.scrollBy(0, - preferences.getInt("offset", 0));
            }
        }, 500);
    }

    @Override
    protected void onPause() {
        super.onPause();
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);

        View firstChild = recyclerView.getChildAt(0);
        int firstVisiblePosition = recyclerView.getChildAdapterPosition(firstChild);
        int offset = firstChild.getTop();

        Log.d(TAG, "Postition: " + firstVisiblePosition);
        Log.d(TAG, "Offset: " + offset);

        preferences.edit()
                .putInt("position", firstVisiblePosition)
                .putInt("offset", offset)
                .apply();
    }
}

这将做什么,它将再次显示正确的项目,并在这500毫秒后跳回到正确的位置,它被保存。

请务必添加空检查和排序!

希望这有帮助!