view设置padding无效的问题

在写一个自定view的时候,需要给一个子view设置padding值,但是没有任何效果.最后发现stackoverflow中有提到.
解决的方式就是:
先设置背景,再设置padding.
image.png
这是什么操作???
按照提供方案尝试发现:
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无效

image.png
上图查看重写getPadding(Rect pading)的子类,并没有常用的ColorDrawable,BitmapDrawable…在设置padding之后再这是背景颜色,位图背景,或者资源背景都会使padding失效.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

truemi.73

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值