【问题标题】:Cannot restore Scroll position无法恢复滚动位置
【发布时间】:2026-01-22 10:55:02
【问题描述】:

我被困在这里 2 天。我正在尝试在设备旋转后恢复滚动位置。

我在 onSaveInstance 中保存了一个数组列表,将我必须的电影添加到它并尝试设置适配器。

如果我切换短路标准并旋转设备,它会向上滚动到顶部并且不会保留其位置。这是我的代码

public class MainActivity extends AppCompatActivity {

public static final String MOVIE_LIST = "instanceMovieList";
public static final String RECYCLER_VIEW_STATE_KEY = "RECYCLER_VIEW_STATE_KEY";
private static final String LOG_TAG = MainActivity.class.getSimpleName();
Context mContext;
Toolbar mToolBar;
@BindView(R.id.prefSpinnner)
Spinner prefSpinner;
@BindView(R.id.noDataTv)
TextView noDataTv;
@BindView(R.id.rv_movies)
RecyclerView mMoviesRV;
private AppDatabase mDb;
private String userOption = "Most Popular";
private ArrayList<Movie> mMoviesList = new ArrayList<>();
private MovieRecycleViewAdapter movieRecycleViewAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private Parcelable mListState;

@Override
protected void onSaveInstanceState(Bundle outState) {
    if (mMoviesList != null) {
        outState.putParcelableArrayList(MOVIE_LIST, mMoviesList);
        mListState = mLayoutManager.onSaveInstanceState();
        outState.putParcelable(RECYCLER_VIEW_STATE_KEY, mListState);
    }
    super.onSaveInstanceState(outState);
}


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


    GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
    mMoviesRV.setLayoutManager(gridLayoutManager);
    mMoviesRV.setHasFixedSize(true);
    movieRecycleViewAdapter = new MovieRecycleViewAdapter(MainActivity.this, mMoviesList);
    mMoviesRV.setAdapter(movieRecycleViewAdapter);

    mLayoutManager = mMoviesRV.getLayoutManager();

    if (savedInstanceState != null && savedInstanceState.containsKey(MOVIE_LIST)) {
        ArrayList<Movie> savedMovieList = savedInstanceState.getParcelableArrayList(MOVIE_LIST);
        Log.d(LOG_TAG, "getting movies from instance ");
        mMoviesList.addAll(savedMovieList);
        movieRecycleViewAdapter = new MovieRecycleViewAdapter(MainActivity.this, mMoviesList);
        mMoviesRV.setAdapter(movieRecycleViewAdapter);
    }

    mToolBar = findViewById(R.id.toolbar);

    mToolBar.setTitle(getResources().getString(R.string.app_name));

    ArrayAdapter<String> spinAdapter = new ArrayAdapter<String>(MainActivity.this, R.layout.pref_spinner_item_list, getResources().getStringArray(R.array.userPrefs));
    spinAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    prefSpinner.setAdapter(spinAdapter);

    prefSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            userOption = prefSpinner.getSelectedItem().toString();

            if (userOption.contentEquals("Most Popular") || userOption.contentEquals("Highest Rated")) {
                PopulateMoviesTask newTask = new PopulateMoviesTask();
                newTask.execute();


            } else {
                setUpViewModel();
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {
            PopulateMoviesTask newTask = new PopulateMoviesTask();
            newTask.execute();
        }
    });
    mDb = AppDatabase.getInstance(getApplicationContext());

}

private void setUpViewModel() {
    MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
    viewModel.getMovies().observe(this, new Observer<List<Movie>>() {
        @Override
        public void onChanged(@Nullable List<Movie> movies) {
            Log.d(LOG_TAG, "updating list of movies from livedata in viewmodel");
            movieRecycleViewAdapter.setMovies(movies);
        }
    });
}

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

    if (savedInstanceState != null) {
        mMoviesList = savedInstanceState.getParcelableArrayList(MOVIE_LIST);
        mListState = savedInstanceState.getParcelable(RECYCLER_VIEW_STATE_KEY);
    }
}

@Override
protected void onResume() {
    super.onResume();
    if (mListState != null) {
        mLayoutManager.onRestoreInstanceState(mListState);
    }
}

private class PopulateMoviesTask extends AsyncTask<URL, Void, String> {

    @Override
    protected String doInBackground(URL... urls) {
        URL searchMovieObjectUrl = NetworkUtils.createUrl(userOption);
        String jsonString = "";

        try {
            jsonString = NetworkUtils.makeHttpRequest(searchMovieObjectUrl);
        } catch (IOException e) {
            Log.e("Main Activity", "Problem making the HTTP request.", e);
        }
        return jsonString;
    }

    @Override
    protected void onPostExecute(String jsonString) {
        if (jsonString == null) {
            mMoviesRV.setVisibility(View.GONE);
            noDataTv.setVisibility(View.VISIBLE);
        } else {
            mMoviesRV.setVisibility(View.VISIBLE);
            noDataTv.setVisibility(View.GONE);
            mMoviesList = JsonUtils.extractFeatureFromJson(jsonString);
        }
        movieRecycleViewAdapter = new MovieRecycleViewAdapter(MainActivity.this, mMoviesList);
        mMoviesRV.setAdapter(movieRecycleViewAdapter);
    }
}

}

【问题讨论】:

    标签: java android scroll position restore


    【解决方案1】:

    使用这个

    private Parcelable recyclerViewState;// 全局

    onResume

     @Override
    public void onResume() {
        super.onResume();
        mMoviesRV.getLayoutManager().onRestoreInstanceState(recyclerViewState);
    }
    

    暂停

      @Override
    public void onPause() {
        super.onPause();
        recyclerViewState= mMoviesRV.getLayoutManager().onSaveInstanceState();
    
    }
    

    【讨论】:

    • 我做到了。还是一样。你认为这与我的 postExecute 有什么关系吗?
    【解决方案2】:

    既然我发现了发生了什么,我想我应该发布一个答案。

    @Override protected void onSaveInstanceState(Bundle outState) {
    if (mMoviesList != null) {
        outState.putParcelableArrayList(MOVIE_LIST, mMoviesList);
        mListState = mLayoutManager.onSaveInstanceState();
        outState.putParcelable(RECYCLER_VIEW_STATE_KEY, mListState);
    }
    super.onSaveInstanceState(outState);
    

    }

    足以通过生命周期保存moviesList然后检索它

    if (savedInstanceState != null && savedInstanceState.containsKey(MOVIE_LIST)) {
            mMoviesList = savedInstanceState.getParcelableArrayList(MOVIE_LIST);
    

    这回答了原来的问题

    但问题出在我的适配器上,而当我初始化它时。 适配器只应在 onCreate 中初始化(至少在我的情况下),然后在 Arraylist 数据更改时通知它more info here

    【讨论】: