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);