2 * Copyright (C) 2006 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.graphics.drawable;
19 import android.content.res.Resources;
20 import android.content.res.TypedArray;
21 import android.graphics.Canvas;
22 import android.graphics.ColorFilter;
23 import android.graphics.DashPathEffect;
24 import android.graphics.LinearGradient;
25 import android.graphics.Paint;
26 import android.graphics.PixelFormat;
27 import android.graphics.Rect;
28 import android.graphics.RectF;
29 import android.graphics.Shader;
30 import android.graphics.Path;
31 import android.graphics.RadialGradient;
32 import android.graphics.SweepGradient;
33 import android.util.AttributeSet;
34 import android.util.Log;
35 import android.util.TypedValue;
37 import org.xmlpull.v1.XmlPullParser;
38 import org.xmlpull.v1.XmlPullParserException;
40 import java.io.IOException;
43 * A Drawable with a color gradient for buttons, backgrounds, etc.
45 * <p>It can be defined in an XML file with the <code><shape></code> element. For more
46 * information, see the guide to <a
47 * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
49 * @attr ref android.R.styleable#GradientDrawable_visible
50 * @attr ref android.R.styleable#GradientDrawable_shape
51 * @attr ref android.R.styleable#GradientDrawable_innerRadiusRatio
52 * @attr ref android.R.styleable#GradientDrawable_innerRadius
53 * @attr ref android.R.styleable#GradientDrawable_thicknessRatio
54 * @attr ref android.R.styleable#GradientDrawable_thickness
55 * @attr ref android.R.styleable#GradientDrawable_useLevel
56 * @attr ref android.R.styleable#GradientDrawableSize_width
57 * @attr ref android.R.styleable#GradientDrawableSize_height
58 * @attr ref android.R.styleable#GradientDrawableGradient_startColor
59 * @attr ref android.R.styleable#GradientDrawableGradient_centerColor
60 * @attr ref android.R.styleable#GradientDrawableGradient_endColor
61 * @attr ref android.R.styleable#GradientDrawableGradient_useLevel
62 * @attr ref android.R.styleable#GradientDrawableGradient_angle
63 * @attr ref android.R.styleable#GradientDrawableGradient_type
64 * @attr ref android.R.styleable#GradientDrawableGradient_centerX
65 * @attr ref android.R.styleable#GradientDrawableGradient_centerY
66 * @attr ref android.R.styleable#GradientDrawableGradient_gradientRadius
67 * @attr ref android.R.styleable#GradientDrawableSolid_color
68 * @attr ref android.R.styleable#GradientDrawableStroke_width
69 * @attr ref android.R.styleable#GradientDrawableStroke_color
70 * @attr ref android.R.styleable#GradientDrawableStroke_dashWidth
71 * @attr ref android.R.styleable#GradientDrawableStroke_dashGap
72 * @attr ref android.R.styleable#GradientDrawablePadding_left
73 * @attr ref android.R.styleable#GradientDrawablePadding_top
74 * @attr ref android.R.styleable#GradientDrawablePadding_right
75 * @attr ref android.R.styleable#GradientDrawablePadding_bottom
77 public class GradientDrawable extends Drawable {
79 * Shape is a rectangle, possibly with rounded corners
81 public static final int RECTANGLE = 0;
86 public static final int OVAL = 1;
91 public static final int LINE = 2;
96 public static final int RING = 3;
99 * Gradient is linear (default.)
101 public static final int LINEAR_GRADIENT = 0;
104 * Gradient is circular.
106 public static final int RADIAL_GRADIENT = 1;
109 * Gradient is a sweep.
111 public static final int SWEEP_GRADIENT = 2;
113 private GradientState mGradientState;
115 private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
116 private Rect mPadding;
117 private Paint mStrokePaint; // optional, set by the caller
118 private ColorFilter mColorFilter; // optional, set by the caller
119 private int mAlpha = 0xFF; // modified by the caller
120 private boolean mDither;
122 private final Path mPath = new Path();
123 private final RectF mRect = new RectF();
125 private Paint mLayerPaint; // internal, used if we use saveLayer()
126 private boolean mRectIsDirty; // internal state
127 private boolean mMutated;
128 private Path mRingPath;
129 private boolean mPathIsDirty;
132 * Controls how the gradient is oriented relative to the drawable's bounds
134 public enum Orientation {
135 /** draw the gradient from the top to the bottom */
137 /** draw the gradient from the top-right to the bottom-left */
139 /** draw the gradient from the right to the left */
141 /** draw the gradient from the bottom-right to the top-left */
143 /** draw the gradient from the bottom to the top */
145 /** draw the gradient from the bottom-left to the top-right */
147 /** draw the gradient from the left to the right */
149 /** draw the gradient from the top-left to the bottom-right */
153 public GradientDrawable() {
154 this(new GradientState(Orientation.TOP_BOTTOM, null));
158 * Create a new gradient drawable given an orientation and an array
159 * of colors for the gradient.
161 public GradientDrawable(Orientation orientation, int[] colors) {
162 this(new GradientState(orientation, colors));
166 public boolean getPadding(Rect padding) {
167 if (mPadding != null) {
168 padding.set(mPadding);
171 return super.getPadding(padding);
176 * Specify radii for each of the 4 corners. For each corner, the array
177 * contains 2 values, [X_radius, Y_radius]. The corners are ordered
178 * top-left, top-right, bottom-right, bottom-left
180 public void setCornerRadii(float[] radii) {
181 mGradientState.setCornerRadii(radii);
185 * Specify radius for the corners of the gradient. If this is > 0, then the
186 * drawable is drawn in a round-rectangle, rather than a rectangle.
188 public void setCornerRadius(float radius) {
189 mGradientState.setCornerRadius(radius);
193 * Set the stroke width and color for the drawable. If width is zero,
194 * then no stroke is drawn.
196 public void setStroke(int width, int color) {
197 setStroke(width, color, 0, 0);
200 public void setStroke(int width, int color, float dashWidth, float dashGap) {
201 mGradientState.setStroke(width, color, dashWidth, dashGap);
203 if (mStrokePaint == null) {
204 mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
205 mStrokePaint.setStyle(Paint.Style.STROKE);
207 mStrokePaint.setStrokeWidth(width);
208 mStrokePaint.setColor(color);
210 DashPathEffect e = null;
212 e = new DashPathEffect(new float[] { dashWidth, dashGap }, 0);
214 mStrokePaint.setPathEffect(e);
217 public void setSize(int width, int height) {
218 mGradientState.setSize(width, height);
221 public void setShape(int shape) {
223 mGradientState.setShape(shape);
226 public void setGradientType(int gradient) {
227 mGradientState.setGradientType(gradient);
231 public void setGradientCenter(float x, float y) {
232 mGradientState.setGradientCenter(x, y);
235 public void setGradientRadius(float gradientRadius) {
236 mGradientState.setGradientRadius(gradientRadius);
239 public void setUseLevel(boolean useLevel) {
240 mGradientState.mUseLevel = useLevel;
243 private int modulateAlpha(int alpha) {
244 int scale = mAlpha + (mAlpha >> 7);
245 return alpha * scale >> 8;
249 public void draw(Canvas canvas) {
250 if (!ensureValidRect()) {
255 // remember the alpha values, in case we temporarily overwrite them
256 // when we modulate them with mAlpha
257 final int prevFillAlpha = mFillPaint.getAlpha();
258 final int prevStrokeAlpha = mStrokePaint != null ? mStrokePaint.getAlpha() : 0;
259 // compute the modulate alpha values
260 final int currFillAlpha = modulateAlpha(prevFillAlpha);
261 final int currStrokeAlpha = modulateAlpha(prevStrokeAlpha);
263 final boolean haveStroke = currStrokeAlpha > 0 && mStrokePaint.getStrokeWidth() > 0;
264 final boolean haveFill = currFillAlpha > 0;
265 final GradientState st = mGradientState;
266 /* we need a layer iff we're drawing both a fill and stroke, and the
267 stroke is non-opaque, and our shapetype actually supports
268 fill+stroke. Otherwise we can just draw the stroke (if any) on top
269 of the fill (if any) without worrying about blending artifacts.
271 final boolean useLayer = haveStroke && haveFill && st.mShape != LINE &&
272 currStrokeAlpha < 255;
274 /* Drawing with a layer is slower than direct drawing, but it
275 allows us to apply paint effects like alpha and colorfilter to
276 the result of multiple separate draws. In our case, if the user
277 asks for a non-opaque alpha value (via setAlpha), and we're
278 stroking, then we need to apply the alpha AFTER we've drawn
279 both the fill and the stroke.
282 if (mLayerPaint == null) {
283 mLayerPaint = new Paint();
285 mLayerPaint.setDither(mDither);
286 mLayerPaint.setAlpha(mAlpha);
287 mLayerPaint.setColorFilter(mColorFilter);
289 float rad = mStrokePaint.getStrokeWidth();
290 canvas.saveLayer(mRect.left - rad, mRect.top - rad,
291 mRect.right + rad, mRect.bottom + rad,
292 mLayerPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
294 // don't perform the filter in our individual paints
295 // since the layer will do it for us
296 mFillPaint.setColorFilter(null);
297 mStrokePaint.setColorFilter(null);
299 /* if we're not using a layer, apply the dither/filter to our
302 mFillPaint.setAlpha(currFillAlpha);
303 mFillPaint.setDither(mDither);
304 mFillPaint.setColorFilter(mColorFilter);
306 mStrokePaint.setAlpha(currStrokeAlpha);
307 mStrokePaint.setDither(mDither);
308 mStrokePaint.setColorFilter(mColorFilter);
314 if (st.mRadiusArray != null) {
316 mPath.addRoundRect(mRect, st.mRadiusArray,
318 canvas.drawPath(mPath, mFillPaint);
320 canvas.drawPath(mPath, mStrokePaint);
324 // since the caller is only giving us 1 value, we will force
325 // it to be square if the rect is too small in one dimension
326 // to show it. If we did nothing, Skia would clamp the rad
327 // independently along each axis, giving us a thin ellips
328 // if the rect were very wide but not very tall
329 float rad = st.mRadius;
330 float r = Math.min(mRect.width(), mRect.height()) * 0.5f;
334 canvas.drawRoundRect(mRect, rad, rad, mFillPaint);
336 canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
341 canvas.drawOval(mRect, mFillPaint);
343 canvas.drawOval(mRect, mStrokePaint);
348 float y = r.centerY();
349 canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
353 Path path = buildRing(st);
354 canvas.drawPath(path, mFillPaint);
356 canvas.drawPath(path, mStrokePaint);
364 mFillPaint.setAlpha(prevFillAlpha);
366 mStrokePaint.setAlpha(prevStrokeAlpha);
371 private Path buildRing(GradientState st) {
372 if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath;
373 mPathIsDirty = false;
375 float sweep = st.mUseLevelForShape ? (360.0f * getLevel() / 10000.0f) : 360f;
377 RectF bounds = new RectF(mRect);
379 float x = bounds.width() / 2.0f;
380 float y = bounds.height() / 2.0f;
382 float thickness = st.mThickness != -1 ?
383 st.mThickness : bounds.width() / st.mThicknessRatio;
385 float radius = st.mInnerRadius != -1 ?
386 st.mInnerRadius : bounds.width() / st.mInnerRadiusRatio;
388 RectF innerBounds = new RectF(bounds);
389 innerBounds.inset(x - radius, y - radius);
391 bounds = new RectF(innerBounds);
392 bounds.inset(-thickness, -thickness);
394 if (mRingPath == null) {
395 mRingPath = new Path();
400 final Path ringPath = mRingPath;
401 // arcTo treats the sweep angle mod 360, so check for that, since we
402 // think 360 means draw the entire oval
403 if (sweep < 360 && sweep > -360) {
404 ringPath.setFillType(Path.FillType.EVEN_ODD);
406 ringPath.moveTo(x + radius, y);
408 ringPath.lineTo(x + radius + thickness, y);
410 ringPath.arcTo(bounds, 0.0f, sweep, false);
412 ringPath.arcTo(innerBounds, sweep, -sweep, false);
415 // add the entire ovals
416 ringPath.addOval(bounds, Path.Direction.CW);
417 ringPath.addOval(innerBounds, Path.Direction.CCW);
423 public void setColor(int argb) {
424 mGradientState.setSolidColor(argb);
425 mFillPaint.setColor(argb);
429 public int getChangingConfigurations() {
430 return super.getChangingConfigurations()
431 | mGradientState.mChangingConfigurations;
435 public void setAlpha(int alpha) {
440 public void setDither(boolean dither) {
445 public void setColorFilter(ColorFilter cf) {
450 public int getOpacity() {
451 // XXX need to figure out the actual opacity...
452 return PixelFormat.TRANSLUCENT;
456 protected void onBoundsChange(Rect r) {
457 super.onBoundsChange(r);
464 protected boolean onLevelChange(int level) {
465 super.onLevelChange(level);
473 * This checks mRectIsDirty, and if it is true, recomputes both our drawing
474 * rectangle (mRect) and the gradient itself, since it depends on our
476 * @return true if the resulting rectangle is not empty, false otherwise
478 private boolean ensureValidRect() {
480 mRectIsDirty = false;
482 Rect bounds = getBounds();
485 if (mStrokePaint != null) {
486 inset = mStrokePaint.getStrokeWidth() * 0.5f;
489 final GradientState st = mGradientState;
491 mRect.set(bounds.left + inset, bounds.top + inset,
492 bounds.right - inset, bounds.bottom - inset);
494 final int[] colors = st.mColors;
495 if (colors != null) {
497 float x0, x1, y0, y1;
499 if (st.mGradient == LINEAR_GRADIENT) {
500 final float level = st.mUseLevel ? (float) getLevel() / 10000.0f : 1.0f;
501 switch (st.mOrientation) {
503 x0 = r.left; y0 = r.top;
504 x1 = x0; y1 = level * r.bottom;
507 x0 = r.right; y0 = r.top;
508 x1 = level * r.left; y1 = level * r.bottom;
511 x0 = r.right; y0 = r.top;
512 x1 = level * r.left; y1 = y0;
515 x0 = r.right; y0 = r.bottom;
516 x1 = level * r.left; y1 = level * r.top;
519 x0 = r.left; y0 = r.bottom;
520 x1 = x0; y1 = level * r.top;
523 x0 = r.left; y0 = r.bottom;
524 x1 = level * r.right; y1 = level * r.top;
527 x0 = r.left; y0 = r.top;
528 x1 = level * r.right; y1 = y0;
531 x0 = r.left; y0 = r.top;
532 x1 = level * r.right; y1 = level * r.bottom;
536 mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1,
537 colors, st.mPositions, Shader.TileMode.CLAMP));
538 } else if (st.mGradient == RADIAL_GRADIENT) {
539 x0 = r.left + (r.right - r.left) * st.mCenterX;
540 y0 = r.top + (r.bottom - r.top) * st.mCenterY;
542 final float level = st.mUseLevel ? (float) getLevel() / 10000.0f : 1.0f;
544 mFillPaint.setShader(new RadialGradient(x0, y0,
545 level * st.mGradientRadius, colors, null,
546 Shader.TileMode.CLAMP));
547 } else if (st.mGradient == SWEEP_GRADIENT) {
548 x0 = r.left + (r.right - r.left) * st.mCenterX;
549 y0 = r.top + (r.bottom - r.top) * st.mCenterY;
551 int[] tempColors = colors;
552 float[] tempPositions = null;
555 tempColors = st.mTempColors;
556 final int length = colors.length;
557 if (tempColors == null || tempColors.length != length + 1) {
558 tempColors = st.mTempColors = new int[length + 1];
560 System.arraycopy(colors, 0, tempColors, 0, length);
561 tempColors[length] = colors[length - 1];
563 tempPositions = st.mTempPositions;
564 final float fraction = 1.0f / (float) (length - 1);
565 if (tempPositions == null || tempPositions.length != length + 1) {
566 tempPositions = st.mTempPositions = new float[length + 1];
569 final float level = (float) getLevel() / 10000.0f;
570 for (int i = 0; i < length; i++) {
571 tempPositions[i] = i * fraction * level;
573 tempPositions[length] = 1.0f;
576 mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions));
580 return !mRect.isEmpty();
584 public void inflate(Resources r, XmlPullParser parser,
586 throws XmlPullParserException, IOException {
588 final GradientState st = mGradientState;
590 TypedArray a = r.obtainAttributes(attrs,
591 com.android.internal.R.styleable.GradientDrawable);
593 super.inflateWithAttributes(r, parser, a,
594 com.android.internal.R.styleable.GradientDrawable_visible);
596 int shapeType = a.getInt(
597 com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE);
599 if (shapeType == RING) {
600 st.mInnerRadius = a.getDimensionPixelSize(
601 com.android.internal.R.styleable.GradientDrawable_innerRadius, -1);
602 if (st.mInnerRadius == -1) {
603 st.mInnerRadiusRatio = a.getFloat(
604 com.android.internal.R.styleable.GradientDrawable_innerRadiusRatio, 3.0f);
606 st.mThickness = a.getDimensionPixelSize(
607 com.android.internal.R.styleable.GradientDrawable_thickness, -1);
608 if (st.mThickness == -1) {
609 st.mThicknessRatio = a.getFloat(
610 com.android.internal.R.styleable.GradientDrawable_thicknessRatio, 9.0f);
612 st.mUseLevelForShape = a.getBoolean(
613 com.android.internal.R.styleable.GradientDrawable_useLevel, true);
622 final int innerDepth = parser.getDepth()+1;
624 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
625 && ((depth=parser.getDepth()) >= innerDepth
626 || type != XmlPullParser.END_TAG)) {
627 if (type != XmlPullParser.START_TAG) {
631 if (depth > innerDepth) {
635 String name = parser.getName();
637 if (name.equals("size")) {
638 a = r.obtainAttributes(attrs,
639 com.android.internal.R.styleable.GradientDrawableSize);
640 int width = a.getDimensionPixelSize(
641 com.android.internal.R.styleable.GradientDrawableSize_width, -1);
642 int height = a.getDimensionPixelSize(
643 com.android.internal.R.styleable.GradientDrawableSize_height, -1);
645 setSize(width, height);
646 } else if (name.equals("gradient")) {
647 a = r.obtainAttributes(attrs,
648 com.android.internal.R.styleable.GradientDrawableGradient);
649 int startColor = a.getColor(
650 com.android.internal.R.styleable.GradientDrawableGradient_startColor, 0);
651 boolean hasCenterColor = a
652 .hasValue(com.android.internal.R.styleable.GradientDrawableGradient_centerColor);
653 int centerColor = a.getColor(
654 com.android.internal.R.styleable.GradientDrawableGradient_centerColor, 0);
655 int endColor = a.getColor(
656 com.android.internal.R.styleable.GradientDrawableGradient_endColor, 0);
657 int gradientType = a.getInt(
658 com.android.internal.R.styleable.GradientDrawableGradient_type,
661 st.mCenterX = getFloatOrFraction(
663 com.android.internal.R.styleable.GradientDrawableGradient_centerX,
666 st.mCenterY = getFloatOrFraction(
668 com.android.internal.R.styleable.GradientDrawableGradient_centerY,
671 st.mUseLevel = a.getBoolean(
672 com.android.internal.R.styleable.GradientDrawableGradient_useLevel, false);
673 st.mGradient = gradientType;
675 if (gradientType == LINEAR_GRADIENT) {
676 int angle = (int)a.getFloat(
677 com.android.internal.R.styleable.GradientDrawableGradient_angle, 0);
679 if (angle % 45 != 0) {
680 throw new XmlPullParserException(a.getPositionDescription()
681 + "<gradient> tag requires 'angle' attribute to "
682 + "be a multiple of 45");
687 st.mOrientation = Orientation.LEFT_RIGHT;
690 st.mOrientation = Orientation.BL_TR;
693 st.mOrientation = Orientation.BOTTOM_TOP;
696 st.mOrientation = Orientation.BR_TL;
699 st.mOrientation = Orientation.RIGHT_LEFT;
702 st.mOrientation = Orientation.TR_BL;
705 st.mOrientation = Orientation.TOP_BOTTOM;
708 st.mOrientation = Orientation.TL_BR;
712 TypedValue tv = a.peekValue(
713 com.android.internal.R.styleable.GradientDrawableGradient_gradientRadius);
715 boolean radiusRel = tv.type == TypedValue.TYPE_FRACTION;
716 st.mGradientRadius = radiusRel ?
717 tv.getFraction(1.0f, 1.0f) : tv.getFloat();
718 } else if (gradientType == RADIAL_GRADIENT) {
719 throw new XmlPullParserException(
720 a.getPositionDescription()
721 + "<gradient> tag requires 'gradientRadius' "
722 + "attribute with radial type");
728 if (hasCenterColor) {
729 st.mColors = new int[3];
730 st.mColors[0] = startColor;
731 st.mColors[1] = centerColor;
732 st.mColors[2] = endColor;
734 st.mPositions = new float[3];
735 st.mPositions[0] = 0.0f;
736 // Since 0.5f is default value, try to take the one that isn't 0.5f
737 st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY;
738 st.mPositions[2] = 1f;
740 st.mColors = new int[2];
741 st.mColors[0] = startColor;
742 st.mColors[1] = endColor;
745 } else if (name.equals("solid")) {
746 a = r.obtainAttributes(attrs,
747 com.android.internal.R.styleable.GradientDrawableSolid);
748 int argb = a.getColor(
749 com.android.internal.R.styleable.GradientDrawableSolid_color, 0);
752 } else if (name.equals("stroke")) {
753 a = r.obtainAttributes(attrs,
754 com.android.internal.R.styleable.GradientDrawableStroke);
755 int width = a.getDimensionPixelSize(
756 com.android.internal.R.styleable.GradientDrawableStroke_width, 0);
757 int color = a.getColor(
758 com.android.internal.R.styleable.GradientDrawableStroke_color, 0);
759 float dashWidth = a.getDimension(
760 com.android.internal.R.styleable.GradientDrawableStroke_dashWidth, 0);
761 if (dashWidth != 0.0f) {
762 float dashGap = a.getDimension(
763 com.android.internal.R.styleable.GradientDrawableStroke_dashGap, 0);
764 setStroke(width, color, dashWidth, dashGap);
766 setStroke(width, color);
769 } else if (name.equals("corners")) {
770 a = r.obtainAttributes(attrs,
771 com.android.internal.R.styleable.DrawableCorners);
772 int radius = a.getDimensionPixelSize(
773 com.android.internal.R.styleable.DrawableCorners_radius, 0);
774 setCornerRadius(radius);
775 int topLeftRadius = a.getDimensionPixelSize(
776 com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius);
777 int topRightRadius = a.getDimensionPixelSize(
778 com.android.internal.R.styleable.DrawableCorners_topRightRadius, radius);
779 int bottomLeftRadius = a.getDimensionPixelSize(
780 com.android.internal.R.styleable.DrawableCorners_bottomLeftRadius, radius);
781 int bottomRightRadius = a.getDimensionPixelSize(
782 com.android.internal.R.styleable.DrawableCorners_bottomRightRadius, radius);
783 if (topLeftRadius != radius || topRightRadius != radius ||
784 bottomLeftRadius != radius || bottomRightRadius != radius) {
785 setCornerRadii(new float[] {
786 topLeftRadius, topLeftRadius,
787 topRightRadius, topRightRadius,
788 bottomLeftRadius, bottomLeftRadius,
789 bottomRightRadius, bottomRightRadius
793 } else if (name.equals("padding")) {
794 a = r.obtainAttributes(attrs,
795 com.android.internal.R.styleable.GradientDrawablePadding);
797 a.getDimensionPixelOffset(
798 com.android.internal.R.styleable.GradientDrawablePadding_left, 0),
799 a.getDimensionPixelOffset(
800 com.android.internal.R.styleable.GradientDrawablePadding_top, 0),
801 a.getDimensionPixelOffset(
802 com.android.internal.R.styleable.GradientDrawablePadding_right, 0),
803 a.getDimensionPixelOffset(
804 com.android.internal.R.styleable.GradientDrawablePadding_bottom, 0));
806 mGradientState.mPadding = mPadding;
808 Log.w("drawable", "Bad element under <shape>: " + name);
813 private static float getFloatOrFraction(TypedArray a, int index, float defaultValue) {
814 TypedValue tv = a.peekValue(index);
815 float v = defaultValue;
817 boolean vIsFraction = tv.type == TypedValue.TYPE_FRACTION;
818 v = vIsFraction ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
824 public int getIntrinsicWidth() {
825 return mGradientState.mWidth;
829 public int getIntrinsicHeight() {
830 return mGradientState.mHeight;
834 public ConstantState getConstantState() {
835 mGradientState.mChangingConfigurations = super.getChangingConfigurations();
836 return mGradientState;
840 public Drawable mutate() {
841 if (!mMutated && super.mutate() == this) {
842 mGradientState = new GradientState(mGradientState);
843 initializeWithState(mGradientState);
849 final static class GradientState extends ConstantState {
850 public int mChangingConfigurations;
851 public int mShape = RECTANGLE;
852 public int mGradient = LINEAR_GRADIENT;
853 public Orientation mOrientation;
854 public int[] mColors;
855 public int[] mTempColors; // no need to copy
856 public float[] mTempPositions; // no need to copy
857 public float[] mPositions;
858 public boolean mHasSolidColor;
859 public int mSolidColor;
860 public int mStrokeWidth = -1; // if >= 0 use stroking.
861 public int mStrokeColor;
862 public float mStrokeDashWidth;
863 public float mStrokeDashGap;
864 public float mRadius; // use this if mRadiusArray is null
865 public float[] mRadiusArray;
866 public Rect mPadding;
867 public int mWidth = -1;
868 public int mHeight = -1;
869 public float mInnerRadiusRatio;
870 public float mThicknessRatio;
871 public int mInnerRadius;
872 public int mThickness;
873 private float mCenterX = 0.5f;
874 private float mCenterY = 0.5f;
875 private float mGradientRadius = 0.5f;
876 private boolean mUseLevel;
877 private boolean mUseLevelForShape;
881 mOrientation = Orientation.TOP_BOTTOM;
884 GradientState(Orientation orientation, int[] colors) {
885 mOrientation = orientation;
889 public GradientState(GradientState state) {
890 mChangingConfigurations = state.mChangingConfigurations;
891 mShape = state.mShape;
892 mGradient = state.mGradient;
893 mOrientation = state.mOrientation;
894 if (state.mColors != null) {
895 mColors = state.mColors.clone();
897 if (state.mPositions != null) {
898 mPositions = state.mPositions.clone();
900 mHasSolidColor = state.mHasSolidColor;
901 mStrokeWidth = state.mStrokeWidth;
902 mStrokeColor = state.mStrokeColor;
903 mStrokeDashWidth = state.mStrokeDashWidth;
904 mStrokeDashGap = state.mStrokeDashGap;
905 mRadius = state.mRadius;
906 if (state.mRadiusArray != null) {
907 mRadiusArray = state.mRadiusArray.clone();
909 if (state.mPadding != null) {
910 mPadding = new Rect(state.mPadding);
912 mWidth = state.mWidth;
913 mHeight = state.mHeight;
914 mInnerRadiusRatio = state.mInnerRadiusRatio;
915 mThicknessRatio = state.mThicknessRatio;
916 mInnerRadius = state.mInnerRadius;
917 mThickness = state.mThickness;
918 mCenterX = state.mCenterX;
919 mCenterY = state.mCenterY;
920 mGradientRadius = state.mGradientRadius;
921 mUseLevel = state.mUseLevel;
922 mUseLevelForShape = state.mUseLevelForShape;
926 public Drawable newDrawable() {
927 return new GradientDrawable(this);
931 public Drawable newDrawable(Resources res) {
932 return new GradientDrawable(this);
936 public int getChangingConfigurations() {
937 return mChangingConfigurations;
940 public void setShape(int shape) {
944 public void setGradientType(int gradient) {
945 mGradient = gradient;
948 public void setGradientCenter(float x, float y) {
953 public void setSolidColor(int argb) {
954 mHasSolidColor = true;
959 public void setStroke(int width, int color) {
960 mStrokeWidth = width;
961 mStrokeColor = color;
964 public void setStroke(int width, int color, float dashWidth, float dashGap) {
965 mStrokeWidth = width;
966 mStrokeColor = color;
967 mStrokeDashWidth = dashWidth;
968 mStrokeDashGap = dashGap;
971 public void setCornerRadius(float radius) {
979 public void setCornerRadii(float[] radii) {
980 mRadiusArray = radii;
986 public void setSize(int width, int height) {
991 public void setGradientRadius(float gradientRadius) {
992 mGradientRadius = gradientRadius;
996 private GradientDrawable(GradientState state) {
997 mGradientState = state;
998 initializeWithState(state);
1002 private void initializeWithState(GradientState state) {
1003 if (state.mHasSolidColor) {
1004 mFillPaint.setColor(state.mSolidColor);
1006 mPadding = state.mPadding;
1007 if (state.mStrokeWidth >= 0) {
1008 mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
1009 mStrokePaint.setStyle(Paint.Style.STROKE);
1010 mStrokePaint.setStrokeWidth(state.mStrokeWidth);
1011 mStrokePaint.setColor(state.mStrokeColor);
1013 if (state.mStrokeDashWidth != 0.0f) {
1014 DashPathEffect e = new DashPathEffect(
1015 new float[] { state.mStrokeDashWidth, state.mStrokeDashGap }, 0);
1016 mStrokePaint.setPathEffect(e);