方向更改后如何防止 StaggeredGridLayoutManager 中的项目重新排序?
How to prevent item reordering in StaggeredGridLayoutManager after orientation change?
我知道这个问题听起来与 Whosebug 上的其他问题相似,但这是专门针对方向更改后发生的情况。
我有一个带有工具栏和 RecyclerView 的 Activity。我使用具有垂直方向和 3 列的 StaggeredGridLayoutManager 来填充 RecyclerView。项目布局是一个包含 ImageView 和 TextView 的 LinearLayout。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/grid_item_margin"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dp"
android:paddingTop="2dp" />
</LinearLayout>
图片是从网上加载的。我知道绑定 ViewHolder 之前的图像大小。所以我在 onBindViewHolder 中设置图像高度以防止项目重新排序并通过 Picasso 加载图像:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ViewGroup.LayoutParams lp = holder.imageView.getLayoutParams();
lp.height = imageInfos.get(position).imageHeight;
holder.imageView.setLayoutParams(lp);
mPicasso.load(url)
.noFade()
.into(holder.imageView); // imageView scaleType=centerCrop
}
假设我的适配器中有 50 个项目。我处于纵向模式并向下滚动 20 个项目。然后我切换到横向模式。我现在创建了一个包含 4 列的新 StaggeredGridLayoutManager,比纵向模式多一列。我重新计算图像高度以适应新的目标宽度并在布局上设置保存状态,如下所示:
ArrayList<ImageInfo> imageInfos = savedInstanceState
.getParcelableArrayList(IMG_INFOS_KEY);
setNewImgTargetDimensions(imageInfos, columns);
mAdapter = new ImageGridAdapter(mPicasso, imageInfos);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(layoutMgr);
Parcelable sglmState = savedInstanceState
.getParcelable(LAYOUT_MGR_STATE_KEY);
mRecyclerView.getLayoutManager()
.onRestoreInstanceState(sglmState);
网格自动滚动到所需的存储位置。现在我向上滚动。由于新的图像尺寸,"space above the scrolled position" 无法完全由图像填充。所以项目会重新排序,但大多数时候在网格顶部仍有或多或少的大差距(见底部的截图链接)。当我飞到山顶时,情况最糟。那么在这种情况下有没有办法防止重新排序和间隙?
portrait mode显示第一次加载时的画面。
landscape mode 显示方向更改后向上滚动时的屏幕。左边的图像几乎是透明的,因为我猜这是动画中的自定义淡入淡出。我从示例代码中删除了它以使其更小。
编辑:
我刚刚发现方向改变后所有项目都在屏幕顶部对齐。虽然这看起来不错,但我更希望回收站视图顶部的对齐方式正确。
所以我想出了一个解决方法。简而言之,当网格一直滚动到顶部时,我将间隙策略设置为 GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
以便重新排列项目。
这里有更详细的说明。当使用保存的实例状态调用 onActivityCreated
时,我检查方向是否已更改。如果有,我设置一个标志。在 onScrollStateChanged
回调中,我检查第一个可见项目位置是否为 0。这就是我认为的 "scrolled to the top"。因此,如果用户滚动到顶部并且方向发生变化,我会调用 layoutMgr.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
和 layoutMgr.invalidateSpanAssignments();
(并重置标志)。
我知道这个问题听起来与 Whosebug 上的其他问题相似,但这是专门针对方向更改后发生的情况。
我有一个带有工具栏和 RecyclerView 的 Activity。我使用具有垂直方向和 3 列的 StaggeredGridLayoutManager 来填充 RecyclerView。项目布局是一个包含 ImageView 和 TextView 的 LinearLayout。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/grid_item_margin"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dp"
android:paddingTop="2dp" />
</LinearLayout>
图片是从网上加载的。我知道绑定 ViewHolder 之前的图像大小。所以我在 onBindViewHolder 中设置图像高度以防止项目重新排序并通过 Picasso 加载图像:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ViewGroup.LayoutParams lp = holder.imageView.getLayoutParams();
lp.height = imageInfos.get(position).imageHeight;
holder.imageView.setLayoutParams(lp);
mPicasso.load(url)
.noFade()
.into(holder.imageView); // imageView scaleType=centerCrop
}
假设我的适配器中有 50 个项目。我处于纵向模式并向下滚动 20 个项目。然后我切换到横向模式。我现在创建了一个包含 4 列的新 StaggeredGridLayoutManager,比纵向模式多一列。我重新计算图像高度以适应新的目标宽度并在布局上设置保存状态,如下所示:
ArrayList<ImageInfo> imageInfos = savedInstanceState
.getParcelableArrayList(IMG_INFOS_KEY);
setNewImgTargetDimensions(imageInfos, columns);
mAdapter = new ImageGridAdapter(mPicasso, imageInfos);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(layoutMgr);
Parcelable sglmState = savedInstanceState
.getParcelable(LAYOUT_MGR_STATE_KEY);
mRecyclerView.getLayoutManager()
.onRestoreInstanceState(sglmState);
网格自动滚动到所需的存储位置。现在我向上滚动。由于新的图像尺寸,"space above the scrolled position" 无法完全由图像填充。所以项目会重新排序,但大多数时候在网格顶部仍有或多或少的大差距(见底部的截图链接)。当我飞到山顶时,情况最糟。那么在这种情况下有没有办法防止重新排序和间隙?
portrait mode显示第一次加载时的画面。
landscape mode 显示方向更改后向上滚动时的屏幕。左边的图像几乎是透明的,因为我猜这是动画中的自定义淡入淡出。我从示例代码中删除了它以使其更小。
编辑:
我刚刚发现方向改变后所有项目都在屏幕顶部对齐。虽然这看起来不错,但我更希望回收站视图顶部的对齐方式正确。
所以我想出了一个解决方法。简而言之,当网格一直滚动到顶部时,我将间隙策略设置为 GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
以便重新排列项目。
这里有更详细的说明。当使用保存的实例状态调用 onActivityCreated
时,我检查方向是否已更改。如果有,我设置一个标志。在 onScrollStateChanged
回调中,我检查第一个可见项目位置是否为 0。这就是我认为的 "scrolled to the top"。因此,如果用户滚动到顶部并且方向发生变化,我会调用 layoutMgr.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
和 layoutMgr.invalidateSpanAssignments();
(并重置标志)。