在写一个自定view的时候,需要给一个子view设置padding值,但是没有任何效果.最后发现stackoverflow中有提到.
解决的方式就是:
先设置背景,再设置padding.
这是什么操作???
按照提供方案尝试发现:
view设置padding之后,在设置view的background,会使padding失效.
解决方法:
- 在设置padding之前,设置background
- 在设置background之后,再次设置padding
下面看看为什么会是这样!
源码读起来:
View中,setPadding()如下
public void setPadding(int left, int top, int right, int bottom) {
resetResolvedPaddingInternal();
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
mUserPaddingLeftInitial = left;
mUserPaddingRightInitial = right;
mLeftPaddingDefined = true;
mRightPaddingDefined = true;
internalSetPadding(left, top, right, bottom);
}
可以看到调用了 internalSetPadding(left, top, right, bottom);继续跟进==>
protected void internalSetPadding(int left, int top, int right, int bottom) {
mUserPaddingLeft = left;
mUserPaddingRight = right;
mUserPaddingBottom = bottom;
final int viewFlags = mViewFlags;
boolean changed = false;
// Common case is there are no scroll bars.
if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) {
if ((viewFlags & SCROLLBARS_VERTICAL) != 0) {
final int offset = (viewFlags & SCROLLBARS_INSET_MASK) == 0
? 0 : getVerticalScrollbarWidth();
switch (mVerticalScrollbarPosition) {
case SCROLLBAR_POSITION_DEFAULT:
if (isLayoutRtl()) {
left += offset;
} else {
right += offset;
}
break;
case SCROLLBAR_POSITION_RIGHT:
right += offset;
break;
case SCROLLBAR_POSITION_LEFT:
left += offset;
break;
}
}
if ((viewFlags & SCROLLBARS_HORIZONTAL) != 0) {
bottom += (viewFlags & SCROLLBARS_INSET_MASK) == 0
? 0 : getHorizontalScrollbarHeight();
}
}
if (mPaddingLeft != left) {
changed = true;
mPaddingLeft = left;
}
if (mPaddingTop != top) {
changed = true;
mPaddingTop = top;
}
if (mPaddingRight != right) {
changed = true;
mPaddingRight = right;
}
if (mPaddingBottom != bottom) {
changed = true;
mPaddingBottom = bottom;
}
if (changed) {
requestLayout();
invalidateOutline();
}
}
上面这段代码有点长,但是很简单,先判断是不是有纵向的滚动指示器,有的话,加上相应的padding值,然后判断是否有水平指示器,有的话,bottom加上padding值.然后判断,各个padding值是不是为0,不为零, 则changed = true;
说明发生改变,并把值赋值给对应的mPadding,然后调用invalidateOutline();
重建轮廓,刷新view.
看到这,就可以确定,问题不是出在setPadding这里,那一定是setBackgroundXXX()的问题了!
继续看源码:
public void setBackgroundColor(@ColorInt int color) {
if (mBackground instanceof ColorDrawable) {
((ColorDrawable) mBackground.mutate()).setColor(color);
computeOpaqueFlags();
mBackgroundResource = 0;
} else {
setBackground(new ColorDrawable(color));
}
}
我们就从setBackgroundColor()方法进入,可以看到调用了setBackground(new ColorDrawable(color));
跟进
public void setBackground(Drawable background) {
setBackgroundDrawable(background);
}
紧接着调用了setBackgroundDrawable(background);
public void setBackgroundDrawable(Drawable background) {
computeOpaqueFlags();
...//略
if (background != null) {//这里是我们代码传入的ColorDrawable
Rect padding = sThreadLocal.get();//获取当前线程存放的rect对象(用于保存padding)
if (padding == null) {
padding = new Rect();
sThreadLocal.set(padding);
}
resetResolvedDrawablesInternal();
background.setLayoutDirection(getLayoutDirection());
if (background.getPadding(padding)) {//(敲黑板,划重点)这里调用了getPadding()
resetResolvedPaddingInternal();
switch (background.getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
mUserPaddingLeftInitial = padding.right;
mUserPaddingRightInitial = padding.left;
internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
break;
case LAYOUT_DIRECTION_LTR:
default:
mUserPaddingLeftInitial = padding.left;
mUserPaddingRightInitial = padding.right;
internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
}
mLeftPaddingDefined = false;
mRightPaddingDefined = false;
}
.......
if (requestLayout) {
requestLayout();
}
mBackgroundSizeChanged = true;
invalidate(true);
invalidateOutline();
}
这段代码有点长,我截取了部分,我们可以看到,通过Rect padding = sThreadLocal.get();
获取Rect对象,然后传给了background.getPadding(padding)
我们看看getPadding()方法:
public boolean getPadding(@NonNull Rect padding) {
padding.set(0, 0, 0, 0);
return false;
}
可以看到,直接调用的是Drawable的getPadding()方法,首先padding.set(0, 0, 0, 0);
把padding全部设置为0,然后返回false;看到这,应该就清楚了:
我们设置了padding值,但是由于在调用setBackgroundColor(int color)
的时候,底层调用了Drawable的getPadding(Rect pading)
,通过padding.set(0, 0, 0, 0);
把padding全部置0,从而导致我们设置的padding无效
上图查看重写
getPadding(Rect pading)
的子类,并没有常用的ColorDrawable,BitmapDrawable...在设置padding之后再这是背景颜色,位图背景,或者资源背景都会使padding失效.