在 android 片段中维护设备旋转时的列表项位置
Maintaining list items positions on device rotation in an android fragment
在一个片段中,我试图保存 RecyclerView 列表的滚动状态,但不知何故它没有保存状态。因为它是一个片段,所以我重写了 onSaveInstanceState() 和 onActivityCreated() 方法来保存滚动位置。甚至尝试在 onViewStateRestored() 方法中实现。我看到了一些关于保存滚动状态的相关帖子,但它不起作用。请让我知道我在哪里失败了。下面是我的代码:
public class RecipeListFragment extends Fragment
implements RecipeListContract.View {
@BindView(R.id.recipe_list_recycler_view)
RecyclerView mRecipeListRecyclerView;
@BindView(R.id.recipe_list_progress_bar)
ProgressBar mRecipeListProgressBar;
@BindInt(R.integer.grid_column_count)
int mGridColumnCount;
@BindString(R.string.recipe_list_sync_completed)
String mRecipeListSyncCompleted;
@BindString(R.string.recipe_list_connection_error)
String mRecipeListConnectionError;
GridLayoutManager gridLayoutManager;
Parcelable savedRecyclerLayoutState;
Unbinder unbinder;
private static final String SAVED_LAYOUT_MANAGER
= "com.learnwithme.buildapps.bakingapp.ui.recipelist.fragment";
private RecipeListContract.Presenter mRecipeListPresenter;
private RecipeListAdapter mRecipeListAdapter;
public RecipeListFragment() { }
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup
container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recipe_list, container,
false);
unbinder = ButterKnife.bind(this, view);
mRecipeListAdapter = new RecipeListAdapter(
getContext(),
new ArrayList<>(0),
recipeId -> mRecipeListPresenter.loadRecipeDetails(recipeId)
);
mRecipeListAdapter.setHasStableIds(true);
gridLayoutManager = new GridLayoutManager(getContext(),
mGridColumnCount);
mRecipeListRecyclerView.setLayoutManager(gridLayoutManager);
mRecipeListRecyclerView.setHasFixedSize(true);
mRecipeListRecyclerView.setAdapter(mRecipeListAdapter);
return view;
}
@Override
public void onPause() {
super.onPause();
mRecipeListPresenter.unsubscribe();
}
@Override
public void onResume() {
super.onResume();
mRecipeListPresenter.subscribe();
}
@Override
public void onSaveInstanceState() { }
@Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
if(bundle != null) {
bundle.putParcelable(SAVED_LAYOUT_MANAGER,
mRecipeListRecyclerView
.getLayoutManager()
.onSaveInstanceState());
Timber.d("instance state=>",
mRecipeListRecyclerView.getLayoutManager().onSaveInstanceState());
}
}
@Override
public void onViewStateRestored(@Nullable Bundle bundle) {
super.onViewStateRestored(bundle);
if(bundle != null) {
savedRecyclerLayoutState =
bundle.getParcelable(SAVED_LAYOUT_MANAGER);
Timber.d("onViewStateRestored savedRecyclerLayoutState=>",
savedRecyclerLayoutState);
mRecipeListRecyclerView
.getLayoutManager()
.onRestoreInstanceState(savedRecyclerLayoutState);
}
}
@Override
public void onActivityCreated(@Nullable Bundle bundle) {
super.onActivityCreated(bundle);
if(bundle != null) {
savedRecyclerLayoutState =
bundle.getParcelable(SAVED_LAYOUT_MANAGER);
Timber.d("onViewStateRestored savedRecyclerLayoutState=>",
savedRecyclerLayoutState);
mRecipeListRecyclerView
.getLayoutManager()
.onRestoreInstanceState(savedRecyclerLayoutState);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
public static RecipeListFragment newInstance() {
return new RecipeListFragment();
}
@Override
public void setPresenter(RecipeListContract.Presenter recipeListPresenter) {
this.mRecipeListPresenter = recipeListPresenter;
}
@Override
public void showRecipeList(List<Recipe> recipeList) {
mRecipeListAdapter.refreshRecipes(recipeList);
}
@Override
public void loadProgressBar(boolean show) {
setViewVisibility(mRecipeListRecyclerView, !show);
setViewVisibility(mRecipeListProgressBar, show);
}
@Override
public void displayCompletedMessage() {
Toast.makeText(getContext(), mRecipeListSyncCompleted,
Toast.LENGTH_SHORT).show();
}
@Override
public void displayErrorMessage() {
Toast.makeText(getContext(), mRecipeListConnectionError,
Toast.LENGTH_SHORT).show();
}
@Override
public void displayRecipeDetails(int recipeId) {
startActivity(RecipeDetailsActivity.prepareIntent(getContext(),
recipeId));
}
private void setViewVisibility(View view, boolean visible) {
if (visible) {
view.setVisibility(View.VISIBLE);
} else {
view.setVisibility(View.INVISIBLE);
}
}
}
我已经自己解决了这个问题。问题是在 onSaveInstanceState() 中保存设备的滚动位置,并在 onViewRestored() 方法中恢复它。
private Parcelable mRecipeListParcelable;
private int mScrollPosition = -1;
@Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
int scrollPosition = ((GridLayoutManager)
mRecipeListRecyclerView.getLayoutManager())
.findFirstCompletelyVisibleItemPosition();
mRecipeListParcelable = gridLayoutManager.onSaveInstanceState();
bundle.putParcelable(KEY_LAYOUT, mRecipeListParcelable);
bundle.putInt(POSITION, scrollPosition);
}
@Override
public void onViewStateRestored(@Nullable Bundle bundle) {
super.onViewStateRestored(bundle);
if(bundle != null) {
mRecipeListParcelable = bundle.getParcelable(KEY_LAYOUT);
mScrollPosition = bundle.getInt(POSITION);
}
}
此外,在 loadProgress() 方法中,我必须设置 scrollToPosition() 并保存滚动位置。
@Override
public void loadProgressBar(boolean show) {
setViewVisibility(mRecipeListRecyclerView, !show);
setViewVisibility(mRecipeListProgressBar, show);
mRecipeListRecyclerView.scrollToPosition(mScrollPosition);
}
另外,要记住的另一件事是,无需在 onResume() 方法中恢复任何内容,因为演示者回调将被调用并且视图将被重置。
在一个片段中,我试图保存 RecyclerView 列表的滚动状态,但不知何故它没有保存状态。因为它是一个片段,所以我重写了 onSaveInstanceState() 和 onActivityCreated() 方法来保存滚动位置。甚至尝试在 onViewStateRestored() 方法中实现。我看到了一些关于保存滚动状态的相关帖子,但它不起作用。请让我知道我在哪里失败了。下面是我的代码:
public class RecipeListFragment extends Fragment
implements RecipeListContract.View {
@BindView(R.id.recipe_list_recycler_view)
RecyclerView mRecipeListRecyclerView;
@BindView(R.id.recipe_list_progress_bar)
ProgressBar mRecipeListProgressBar;
@BindInt(R.integer.grid_column_count)
int mGridColumnCount;
@BindString(R.string.recipe_list_sync_completed)
String mRecipeListSyncCompleted;
@BindString(R.string.recipe_list_connection_error)
String mRecipeListConnectionError;
GridLayoutManager gridLayoutManager;
Parcelable savedRecyclerLayoutState;
Unbinder unbinder;
private static final String SAVED_LAYOUT_MANAGER
= "com.learnwithme.buildapps.bakingapp.ui.recipelist.fragment";
private RecipeListContract.Presenter mRecipeListPresenter;
private RecipeListAdapter mRecipeListAdapter;
public RecipeListFragment() { }
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup
container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recipe_list, container,
false);
unbinder = ButterKnife.bind(this, view);
mRecipeListAdapter = new RecipeListAdapter(
getContext(),
new ArrayList<>(0),
recipeId -> mRecipeListPresenter.loadRecipeDetails(recipeId)
);
mRecipeListAdapter.setHasStableIds(true);
gridLayoutManager = new GridLayoutManager(getContext(),
mGridColumnCount);
mRecipeListRecyclerView.setLayoutManager(gridLayoutManager);
mRecipeListRecyclerView.setHasFixedSize(true);
mRecipeListRecyclerView.setAdapter(mRecipeListAdapter);
return view;
}
@Override
public void onPause() {
super.onPause();
mRecipeListPresenter.unsubscribe();
}
@Override
public void onResume() {
super.onResume();
mRecipeListPresenter.subscribe();
}
@Override
public void onSaveInstanceState() { }
@Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
if(bundle != null) {
bundle.putParcelable(SAVED_LAYOUT_MANAGER,
mRecipeListRecyclerView
.getLayoutManager()
.onSaveInstanceState());
Timber.d("instance state=>",
mRecipeListRecyclerView.getLayoutManager().onSaveInstanceState());
}
}
@Override
public void onViewStateRestored(@Nullable Bundle bundle) {
super.onViewStateRestored(bundle);
if(bundle != null) {
savedRecyclerLayoutState =
bundle.getParcelable(SAVED_LAYOUT_MANAGER);
Timber.d("onViewStateRestored savedRecyclerLayoutState=>",
savedRecyclerLayoutState);
mRecipeListRecyclerView
.getLayoutManager()
.onRestoreInstanceState(savedRecyclerLayoutState);
}
}
@Override
public void onActivityCreated(@Nullable Bundle bundle) {
super.onActivityCreated(bundle);
if(bundle != null) {
savedRecyclerLayoutState =
bundle.getParcelable(SAVED_LAYOUT_MANAGER);
Timber.d("onViewStateRestored savedRecyclerLayoutState=>",
savedRecyclerLayoutState);
mRecipeListRecyclerView
.getLayoutManager()
.onRestoreInstanceState(savedRecyclerLayoutState);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
public static RecipeListFragment newInstance() {
return new RecipeListFragment();
}
@Override
public void setPresenter(RecipeListContract.Presenter recipeListPresenter) {
this.mRecipeListPresenter = recipeListPresenter;
}
@Override
public void showRecipeList(List<Recipe> recipeList) {
mRecipeListAdapter.refreshRecipes(recipeList);
}
@Override
public void loadProgressBar(boolean show) {
setViewVisibility(mRecipeListRecyclerView, !show);
setViewVisibility(mRecipeListProgressBar, show);
}
@Override
public void displayCompletedMessage() {
Toast.makeText(getContext(), mRecipeListSyncCompleted,
Toast.LENGTH_SHORT).show();
}
@Override
public void displayErrorMessage() {
Toast.makeText(getContext(), mRecipeListConnectionError,
Toast.LENGTH_SHORT).show();
}
@Override
public void displayRecipeDetails(int recipeId) {
startActivity(RecipeDetailsActivity.prepareIntent(getContext(),
recipeId));
}
private void setViewVisibility(View view, boolean visible) {
if (visible) {
view.setVisibility(View.VISIBLE);
} else {
view.setVisibility(View.INVISIBLE);
}
}
}
我已经自己解决了这个问题。问题是在 onSaveInstanceState() 中保存设备的滚动位置,并在 onViewRestored() 方法中恢复它。
private Parcelable mRecipeListParcelable;
private int mScrollPosition = -1;
@Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
int scrollPosition = ((GridLayoutManager)
mRecipeListRecyclerView.getLayoutManager())
.findFirstCompletelyVisibleItemPosition();
mRecipeListParcelable = gridLayoutManager.onSaveInstanceState();
bundle.putParcelable(KEY_LAYOUT, mRecipeListParcelable);
bundle.putInt(POSITION, scrollPosition);
}
@Override
public void onViewStateRestored(@Nullable Bundle bundle) {
super.onViewStateRestored(bundle);
if(bundle != null) {
mRecipeListParcelable = bundle.getParcelable(KEY_LAYOUT);
mScrollPosition = bundle.getInt(POSITION);
}
}
此外,在 loadProgress() 方法中,我必须设置 scrollToPosition() 并保存滚动位置。
@Override
public void loadProgressBar(boolean show) {
setViewVisibility(mRecipeListRecyclerView, !show);
setViewVisibility(mRecipeListProgressBar, show);
mRecipeListRecyclerView.scrollToPosition(mScrollPosition);
}
另外,要记住的另一件事是,无需在 onResume() 方法中恢复任何内容,因为演示者回调将被调用并且视图将被重置。