shape标签研究

转载请注明出处:http://blog.csdn.net/kester_/article/details/52628477

用处

<shape> 标签一般可用于一个View的background属性,或者嵌入到<selector> 等标签的某个<item> 里使用。它有以下属性标签可使用。

0.几个内属性

android:useLevel
对应GradientDrawable源码:

private Path buildRing(GradientState st) {
    if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath;
    mPathIsDirty = false;

    float sweep = st.mUseLevelForShape ? (360.0f * getLevel() / 10000.0f) : 360f;
    ···
    ···
    ···
}

这里的useLevel对应mUseLevelForShape,区别下面提到的渐变里useLevel对应mUseLevel;
只有在建立环的时候会用到这个属性,默认true可指定任一阶段的环,也可作为进度条一部分,为false的话为整个环。也就是说shape=”ring”时才有用。

android:dither

/**
 * Set to true to have the drawable dither its colors when drawn to a device
 * with fewer than 8-bits per color component. This can improve the look on
 * those devices, but can also slow down the drawing a little.
 */
public void setDither(boolean dither) {}

抖色,提升效果,会降低绘画速度

android:innerRadius // 内环半径
android:innerRadiusRatio // 缺省内环半径比率
以下是取值的地方,可知shape=”ring”时才有效,且mInnerRadius不存在时mInnerRadiusRatio才有效,mInnerRadiusRatio默认值是3.0f。

