Android View背景与Shape

某个view需要有点击、焦点、选中的背景反馈是一般使用selector设置各个状态时的背景色。每一个item设置纯色一般比较少见,比较多的是使用bitmap或者shape,如果是bitmap很好理解,状态变化时直接切换渲染的图片就OK,但比较好的方式是用shape自定义图形。 这样做有两点好处,第一能够减小APK包的大小,现在的App很多已经是20+M,对用户的更新造成诸多不便,所以保持App大小很重要,其次能够避免图片平铺拉伸的处理,虽然.9图片从一定程度上保证了屏幕尺寸的兼容。

Shape使用时要注意Padding标签。

在Shape XML中定义了Padding并且此Shape作为某个View的Background时,View就不需要再设置android:padding,因为两者会产生冲突。 源代码分析可见冲突点:

View.java
setBackgroundDrawable()主要读取Drawable的Padding来设置当前View的Padding
------
    if (background != null) {
           Rect padding = sThreadLocal.get();
           if (padding == null) {
               padding = new Rect();
               sThreadLocal.set(padding);
           }
           resetResolvedDrawables();
           background.setLayoutDirection(getLayoutDirection());
           if (background.getPadding(padding)) {   // 读取背景图的Padding
               resetResolvedPadding();             // 重置当前View的Padding
               switch (background.getLayoutDirection()) {
                   case LAYOUT_DIRECTION_RTL:
                       mUserPaddingLeftInitial = padding.right;  //重置View初始化Padding
                       mUserPaddingRightInitial = padding.left;
                       internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);                         //写入View Padding值
                       break;
                   case LAYOUT_DIRECTION_LTR:
                   default:
                       mUserPaddingLeftInitial = padding.left;
                       mUserPaddingRightInitial = padding.right;
                       internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
               }
               mLeftPaddingDefined = false;    //将原有Padding置为false
               mRightPaddingDefined = false;
           }View.java
----------
  public void setPadding(int left, int top, int right, int bottom) {//直接设置View的Padding
       resetResolvedPadding();        //重置View的Padding

       mUserPaddingStart = UNDEFINED_PADDING;
       mUserPaddingEnd = UNDEFINED_PADDING;

       mUserPaddingLeftInitial = left;
       mUserPaddingRightInitial = right;

       mLeftPaddingDefined = true;
       mRightPaddingDefined = true;     //将Padding数据置为true

       internalSetPadding(left, top, right, bottom); //写入Padding值
   }

通过setBackground() 和setPadding()这两个方法的分析可以看到,Padding最终生效的值,取决于两个方法的调用顺序,按照View读取XML的顺序是先读取背景再读取Padding,与XML中标签的先后顺序无关,但如果在Java代码中就取决于两个方法的调用顺序了。