问题现象
如上图,只是设置了paddingStart,在RTL布局下,左右都产生了10dp的间距。其他布局如LinearLayout,FrameLayout则没有这个问题。
private void positionAtEdge(View child, LayoutParams params, int myWidth) {
if (isLayoutRtl()) {
// myWidth是RelativeLayout宽度(父View指定的,不是最终宽度),params.mRight是子View位置。
//这里可以看出计算时已经减去了mPaddingRight(RTL布局下mPaddingRight 就是PaddingStart)
// 所以这个mLeft是考虑了右边距的。
params.mRight = myWidth - mPaddingRight - params.rightMargin;
params.mLeft = params.mRight - child.getMeasuredWidth();
} else {
params.mLeft = mPaddingLeft + params.leftMargin;
params.mRight = params.mLeft + child.getMeasuredWidth();
}
}
...
if (isWrapContentWidth) {
if (isLayoutRtl()) {
if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
width = Math.max(width, myWidth - params.mLeft);
} else {
// 这个width最终会参与RelativeLayout的宽度计算,这里也是包含了右边的padding的。
width = Math.max(width, myWidth - params.mLeft + params.leftMargin);
}
} else {
if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
width = Math.max(width, params.mRight);
} else {
width = Math.max(width, params.mRight + params.rightMargin);
}
}
}
...
if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
// 重点是这里,上面计算的width在问题场景已经包含了右内边距,
// 这里又加了一次。所以最终边距是双份的。
// 从注释也能看出,原生没有考虑这种情况。
width += mPaddingRight;
if (mLayoutParams != null && mLayoutParams.width >= 0) {
width = Math.max(width, mLayoutParams.width);
}
总结:原生在进行测量时没有考虑这种情况,在计算RelativeLayout布局宽度时多加了一个paddStart,但child的位置计算(layout)并未受到影响,所以最终效果类似于既指定了paddStart又指定了paddingEnd。
解决方法
1.使用其他布局,如线性布局,帧布局,约束布局等替换。
2.使用marginStart替代paddingStart实现相同的UX效果。
问题原因
经过分析源码发现这是RelativeLayout测量布局的一个bug。