GradientDrawable 可以绘制渐变的颜色,但现在的 APP 好像已经不流行渐变色的按钮了。
GradientDrawable 的功能非常强大,有了它,几乎不需要使用 ShapeDrawable 了。
有时候你只需要半个圆角。
public void setCornerRadii(float[] radii)
设置圆角。
public void setCornerRadius(float radius)
可以绘制 ShapeDrawable 无法做到的边框。
public void setStroke(int width, ColorStateList colorStateList, float dashWidth, float dashGap)
设置形状
* @param shape The desired shape for this drawable: {@link #LINE},
* {@link #OVAL}, {@link #RECTANGLE} or {@link #RING}
public void setShape(int shape)
Fill 和 Stroke 一样,也可以设置为 ColorStateList,这意味仅仅依靠 GradientDrawable 就能做出很棒的点击效果了(api 21)。
当 View 的 state 改变时,会触发 Drawable 的 onStateChange 函数。
@Override
protected boolean onStateChange(int[] stateSet) {
boolean invalidateSelf = false;
final GradientState s = mGradientState;
final ColorStateList solidColors = s.mSolidColors;
if (solidColors != null) {
final int newColor = solidColors.getColorForState(stateSet, 0);
final int oldColor = mFillPaint.getColor();
if (oldColor != newColor) {
mFillPaint.setColor(newColor);
invalidateSelf = true;
}
}
final Paint strokePaint = mStrokePaint;
if (strokePaint != null) {
final ColorStateList strokeColors = s.mStrokeColors;
if (strokeColors != null) {
final int newColor = strokeColors.getColorForState(stateSet, 0);
final int oldColor = strokePaint.getColor();
if (oldColor != newColor) {
strokePaint.setColor(newColor);
invalidateSelf = true;
}
}
}
if (s.mTint != null && s.mTintMode != null) {
mTintFilter = updateTintFilter(mTintFilter, s.mTint, s.mTintMode);
invalidateSelf = true;
}
if (invalidateSelf) {
invalidateSelf();
return true;
}
return false;
}
Fill 和 Stroke 的 Paint 都会从对应的 ColorStateList 中选取适合的色彩,然后重画。
switch (st.mShape) {
case RECTANGLE:
if (st.mRadiusArray != null) {
buildPathIfDirty();
canvas.drawPath(mPath, mFillPaint);
if (haveStroke) {
canvas.drawPath(mPath, mStrokePaint);
}
} else if (st.mRadius > 0.0f) {
// 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 = Math.min(st.mRadius,
Math.min(mRect.width(), mRect.height()) * 0.5f);
canvas.drawRoundRect(mRect, rad, rad, mFillPaint);
if (haveStroke) {
canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
}
} else {
if (mFillPaint.getColor() != 0 || colorFilter != null ||
mFillPaint.getShader() != null) {
canvas.drawRect(mRect, mFillPaint);
}
if (haveStroke) {
canvas.drawRect(mRect, mStrokePaint);
}
}
break;
case OVAL:
canvas.drawOval(mRect, mFillPaint);
if (haveStroke) {
canvas.drawOval(mRect, mStrokePaint);
}
break;
case LINE: {
RectF r = mRect;
float y = r.centerY();
if (haveStroke) {
canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
}
break;
}
case RING:
Path path = buildRing(st);
canvas.drawPath(path, mFillPaint);
if (haveStroke) {
canvas.drawPath(path, mStrokePaint);
}
break;
}
使用和 InsetDrawable 中一样的方法,获得实际绘制大小 Rect:
private boolean ensureValidRect() {
if (mGradientIsDirty) {
mGradientIsDirty = false;
Rect bounds = getBounds();
float inset = 0;
if (mStrokePaint != null) {
inset = mStrokePaint.getStrokeWidth() * 0.5f;
}
final GradientState st = mGradientState;
mRect.set(bounds.left + inset, bounds.top + inset,
bounds.right - inset, bounds.bottom - inset);