写点什么

RecyclerView 使用 GridLayoutManager 为什么无法均匀分布?

用户头像
Changing Lin
关注
发布于: 1 小时前

1.现象

我在开发一个 IM 项目的时候,发现一个现象,使用 RecyclerView 绑定 GridLayoutManager 来表格展示应用的列表,但是很容易发现列表的水平布局很不协调,左内边距和右内边距是不对等的,所以,想弄清楚这是什么原因导致的,是否是 RecyclerView 或 GridLayoutManager 的漏洞?


2.原理

  • 在分析问题的时候,查阅 GridLayoutManager 源码有这样一段有意思的处理

 private void updateMeasurements() {        int totalSpace;        if (getOrientation() == VERTICAL) {            totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();        } else {            totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();        }        calculateItemBorders(totalSpace); // 计算每一个表格中子View的边界    }
@Override public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) { if (mCachedBorders == null) { super.setMeasuredDimension(childrenBounds, wSpec, hSpec); } final int width, height; final int horizontalPadding = getPaddingLeft() + getPaddingRight(); final int verticalPadding = getPaddingTop() + getPaddingBottom(); if (mOrientation == VERTICAL) { // 垂直方向列表 final int usedHeight = childrenBounds.height() + verticalPadding; height = chooseSize(hSpec, usedHeight, getMinimumHeight()); width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding, getMinimumWidth()); // 综合计算 子View的宽度 } else { final int usedWidth = childrenBounds.width() + horizontalPadding; width = chooseSize(wSpec, usedWidth, getMinimumWidth()); height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding, getMinimumHeight()); } setMeasuredDimension(width, height); }
/** * @param totalSpace Total available space after padding is removed */ private void calculateItemBorders(int totalSpace) { mCachedBorders = calculateItemBorders(mCachedBorders, mSpanCount, totalSpace); }
/** * @param cachedBorders The out array * @param spanCount number of spans * @param totalSpace total available space after padding is removed * @return The updated array. Might be the same instance as the provided array if its size * has not changed. */ static int[] calculateItemBorders(int[] cachedBorders, int spanCount, int totalSpace) { if (cachedBorders == null || cachedBorders.length != spanCount + 1 || cachedBorders[cachedBorders.length - 1] != totalSpace) { cachedBorders = new int[spanCount + 1]; } cachedBorders[0] = 0; int sizePerSpan = totalSpace / spanCount; // 计算每一个子View的宽度 int sizePerSpanRemainder = totalSpace % spanCount; // 计算剩余总空间大小 int consumedPixels = 0; int additionalSize = 0; for (int i = 1; i <= spanCount; i++) { // spanCount表示一行有几列, int itemSize = sizePerSpan; additionalSize += sizePerSpanRemainder; if (additionalSize > 0 && (spanCount - additionalSize) < sizePerSpanRemainder) { itemSize += 1; additionalSize -= spanCount; } consumedPixels += itemSize; cachedBorders[i] = consumedPixels; } return cachedBorders; }
复制代码



  • 如上图所示:

  • 是如何计算子 View 的宽度的,看子 View 的宽度配置,是具体数值/match_parent/wrap_content 等


/** * Chooses a size from the given specs and parameters that is closest to the desired size * and also complies with the spec. * * @param spec The measureSpec * @param desired The preferred measurement * @param min The minimum value * * @return A size that fits to the given specs */ public static int chooseSize(int spec, int desired, int min) { final int mode = View.MeasureSpec.getMode(spec); final int size = View.MeasureSpec.getSize(spec); switch (mode) { case View.MeasureSpec.EXACTLY: return size; case View.MeasureSpec.AT_MOST: return Math.min(size, Math.max(desired, min)); case View.MeasureSpec.UNSPECIFIED: default: return Math.max(desired, min); } }
复制代码

3.总结

  • 未完待续

发布于: 1 小时前阅读数: 3
用户头像

Changing Lin

关注

获得机遇的手段远超于固有常规之上~ 2020.04.29 加入

我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。

评论

发布
暂无评论
RecyclerView使用GridLayoutManager为什么无法均匀分布?