RecyclerView 使用 GridLayoutManager 为什么无法均匀分布?
发布于: 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
版权声明: 本文为 InfoQ 作者【Changing Lin】的原创文章。
原文链接:【http://xie.infoq.cn/article/64eb6da072616ae112a28974b】。文章转载请联系作者。
Changing Lin
关注
获得机遇的手段远超于固有常规之上~ 2020.04.29 加入
我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。
评论