如何在屏幕旋转时保持 recyclerView 的滚动位置
How to Maintain Scroll position of recyclerView on Screen Rotation
我正在使用 gridlayoutManager 填充 recyclerView。现在我想保存屏幕旋转的滚动位置。
我已经尝试使用 onSaveInstanceState 和 onRestoreInstanceState() 来这样做 post :
下面是我的代码:
@Override
protected void onSaveInstanceState(Bundle outState) {
Log.e(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_INSTANCE_STATE_RV_POSITION,
gridLayoutManager.onSaveInstanceState());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.e(TAG, "onRestoreInstanceState");
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState!= null){
Parcelable savedState =
savedInstanceState.getParcelable(KEY_INSTANCE_STATE_RV_POSITION);
movieAdapter.addAll(movieItemList);
if (savedState!= null){
gridLayoutManager.onRestoreInstanceState(savedState);
}
}
}
//this is my movieAdapter.addAll() method
public void addAll(List<MovieItem>items){
movieItems = items;
}
//This is the method to get lists of movies from ViewModel Class
private void loadFavMovies() {
FavViewModel favViewModel =
ViewModelProviders.of(MainActivity.this).get(FavViewModel.class);
favViewModel.getFavListLiveData().observe(MainActivity.this, new
Observer<List<FavlistItem>>() {
@Override
public void onChanged(List<FavlistItem> favlistItems) {
if (!favlistItems.isEmpty()) {
loadingIndicator.setVisibility(View.GONE);
movieRecycler.setVisibility(View.GONE);
favRecycler.setVisibility(View.VISIBLE);
favAdapter.setFavlistItems(favlistItems);
Toast.makeText(getApplicationContext(), "Swipe Left Or
Right To remove Item",Toast.LENGTH_SHORT).show();
}else {
loadingIndicator.setVisibility(View.GONE);
Toast.makeText(getApplicationContext(), "No Favorite
Movies",Toast.LENGTH_SHORT).show();
}
}
});
}
这是这个项目的 link 到 GitHub https://github.com/harshabhadra/Movies-Mela
一种方法是在方向改变时停止 re-creation of activity。为此,您可以将其添加到清单中的 activity。
android:configChanges="orientation|screenSize"
但这并不总是一个好的做法,因此另一种选择是在 onSaveInstanceState
中退出之前保存适配器位置,然后使用 [=14= 滚动到 onCreate
中的那个位置].因此,例如,您可以按照以下几行进行操作。
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("position", mRecyclerView.getAdapterPosition()); // get current recycle view position here.
//your other code
super.onSaveInstanceState(savedInstanceState);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//your other code
if(savedInstanceState != null){
// scroll to existing position which exist before rotation.
mRecyclerView.scrollToPosition(savedInstanceState.getInt("position"));
}
}
你应该看看这个 。
基本上,您想创建一个新的 class 扩展 RecyclerView
(记住您必须使用新的 class 而不是 RecyclerView)并覆盖 onSaveInstanceState()
和onRestoreInstanceState()
。在 onSaveInstanceState()
中,您将保存第一个可见元素的索引并滚动到 onRestoreInstanceState()
中的该元素。
他们在链接 post 的已接受答案中采用的方式不同,但使用 LinearLayoutManager
代替,因此我将定制代码以使用 GridLayoutManager
:
public class CustomRecyclerView extends RecyclerView {
private int mScrollPosition;
public VacationsRecyclerView(@NonNull Context context) {
super(context);
}
@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);
}
}
}
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
mScrollPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();
}
SavedState newState = new SavedState(superState);
newState.mScrollPosition = mScrollPosition;
return newState;
}
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];
}
};
}
}
实现您想要实现的目标的最简单方法是在 Fragment 中使用 RecyclerView,而不是直接在 Activity.
中使用
与活动不同,片段在默认情况下不会 re-created 旋转,但会保留其状态。这样,recyclerview 将始终保持在相同的滚动位置。
我在我的一个应用程序中以这种方式使用 RecyclerView,它显示包含 > 200 个元素的列表。
如果您使用 GridLayoutManager
子类 LinearLayoutManager
,滚动位置应该已经自动保存。你可以看看 SavedState
inside LinearLayoutManager
:
public static class SavedState implements Parcelable {
int mAnchorPosition;
int mAnchorOffset;
boolean mAnchorLayoutFromEnd;
mAnchorPosition
实际上是您的滚动位置,它会在旋转过程中保存。如果这对您不起作用,则可能是其他地方出了问题:例如,您可能正在错误地重新加载/重新应用数据。这可能是您需要问的真正问题。您可能需要将该数据缓存在某处,以便您可以在配置更改后立即自动重新应用它。
我还可以确认在我使用过 LinearLayoutManager
和 GridLayoutManager
的每个项目中,滚动位置在旋转期间正确保持,我没有额外的工作。
我已经尝试了所有人建议的所有方法,但最终,这个要点对我有用
https://gist.github.com/FrantisekGazo/a9cc4e18cee42199a287
我只是在我的项目中导入这个,并用这个替换默认提供的recyclerView
"StateFulRecyclerView" 它解决了我的问题并在屏幕旋转或任何配置更改期间自动处理滚动位置。无需使用 onSaveInstanceState 来保持滚动位置。
现在,它有简单的solution.use这个依赖
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05"
然后像下面这样设置 stateRestorationPolicy
myAdapter.stateRestorationPolicy=RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
我知道我迟到了,但如果它有帮助的话!
存储回收站视图位置比其他答案看起来更简单
以下是您的操作方法
首先创建一个成员变量
Parcelable state;
现在,
@Override
protected void onPause() {
super.onPause();
state = recyclerView.getLayoutManager().onSaveInstanceState();
}
@Override
protected void onResume() {
super.onResume();
recyclerView.getLayoutManager().onRestoreInstanceState(state);
}
使用上面的代码覆盖 on pause 和 on resume 方法,你就可以开始了!
很简单。暂停时保存位置,恢复时恢复。
暂停
lastVisitPosition = ((LinearLayoutManager)recycleView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
恢复
((LinearLayoutManager) recycleView.getLayoutManager()).scrollToPosition(lastVisitPosition);
或
((LinearLayoutManager) recycleView.getLayoutManager()).scrollToPositionWithOffset(lastVisitPosition,0)
我正在使用 gridlayoutManager 填充 recyclerView。现在我想保存屏幕旋转的滚动位置。
我已经尝试使用 onSaveInstanceState 和 onRestoreInstanceState() 来这样做 post :
下面是我的代码:
@Override
protected void onSaveInstanceState(Bundle outState) {
Log.e(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_INSTANCE_STATE_RV_POSITION,
gridLayoutManager.onSaveInstanceState());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.e(TAG, "onRestoreInstanceState");
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState!= null){
Parcelable savedState =
savedInstanceState.getParcelable(KEY_INSTANCE_STATE_RV_POSITION);
movieAdapter.addAll(movieItemList);
if (savedState!= null){
gridLayoutManager.onRestoreInstanceState(savedState);
}
}
}
//this is my movieAdapter.addAll() method
public void addAll(List<MovieItem>items){
movieItems = items;
}
//This is the method to get lists of movies from ViewModel Class
private void loadFavMovies() {
FavViewModel favViewModel =
ViewModelProviders.of(MainActivity.this).get(FavViewModel.class);
favViewModel.getFavListLiveData().observe(MainActivity.this, new
Observer<List<FavlistItem>>() {
@Override
public void onChanged(List<FavlistItem> favlistItems) {
if (!favlistItems.isEmpty()) {
loadingIndicator.setVisibility(View.GONE);
movieRecycler.setVisibility(View.GONE);
favRecycler.setVisibility(View.VISIBLE);
favAdapter.setFavlistItems(favlistItems);
Toast.makeText(getApplicationContext(), "Swipe Left Or
Right To remove Item",Toast.LENGTH_SHORT).show();
}else {
loadingIndicator.setVisibility(View.GONE);
Toast.makeText(getApplicationContext(), "No Favorite
Movies",Toast.LENGTH_SHORT).show();
}
}
});
}
这是这个项目的 link 到 GitHub https://github.com/harshabhadra/Movies-Mela
一种方法是在方向改变时停止 re-creation of activity。为此,您可以将其添加到清单中的 activity。
android:configChanges="orientation|screenSize"
但这并不总是一个好的做法,因此另一种选择是在 onSaveInstanceState
中退出之前保存适配器位置,然后使用 [=14= 滚动到 onCreate
中的那个位置].因此,例如,您可以按照以下几行进行操作。
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("position", mRecyclerView.getAdapterPosition()); // get current recycle view position here.
//your other code
super.onSaveInstanceState(savedInstanceState);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//your other code
if(savedInstanceState != null){
// scroll to existing position which exist before rotation.
mRecyclerView.scrollToPosition(savedInstanceState.getInt("position"));
}
}
你应该看看这个
基本上,您想创建一个新的 class 扩展 RecyclerView
(记住您必须使用新的 class 而不是 RecyclerView)并覆盖 onSaveInstanceState()
和onRestoreInstanceState()
。在 onSaveInstanceState()
中,您将保存第一个可见元素的索引并滚动到 onRestoreInstanceState()
中的该元素。
他们在链接 post 的已接受答案中采用的方式不同,但使用 LinearLayoutManager
代替,因此我将定制代码以使用 GridLayoutManager
:
public class CustomRecyclerView extends RecyclerView {
private int mScrollPosition;
public VacationsRecyclerView(@NonNull Context context) {
super(context);
}
@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);
}
}
}
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
mScrollPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();
}
SavedState newState = new SavedState(superState);
newState.mScrollPosition = mScrollPosition;
return newState;
}
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];
}
};
}
}
实现您想要实现的目标的最简单方法是在 Fragment 中使用 RecyclerView,而不是直接在 Activity.
中使用与活动不同,片段在默认情况下不会 re-created 旋转,但会保留其状态。这样,recyclerview 将始终保持在相同的滚动位置。
我在我的一个应用程序中以这种方式使用 RecyclerView,它显示包含 > 200 个元素的列表。
如果您使用 GridLayoutManager
子类 LinearLayoutManager
,滚动位置应该已经自动保存。你可以看看 SavedState
inside LinearLayoutManager
:
public static class SavedState implements Parcelable {
int mAnchorPosition;
int mAnchorOffset;
boolean mAnchorLayoutFromEnd;
mAnchorPosition
实际上是您的滚动位置,它会在旋转过程中保存。如果这对您不起作用,则可能是其他地方出了问题:例如,您可能正在错误地重新加载/重新应用数据。这可能是您需要问的真正问题。您可能需要将该数据缓存在某处,以便您可以在配置更改后立即自动重新应用它。
我还可以确认在我使用过 LinearLayoutManager
和 GridLayoutManager
的每个项目中,滚动位置在旋转期间正确保持,我没有额外的工作。
我已经尝试了所有人建议的所有方法,但最终,这个要点对我有用
https://gist.github.com/FrantisekGazo/a9cc4e18cee42199a287
我只是在我的项目中导入这个,并用这个替换默认提供的recyclerView "StateFulRecyclerView" 它解决了我的问题并在屏幕旋转或任何配置更改期间自动处理滚动位置。无需使用 onSaveInstanceState 来保持滚动位置。
现在,它有简单的solution.use这个依赖
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05"
然后像下面这样设置 stateRestorationPolicy
myAdapter.stateRestorationPolicy=RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
我知道我迟到了,但如果它有帮助的话!
存储回收站视图位置比其他答案看起来更简单
以下是您的操作方法
首先创建一个成员变量
Parcelable state;
现在,
@Override
protected void onPause() {
super.onPause();
state = recyclerView.getLayoutManager().onSaveInstanceState();
}
@Override
protected void onResume() {
super.onResume();
recyclerView.getLayoutManager().onRestoreInstanceState(state);
}
使用上面的代码覆盖 on pause 和 on resume 方法,你就可以开始了!
很简单。暂停时保存位置,恢复时恢复。
暂停
lastVisitPosition = ((LinearLayoutManager)recycleView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
恢复
((LinearLayoutManager) recycleView.getLayoutManager()).scrollToPosition(lastVisitPosition);
或
((LinearLayoutManager) recycleView.getLayoutManager()).scrollToPositionWithOffset(lastVisitPosition,0)