本文介绍了RecyclerView GridLayoutManager:如何自动检测跨度计数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用新的 GridLayoutManager:https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager.html

Using the new GridLayoutManager: https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager.html

它需要一个明确的跨度计数,所以现在的问题变成:你怎么知道每行有多少个跨度"?毕竟这是一个网格.根据测量的宽度,应该有 RecyclerView 可以容纳的尽可能多的跨度.

It takes an explicit span count, so the problem now becomes: how do you know how many "spans" fit per row? This is a grid, after all. There should be as many spans as the RecyclerView can fit, based on measured width.

使用旧的 GridView,您只需设置columnWidth"属性,它就会自动检测适合的列数.这基本上是我想为 RecyclerView 复制的内容:

Using the old GridView, you would just set the "columnWidth" property and it would automatically detect how many columns fit. This is basically what I want to replicate for the RecyclerView:

  • RecyclerView
  • 上添加 OnLayoutChangeListener
  • 在此回调中,对单个网格项"进行充气并对其进行测量
  • spanCount = recyclerViewWidth/singleItemWidth;

这似乎是很常见的行为,那么有没有我没有看到的更简单的方法?

This seems like pretty common behavior, so is there a simpler way that I'm not seeing?

推荐答案

我个人不喜欢为此将 RecyclerView 子类化,因为对我来说似乎 GridLayoutManager 有责任检测跨度计数.因此,在为 RecyclerView 和 GridLayoutManager 挖掘了一些 android 源代码之后,我编写了自己的类扩展 GridLayoutManager 来完成这项工作:

Personaly I don't like to subclass RecyclerView for this, because for me it seems that there is GridLayoutManager's responsibility to detect span count. So after some android source code digging for RecyclerView and GridLayoutManager I wrote my own class extended GridLayoutManager that do the job:

public class GridAutofitLayoutManager extends GridLayoutManager
{
    private int columnWidth;
    private boolean isColumnWidthChanged = true;
    private int lastWidth;
    private int lastHeight;

    public GridAutofitLayoutManager(@NonNull final Context context, final int columnWidth) {
        /* Initially set spanCount to 1, will be changed automatically later. */
        super(context, 1);
        setColumnWidth(checkedColumnWidth(context, columnWidth));
    }

    public GridAutofitLayoutManager(
        @NonNull final Context context,
        final int columnWidth,
        final int orientation,
        final boolean reverseLayout) {

        /* Initially set spanCount to 1, will be changed automatically later. */
        super(context, 1, orientation, reverseLayout);
        setColumnWidth(checkedColumnWidth(context, columnWidth));
    }

    private int checkedColumnWidth(@NonNull final Context context, final int columnWidth) {
        if (columnWidth <= 0) {
            /* Set default columnWidth value (48dp here). It is better to move this constant
            to static constant on top, but we need context to convert it to dp, so can't really
            do so. */
            columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
                    context.getResources().getDisplayMetrics());
        }
        return columnWidth;
    }

    public void setColumnWidth(final int newColumnWidth) {
        if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
            columnWidth = newColumnWidth;
            isColumnWidthChanged = true;
        }
    }

    @Override
    public void onLayoutChildren(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
        final int width = getWidth();
        final int height = getHeight();
        if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
            final int totalSpace;
            if (getOrientation() == VERTICAL) {
                totalSpace = width - getPaddingRight() - getPaddingLeft();
            } else {
                totalSpace = height - getPaddingTop() - getPaddingBottom();
            }
            final int spanCount = Math.max(1, totalSpace / columnWidth);
            setSpanCount(spanCount);
            isColumnWidthChanged = false;
        }
        lastWidth = width;
        lastHeight = height;
        super.onLayoutChildren(recycler, state);
    }
}

我其实不记得为什么我选择在onLayoutChildren中设置span count,我前段时间写了这个类.但关键是我们需要在视图得到测量后这样做.所以我们可以得到它的高度和宽度.

I don't actually remember why I choosed to set span count in onLayoutChildren, I wrote this class some time ago. But the point is we need to do so after view get measured. so we can get it's height and width.

编辑 1: 修复了因错误设置跨度计数而导致的代码错误.感谢用户 @Elyees Abouda 的报告和建议 解决方案.

EDIT 1: Fix error in code caused to incorrectly setting span count. Thanks user @Elyees Abouda for reporting and suggesting solution.

编辑 2: 一些小的重构和修复边缘情况,手动方向更改处理.感谢用户 @tatarize 的报告和建议 解决方案.

EDIT 2: Some small refactoring and fix edge case with manual orientation changes handling. Thanks user @tatarize for reporting and suggesting solution.

这篇关于RecyclerView GridLayoutManager:如何自动检测跨度计数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-13 18:33