android - 如何正确地将水平 RecyclerView 中的第一个和最后一个项目居中

标签 android android-recyclerview centering item-decoration

StackOverflow 包含很多这样的问题,但到目前为止绝对没有 100% 有效的解决方案。

我尝试了这些解决方案:

RecyclerView ItemDecoration - How to draw a different width divider for every viewHolder?

first item center aligns in SnapHelper in RecyclerView

Horizontally center first item of RecyclerView

LinearSnapHelper doesn't snap on edge items of RecyclerView

Android Centering Item in RecyclerView

How to make recycler view start adding items from center?

How to have RecyclerView snapped to center and yet be able to scroll to all items, while the center is "selected"?

How to snap to particular position of LinearSnapHelper in horizontal RecyclerView?


并且每次第一个项目都无法正确居中。

我正在使用的示例代码

    recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
        @Override
        public void getItemOffsets(
            @NonNull Rect outRect,
            @NonNull View view,
            @NonNull RecyclerView parent,
            @NonNull RecyclerView.State state
        ) {

            super.getItemOffsets(outRect, view, parent, state);

            final int count = state.getItemCount();
            final int position = parent.getChildAdapterPosition(view);

            if (position == 0 || position == count - 1) {

                int offset = (int) (parent.getWidth() * 0.5f - view.getWidth() * 0.5f);

                if (position == 0) {
                    setupOutRect(outRect, offset, true);
                } else if (position == count - 1) {
                    setupOutRect(outRect, offset, false);
                }

            }

        }

        private void setupOutRect(Rect rect, int offset, boolean start) {
            if (start) {
                rect.left = offset;
            } else {
                rect.right = offset;
            }
        }

    });

经过调查我发现这是因为在 getItemOffsetsview.getWidth 为 0,尚未测量。

我试图强制测量它,但每次它给出的尺寸都不正确,与它实际占据的尺寸完全不同,它更小。

我还尝试使用 addOnGlobalLayoutListener 技巧,但是当它被调用并具有正确的宽度时,outRect 已经被消耗,所以它丢失了。

我不想设置任何固定大小,因为 RecyclerView 中的项目可以有不同的大小,所以提前设置它的填充不是一个选项。

我也不想添加“幽灵”项目来填充空间,这些项目也不适合滚动体验。

我怎样才能让它正常工作?

理想情况下,ItemDecorator 方法看起来是最好的,但对于第一个项目,它立即表现平平。

最佳答案

我一直在使用来自 Pawel's answerCenterLinearLayoutManager到目前为止,它几乎完美地工作。我说几乎,不是因为它不能立即正常工作,而是因为我最终在使用上述布局管理器的同一 RecyclerView 中使用了 LinearSnapHelper .

因为 snap 助手依赖于了解 RecyclerView 填充来计算它的正确中心,所以只为第一个项目设置填充会抛出这个过程,导致第一个项目(和后续项目直到最后一个一个实际显示)从中心偏移。

我的解决方案是确保在显示第一个项目时从一开始就正确设置两个内边距。

所以这样:

if (!reverseLayout) {
    if (lp == 0) recyclerView.updatePaddingRelative(start = hPadding)
    if (lp == itemCount - 1) recyclerView.updatePaddingRelative(end = hPadding)
  } else {
    if (lp == 0) recyclerView.updatePaddingRelative(end = hPadding)
    if (lp == itemCount - 1) recyclerView.updatePaddingRelative(start = hPadding)
  }

变成这样

if (!reverseLayout) {
    if (lp == 0) recyclerView.updatePaddingRelative(start = hPadding, end = hPadding) // here we set the same padding for both sides
    if (lp == itemCount - 1) recyclerView.updatePaddingRelative(end = hPadding)
   } else {
    if (lp == 0) recyclerView.updatePaddingRelative(end = hPadding, start = hPadding) // here we set the same padding for both sides
    if (lp == itemCount - 1) recyclerView.updatePaddingRelative(start = hPadding)
   }

而且我假设同样的逻辑也必须应用于垂直 block 。

我相信这现在可以进一步优化,所以上面的最后一个 block 看起来像这样:

if (lp == 0) recyclerView.updatePaddingRelative(start = hPadding, end = hPadding) // here we set the same padding for both sides
if (lp == itemCount - 1) {
   if (!reverseLayout) recyclerView.updatePaddingRelative(end = hPadding)
   if (reverseLayout) recyclerView.updatePaddingRelative(start = hPadding)
}

注意:我最终发现,如果我使用的项目之间的宽度差异很大,那么当最后一个项目加载时,我在这里提到的同样的问题也会发生,这是有道理的,因为那时我们在不同的一侧设置了填充比另一个,再次摆脱了原生中心计算。

这个的解决方案是在第一个项目和最后一个项目加载时为两侧设置相同的填充

recyclerView.updatePaddingRelative(start = hPadding, end = hPadding)

就是这样,没有像前面示例中那样的 if。

当然,这仍然不能解决当显示的项目太少以至于第一个和最后一个项目可见时的问题,但是当我设法找到针对该特定情况的解决方案时,我会更新它在这里。

关于android - 如何正确地将水平 RecyclerView 中的第一个和最后一个项目居中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64853649/

相关文章:

Android 从月份名称创建 ArrayList

android - HideBottomViewOnScrollBehavior 不适用于回收器 View 项目展开/折叠

python - 如何在 PyQt5 中居中窗口标题?

android - 身份验证 - Xamarin.Forms、Azure 移动应用

java - Android 中的 Class<T, String> 类型是什么?

java - Android 中的嵌套 ArrayList

android - 在 android 中添加项目之前,是否可以估计 RecyclerView.Adapter 子项的数量?

android RecyclerView 背景颜色

Android textview 居中定位

html - 将固定容器置于引导轮播之上