转载请注明出处: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();
}