if (shapeType == RING) {
    st.mInnerRadius = a.getDimensionPixelSize(
            com.android.internal.R.styleable.GradientDrawable_innerRadius, -1);
    if (st.mInnerRadius == -1) {
        st.mInnerRadiusRatio = a.getFloat(
                com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f);
    }

以下是使用值的地方,可知没有指定内环半径mInnerRadius时使用缺省内环半径,值为bounds.width()/st.mInnerRadiusRatio,而mInnerRadius默认值是3.0f,也就是说什么值都不填的话默认环的内环半径是View的三分之一长。

private Path buildRing(GradientState st) {
   ···
   ···
   ···
   // inner radius
   float radius = st.mInnerRadius != -1 ?
           st.mInnerRadius : bounds.width() / st.mInnerRadiusRatio;
   ···
   ···
   ···

android:shape
形状,默认矩形,有四种选择
ring=环
line=线
oval=椭圆
rectangle=矩形

int shapeType = a.getInt(
    com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE);

android:thickness
android:thicknessRatio

if (shapeType == RING) {
    st.mInnerRadius = a.getDimensionPixelSize(
            com.android.internal.R.styleable.GradientDrawable_innerRadius, -1);
    if (st.mInnerRadius == -1) {
        st.mInnerRadiusRatio = a.getFloat(
                com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f);
    }
    st.mThickness = a.getDimensionPixelSize(
            com.android.internal.R.styleable.GradientDrawable_thickness, -1);
    if (st.mThickness == -1) {
        st.mThicknessRatio = a.getFloat(
                com.android.internal.R.styleable.GradientDrawable_thicknessRatio, 9.0f);
    }
    st.mUseLevelForShape = a.getBoolean(
            com.android.internal.R.styleable.GradientDrawable_useLevel, true);
}

可知,也是在环下有效。其实是决定环厚度的,玩法和innerRadius一样,thicknessRatio默认值为9。


1.<corners>

功能:控制圆角半径

<corners
    android:radius="9dp"                 // 设置四个角的圆角半径都为9dp
    android:topLeftRadius="2dp"          // 设置左上角的圆角半径为2dp
    android:topRightRadius="2dp"         // 设置右上角的圆角半径为2dp
    android:bottomLeftRadius="2dp"       // 设置左下角的圆角半径为2dp
    android:bottomRightRadius="2dp"/>    // 设置右下角的圆角半径为2dp

根据源码GradientDrawable

else if (name.equals("corners")) {
      a = r.obtainAttributes(attrs,
              com.android.internal.R.styleable.DrawableCorners);
      int radius = a.getDimensionPixelSize(
              com.android.internal.R.styleable.DrawableCorners_radius, 0);
      setCornerRadius(radius);
      int topLeftRadius = a.getDimensionPixelSize(
              com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius);
      int topRightRadius = a.getDimensionPixelSize(
              com.android.internal.R.styleable.DrawableCorners_topRightRadius, radius);
      int bottomLeftRadius = a.getDimensionPixelSize(
              com.android.internal.R.styleable.DrawableCorners_bottomLeftRadius, radius);
      int bottomRightRadius = a.getDimensionPixelSize(
              com.android.internal.R.styleable.DrawableCorners_bottomRightRadius, radius);
      if (topLeftRadius != radius || topRightRadius != radius ||
              bottomLeftRadius != radius || bottomRightRadius != radius) {
          // The corner radii are specified in clockwise order (see Path.addRoundRect())
          setCornerRadii(new float[] {
                  topLeftRadius, topLeftRadius,
                  topRightRadius, topRightRadius,
                  bottomRightRadius, bottomRightRadius,
                  bottomLeftRadius, bottomLeftRadius
          });
      }
      a.recycle();
  }
...
...
...
public void setCornerRadii(float[] radii) {
    mRadiusArray = radii;    // ;四个属性设置到mRadiusArray
    if (radii == null) {
        mRadius = 0;
    }
}
···
···
···
case RECTANGLE:
    if (st.mRadiusArray != null) {   // ;优先使用mRadiusArray
        if (mPathIsDirty || mRectIsDirty) {
            mPath.reset();
            mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
            mPathIsDirty = mRectIsDirty = false;
        }
        canvas.drawPath(mPath, mFillPaint);
        if (haveStroke) {
            canvas.drawPath(mPath, mStrokePaint);
        }
    } else if (st.mRadius > 0.0f) {    // ;mRadiusArray为空才使用mRadius
        // since the caller is only giving us 1 value, we will force
        // it to be square if the rect is too small in one dimension
        // to show it. If we did nothing, Skia would clamp the rad
        // independently along each axis, giving us a thin ellipse
        // if the rect were very wide but not very tall
        float rad = st.mRadius;
        float r = Math.min(mRect.width(), mRect.height()) * 0.5f;
        if (rad > r) {
            rad = r;
        }
        canvas.drawRoundRect(mRect, rad, rad, mFillPaint);
        if (haveStroke) {
            canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
        }
    } else {
        if (mFillPaint.getColor() != 0 || mColorFilter != null ||
                mFillPaint.getShader() != null) {
            canvas.drawRect(mRect, mFillPaint);
        }
        if (haveStroke) {
            canvas.drawRect(mRect, mStrokePaint);
        }
    }
    break;

可以发现,设置了topLeftRadius等四个属性时,以topLeftRadius等四个属性的值为准。


2.<gradient>

功能:控制渐变

<gradient
    android:startColor="@android:color/white"         // 渐变启始颜色
    android:centerColor="@android:color/darker_gray"  // 渐变中间颜色
    android:endColor="@android:color/black"           // 渐变结束颜色
    android:useLevel="true"                           // 控制渐变进度
    android:angle="45"                                // 渐变角度
    android:type="radial"                             // 渐变类型
    android:centerX="0"                               // 渐变中间颜色X坐标
    android:centerY="0"                               // 渐变中间颜色Y坐标
    android:gradientRadius="90"/>                     // 渐变半径

android:useLevel=”true”,渐变进度,View.getBackground().setLevel(int)设置渐变的进度,取值[0,10000]。

查看关于”angle”的源码:

if (gradientType == LINEAR_GRADIENT) {
    int angle = (int)a.getFloat(
            com.android.internal.R.styleable.GradientDrawableGradient_angle, 0);
    angle %= 360;
    if (angle % 45 != 0) {
        throw new XmlPullParserException(a.getPositionDescription()
                + "<gradient> tag requires 'angle' attribute to "
                + "be a multiple of 45");
    }

    switch (angle) {
    case 0:
        st.mOrientation = Orientation.LEFT_RIGHT;
        break;
    case 45:
        st.mOrientation = Orientation.BL_TR;
        break;
    case 90:
        st.mOrientation = Orientation.BOTTOM_TOP;
        break;
    case 135:
        st.mOrientation = Orientation.BR_TL;
        break;
    case 180:
        st.mOrientation = Orientation.RIGHT_LEFT;
        break;
    case 225:
        st.mOrientation = Orientation.TR_BL;
        break;
    case 270:
        st.mOrientation = Orientation.TOP_BOTTOM;
        break;
    case 315:
        st.mOrientation = Orientation.TL_BR;
        break;
    }
}

可以发现:android:angle=”45”,渐变角度,只对线性渐变有效,必须是45的倍数,渐变逆时针旋转的度数。

android:type=”radial”,渐变类型,”radial”,辐射状,搭配”gradientRadius”服用效果才有效果;”linear”,线性;”sweep”,扇形。

android:centerColor渐变中间颜色,搭配”centerX”和”centerY”作为中间颜色的坐标,取值[0,1]。

设置了<solid> 填充色的话会渐变会失效。


3.<padding>

功能:控制内间隔

<padding
    android:left="2dp"
    android:top="2dp"
    android:right="2dp"
    android:bottom="2dp"/>
else if (name.equals("padding")) {
    a = r.obtainAttributes(attrs,
            com.android.internal.R.styleable.GradientDrawablePadding);
    mPadding = new Rect(
            a.getDimensionPixelOffset(
                    com.android.internal.R.styleable.GradientDrawablePadding_left, 0),
            a.getDimensionPixelOffset(
                    com.android.internal.R.styleable.GradientDrawablePadding_top, 0),
            a.getDimensionPixelOffset(
                    com.android.internal.R.styleable.GradientDrawablePadding_right, 0),
            a.getDimensionPixelOffset(
                    com.android.internal.R.styleable.GradientDrawablePadding_bottom, 0));
    a.recycle();
    mGradientState.mPadding = mPadding;
}

很纯粹的控制控件的padding。


4.<size>

功能:控制宽高

<size
    android:width="50dp"
    android:height="100dp"/>

如果是作为一个View的Background设置了这个shape的size的话,会取View和Background的宽度最大值作为View的最小宽度,可参见以下View的源码得知。

/**
 * Returns the suggested minimum width that the view should use. This
 * returns the maximum of the view's minimum width)
 * and the background's minimum width
 *  ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).
 * <p>
 * When being used in {@link #onMeasure(int, int)}, the caller should still
 * ensure the returned width is within the requirements of the parent.
 *
 * @return The suggested minimum width of the view.
 */
protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

5.<solid>

功能:控制填充色

<solid
    android:color="#7ff0ff"/>
else if (name.equals("solid")) {
    a = r.obtainAttributes(attrs,
            com.android.internal.R.styleable.GradientDrawableSolid);
    int argb = a.getColor(
            com.android.internal.R.styleable.GradientDrawableSolid_color, 0);
    a.recycle();
    setColor(argb);
}

6.<stroke>

功能:控制描边

<stroke
    android:width="2dp"                   // 描边宽度
    android:color="@android:color/black"  // 描边颜色
    android:dashWidth="1dp"               // 将描边分段的话每段的长度
    android:dashGap="2dp"/>               // 将描边分段的话段与段之间间隔长度
 else if (name.equals("stroke")) {
    a = r.obtainAttributes(attrs,
            com.android.internal.R.styleable.GradientDrawableStroke);
    int width = a.getDimensionPixelSize(
            com.android.internal.R.styleable.GradientDrawableStroke_width, 0);
    int color = a.getColor(
            com.android.internal.R.styleable.GradientDrawableStroke_color, 0);
    float dashWidth = a.getDimension(
            com.android.internal.R.styleable.GradientDrawableStroke_dashWidth, 0);
    if (dashWidth != 0.0f) {
        float dashGap = a.getDimension(
                com.android.internal.R.styleable.GradientDrawableStroke_dashGap, 0);
        setStroke(width, color, dashWidth, dashGap);
    } else {
        setStroke(width, color);
    }
    a.recycle();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值