You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
1351 lines
40 KiB
Java
1351 lines
40 KiB
Java
/*
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.graphics.drawable;
|
|
|
|
import android.content.res.ColorStateList;
|
|
import android.content.res.Resources;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.ColorFilter;
|
|
import android.graphics.DashPathEffect;
|
|
import android.graphics.LinearGradient;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Path;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.PorterDuffColorFilter;
|
|
import android.graphics.RadialGradient;
|
|
import android.graphics.Rect;
|
|
import android.graphics.RectF;
|
|
import android.graphics.Shader;
|
|
//import android.graphics.SweepGradient;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.util.TypedValue;
|
|
|
|
import com.android.internal.R;
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
|
|
import java.io.IOException;
|
|
|
|
/**
|
|
* A Drawable with a color gradient for buttons, backgrounds, etc.
|
|
*
|
|
* <p>It can be defined in an XML file with the <code><shape></code> element. For more
|
|
* information, see the guide to <a
|
|
* href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
|
|
*
|
|
* @attr ref android.R.styleable#GradientDrawable_visible
|
|
* @attr ref android.R.styleable#GradientDrawable_shape
|
|
* @attr ref android.R.styleable#GradientDrawable_innerRadiusRatio
|
|
* @attr ref android.R.styleable#GradientDrawable_innerRadius
|
|
* @attr ref android.R.styleable#GradientDrawable_thicknessRatio
|
|
* @attr ref android.R.styleable#GradientDrawable_thickness
|
|
* @attr ref android.R.styleable#GradientDrawable_useLevel
|
|
* @attr ref android.R.styleable#GradientDrawableSize_width
|
|
* @attr ref android.R.styleable#GradientDrawableSize_height
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_startColor
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_centerColor
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_endColor
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_useLevel
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_angle
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_type
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_centerX
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_centerY
|
|
* @attr ref android.R.styleable#GradientDrawableGradient_gradientRadius
|
|
* @attr ref android.R.styleable#GradientDrawableSolid_color
|
|
* @attr ref android.R.styleable#GradientDrawableStroke_width
|
|
* @attr ref android.R.styleable#GradientDrawableStroke_color
|
|
* @attr ref android.R.styleable#GradientDrawableStroke_dashWidth
|
|
* @attr ref android.R.styleable#GradientDrawableStroke_dashGap
|
|
* @attr ref android.R.styleable#GradientDrawablePadding_left
|
|
* @attr ref android.R.styleable#GradientDrawablePadding_top
|
|
* @attr ref android.R.styleable#GradientDrawablePadding_right
|
|
* @attr ref android.R.styleable#GradientDrawablePadding_bottom
|
|
*/
|
|
public class GradientDrawable extends Drawable {
|
|
/**
|
|
* Shape is a rectangle, possibly with rounded corners
|
|
*/
|
|
public static final int RECTANGLE = 0;
|
|
|
|
/**
|
|
* Shape is an ellipse
|
|
*/
|
|
public static final int OVAL = 1;
|
|
|
|
/**
|
|
* Shape is a line
|
|
*/
|
|
public static final int LINE = 2;
|
|
|
|
/**
|
|
* Shape is a ring.
|
|
*/
|
|
public static final int RING = 3;
|
|
|
|
/**
|
|
* Gradient is linear (default.)
|
|
*/
|
|
public static final int LINEAR_GRADIENT = 0;
|
|
|
|
/**
|
|
* Gradient is circular.
|
|
*/
|
|
public static final int RADIAL_GRADIENT = 1;
|
|
|
|
/**
|
|
* Gradient is a sweep.
|
|
*/
|
|
public static final int SWEEP_GRADIENT = 2;
|
|
|
|
private GradientState mGradientState;
|
|
|
|
private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
private Rect mPadding;
|
|
private Paint mStrokePaint; // optional, set by the caller
|
|
private ColorFilter mColorFilter; // optional, set by the caller
|
|
private PorterDuffColorFilter mTintFilter;
|
|
private int mAlpha = 0xFF; // modified by the caller
|
|
private boolean mDither;
|
|
|
|
private final Path mPath = new Path();
|
|
private final RectF mRect = new RectF();
|
|
|
|
private Paint mLayerPaint; // internal, used if we use saveLayer()
|
|
private boolean mRectIsDirty; // internal state
|
|
private boolean mMutated;
|
|
private Path mRingPath;
|
|
private boolean mPathIsDirty = true;
|
|
|
|
/**
|
|
* Controls how the gradient is oriented relative to the drawable's bounds
|
|
*/
|
|
public enum Orientation {
|
|
/**
|
|
draw the gradient from the top to the bottom
|
|
*/
|
|
TOP_BOTTOM,
|
|
/**
|
|
draw the gradient from the top-right to the bottom-left
|
|
*/
|
|
TR_BL,
|
|
/**
|
|
draw the gradient from the right to the left
|
|
*/
|
|
RIGHT_LEFT,
|
|
/**
|
|
draw the gradient from the bottom-right to the top-left
|
|
*/
|
|
BR_TL,
|
|
/**
|
|
draw the gradient from the bottom to the top
|
|
*/
|
|
BOTTOM_TOP,
|
|
/**
|
|
draw the gradient from the bottom-left to the top-right
|
|
*/
|
|
BL_TR,
|
|
/**
|
|
draw the gradient from the left to the right
|
|
*/
|
|
LEFT_RIGHT,
|
|
/**
|
|
draw the gradient from the top-left to the bottom-right
|
|
*/
|
|
TL_BR,
|
|
}
|
|
|
|
public GradientDrawable() {
|
|
this(new GradientState(Orientation.TOP_BOTTOM, null));
|
|
}
|
|
|
|
/**
|
|
* Create a new gradient drawable given an orientation and an array
|
|
* of colors for the gradient.
|
|
*/
|
|
public GradientDrawable(Orientation orientation, int[] colors) {
|
|
this(new GradientState(orientation, colors));
|
|
}
|
|
|
|
@Override
|
|
public boolean getPadding(Rect padding) {
|
|
if (mPadding != null) {
|
|
padding.set(mPadding);
|
|
return true;
|
|
} else {
|
|
return super.getPadding(padding);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* <p>Specify radii for each of the 4 corners. For each corner, the array
|
|
* contains 2 values, <code>[X_radius, Y_radius]</code>. The corners are ordered
|
|
* top-left, top-right, bottom-right, bottom-left. This property
|
|
* is honored only when the shape is of type {@link #RECTANGLE}.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param radii 4 pairs of X and Y radius for each corner, specified in pixels.
|
|
* The length of this array must be >= 8
|
|
*
|
|
* @see #mutate()
|
|
* @see #setCornerRadii(float[])
|
|
* @see #setShape(int)
|
|
*/
|
|
public void setCornerRadii(float[] radii) {
|
|
mGradientState.setCornerRadii(radii);
|
|
mPathIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Specify radius for the corners of the gradient. If this is > 0, then the
|
|
* drawable is drawn in a round-rectangle, rather than a rectangle. This property
|
|
* is honored only when the shape is of type {@link #RECTANGLE}.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param radius The radius in pixels of the corners of the rectangle shape
|
|
*
|
|
* @see #mutate()
|
|
* @see #setCornerRadii(float[])
|
|
* @see #setShape(int)
|
|
*/
|
|
public void setCornerRadius(float radius) {
|
|
mGradientState.setCornerRadius(radius);
|
|
mPathIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Set the stroke width and color for the drawable. If width is zero,
|
|
* then no stroke is drawn.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param width The width in pixels of the stroke
|
|
* @param color The color of the stroke
|
|
*
|
|
* @see #mutate()
|
|
* @see #setStroke(int, int, float, float)
|
|
*/
|
|
public void setStroke(int width, int color) {
|
|
setStroke(width, color, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* <p>Set the stroke width and color for the drawable. If width is zero,
|
|
* then no stroke is drawn. This method can also be used to dash the stroke.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param width The width in pixels of the stroke
|
|
* @param color The color of the stroke
|
|
* @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes
|
|
* @param dashGap The gap in pixels between dashes
|
|
*
|
|
* @see #mutate()
|
|
* @see #setStroke(int, int)
|
|
*/
|
|
public void setStroke(int width, int color, float dashWidth, float dashGap) {
|
|
mGradientState.setStroke(width, color, dashWidth, dashGap);
|
|
|
|
if (mStrokePaint == null) {
|
|
mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
mStrokePaint.setStyle(Paint.Style.STROKE);
|
|
}
|
|
mStrokePaint.setStrokeWidth(width);
|
|
mStrokePaint.setColor(color);
|
|
|
|
DashPathEffect e = null;
|
|
if (dashWidth > 0) {
|
|
e = new DashPathEffect(new float[] {dashWidth, dashGap}, 0);
|
|
}
|
|
mStrokePaint.setPathEffect(e);
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Sets the size of the shape drawn by this drawable.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param width The width of the shape used by this drawable
|
|
* @param height The height of the shape used by this drawable
|
|
*
|
|
* @see #mutate()
|
|
* @see #setGradientType(int)
|
|
*/
|
|
public void setSize(int width, int height) {
|
|
mGradientState.setSize(width, height);
|
|
mPathIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Sets the type of shape used to draw the gradient.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param shape The desired shape for this drawable: {@link #LINE},
|
|
* {@link #OVAL}, {@link #RECTANGLE} or {@link #RING}
|
|
*
|
|
* @see #mutate()
|
|
*/
|
|
public void setShape(int shape) {
|
|
mRingPath = null;
|
|
mPathIsDirty = true;
|
|
mGradientState.setShape(shape);
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Sets the type of gradient used by this drawable..</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param gradient The type of the gradient: {@link #LINEAR_GRADIENT},
|
|
* {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT}
|
|
*
|
|
* @see #mutate()
|
|
*/
|
|
public void setGradientType(int gradient) {
|
|
mGradientState.setGradientType(gradient);
|
|
mRectIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Sets the center location of the gradient. The radius is honored only when
|
|
* the gradient type is set to {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT}.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param x The x coordinate of the gradient's center
|
|
* @param y The y coordinate of the gradient's center
|
|
*
|
|
* @see #mutate()
|
|
* @see #setGradientType(int)
|
|
*/
|
|
public void setGradientCenter(float x, float y) {
|
|
mGradientState.setGradientCenter(x, y);
|
|
mRectIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Sets the radius of the gradient. The radius is honored only when the
|
|
* gradient type is set to {@link #RADIAL_GRADIENT}.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param gradientRadius The radius of the gradient in pixels
|
|
*
|
|
* @see #mutate()
|
|
* @see #setGradientType(int)
|
|
*/
|
|
public void setGradientRadius(float gradientRadius) {
|
|
mGradientState.setGradientRadius(gradientRadius);
|
|
mRectIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Sets whether or not this drawable will honor its <code>level</code>
|
|
* property.</p>
|
|
* <p><strong>Note</strong>: changing this property will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing this property.</p>
|
|
*
|
|
* @param useLevel True if this drawable should honor its level, false otherwise
|
|
*
|
|
* @see #mutate()
|
|
* @see #setLevel(int)
|
|
* @see #getLevel()
|
|
*/
|
|
public void setUseLevel(boolean useLevel) {
|
|
mGradientState.mUseLevel = useLevel;
|
|
mRectIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
private int modulateAlpha(int alpha) {
|
|
int scale = mAlpha + (mAlpha >> 7);
|
|
return alpha * scale >> 8;
|
|
}
|
|
|
|
/**
|
|
* Returns the orientation of the gradient defined in this drawable.
|
|
*/
|
|
public Orientation getOrientation() {
|
|
return mGradientState.mOrientation;
|
|
}
|
|
|
|
/**
|
|
* <p>Changes the orientation of the gradient defined in this drawable.</p>
|
|
* <p><strong>Note</strong>: changing orientation will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing the orientation.</p>
|
|
*
|
|
* @param orientation The desired orientation (angle) of the gradient
|
|
*
|
|
* @see #mutate()
|
|
*/
|
|
public void setOrientation(Orientation orientation) {
|
|
mGradientState.mOrientation = orientation;
|
|
mRectIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
/**
|
|
* <p>Sets the colors used to draw the gradient. Each color is specified as an
|
|
* ARGB integer and the array must contain at least 2 colors.</p>
|
|
* <p><strong>Note</strong>: changing orientation will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing the orientation.</p>
|
|
*
|
|
* @param colors 2 or more ARGB colors
|
|
*
|
|
* @see #mutate()
|
|
* @see #setColor(int)
|
|
*/
|
|
public void setColors(int[] colors) {
|
|
mGradientState.setColors(colors);
|
|
mRectIsDirty = true;
|
|
invalidateSelf();
|
|
}
|
|
|
|
@Override
|
|
public void draw(Canvas canvas) {
|
|
if (!ensureValidRect()) {
|
|
// nothing to draw
|
|
return;
|
|
}
|
|
|
|
// remember the alpha values, in case we temporarily overwrite them
|
|
// when we modulate them with mAlpha
|
|
final int prevFillAlpha = mFillPaint.getAlpha();
|
|
final int prevStrokeAlpha = mStrokePaint != null ? mStrokePaint.getAlpha() : 0;
|
|
// compute the modulate alpha values
|
|
final int currFillAlpha = modulateAlpha(prevFillAlpha);
|
|
final int currStrokeAlpha = modulateAlpha(prevStrokeAlpha);
|
|
|
|
final boolean haveStroke = currStrokeAlpha > 0 && mStrokePaint != null &&
|
|
mStrokePaint.getStrokeWidth() > 0;
|
|
final boolean haveFill = currFillAlpha > 0;
|
|
final GradientState st = mGradientState;
|
|
final ColorFilter colorFilter = mColorFilter != null ? mColorFilter : mTintFilter;
|
|
System.out.println("draw(): colorFilter: "+colorFilter);
|
|
/* we need a layer iff we're drawing both a fill and stroke, and the
|
|
stroke is non-opaque, and our shapetype actually supports
|
|
fill+stroke. Otherwise we can just draw the stroke (if any) on top
|
|
of the fill (if any) without worrying about blending artifacts.
|
|
*/
|
|
final boolean useLayer = haveStroke && haveFill && st.mShape != LINE &&
|
|
currStrokeAlpha < 255 && (mAlpha < 255 || colorFilter != null);
|
|
|
|
/* Drawing with a layer is slower than direct drawing, but it
|
|
allows us to apply paint effects like alpha and colorfilter to
|
|
the result of multiple separate draws. In our case, if the user
|
|
asks for a non-opaque alpha value (via setAlpha), and we're
|
|
stroking, then we need to apply the alpha AFTER we've drawn
|
|
both the fill and the stroke.
|
|
*/
|
|
/*if (useLayer) {
|
|
if (mLayerPaint == null) {
|
|
mLayerPaint = new Paint();
|
|
}
|
|
mLayerPaint.setDither(mDither);
|
|
mLayerPaint.setAlpha(mAlpha);
|
|
mLayerPaint.setColorFilter(colorFilter);
|
|
|
|
float rad = mStrokePaint.getStrokeWidth();
|
|
canvas.saveLayer(mRect.left - rad, mRect.top - rad,
|
|
mRect.right + rad, mRect.bottom + rad,
|
|
mLayerPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
|
|
|
|
// don't perform the filter in our individual paints
|
|
// since the layer will do it for us
|
|
mFillPaint.setColorFilter(null);
|
|
mStrokePaint.setColorFilter(null);
|
|
} else {*/
|
|
/* if we're not using a layer, apply the dither/filter to our
|
|
individual paints
|
|
*/
|
|
mFillPaint.setAlpha(currFillAlpha);
|
|
mFillPaint.setDither(mDither);
|
|
mFillPaint.setColorFilter(colorFilter);
|
|
if (colorFilter != null && !mGradientState.mHasSolidColor) {
|
|
mFillPaint.setColor(mAlpha << 24);
|
|
}
|
|
if (haveStroke) {
|
|
mStrokePaint.setAlpha(currStrokeAlpha);
|
|
mStrokePaint.setDither(mDither);
|
|
mStrokePaint.setColorFilter(colorFilter);
|
|
}
|
|
/*}*/
|
|
|
|
switch (st.mShape) {
|
|
case RECTANGLE:
|
|
if (st.mRadiusArray != null) {
|
|
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) {
|
|
// 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 || 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();
|
|
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;
|
|
}
|
|
|
|
if (useLayer) {
|
|
canvas.restore();
|
|
} else {
|
|
mFillPaint.setAlpha(prevFillAlpha);
|
|
if (haveStroke) {
|
|
mStrokePaint.setAlpha(prevStrokeAlpha);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
RectF bounds = new RectF(mRect);
|
|
|
|
float x = bounds.width() / 2.0f;
|
|
float y = bounds.height() / 2.0f;
|
|
|
|
float thickness = st.mThickness != -1 ? st.mThickness : bounds.width() / st.mThicknessRatio;
|
|
// inner radius
|
|
float radius = st.mInnerRadius != -1 ? st.mInnerRadius : bounds.width() / st.mInnerRadiusRatio;
|
|
|
|
RectF innerBounds = new RectF(bounds);
|
|
innerBounds.inset(x - radius, y - radius);
|
|
|
|
bounds = new RectF(innerBounds);
|
|
bounds.inset(-thickness, -thickness);
|
|
|
|
if (mRingPath == null) {
|
|
mRingPath = new Path();
|
|
} else {
|
|
mRingPath.reset();
|
|
}
|
|
|
|
final Path ringPath = mRingPath;
|
|
// arcTo treats the sweep angle mod 360, so check for that, since we
|
|
// think 360 means draw the entire oval
|
|
if (sweep < 360 && sweep > -360) {
|
|
ringPath.setFillType(Path.FillType.EVEN_ODD);
|
|
// inner top
|
|
ringPath.moveTo(x + radius, y);
|
|
// outer top
|
|
ringPath.lineTo(x + radius + thickness, y);
|
|
// outer arc
|
|
ringPath.arcTo(bounds, 0.0f, sweep, false);
|
|
// inner arc
|
|
ringPath.arcTo(innerBounds, sweep, -sweep, false);
|
|
ringPath.close();
|
|
} else {
|
|
// add the entire ovals
|
|
ringPath.addOval(bounds, Path.Direction.CW);
|
|
ringPath.addOval(innerBounds, Path.Direction.CCW);
|
|
}
|
|
|
|
return ringPath;
|
|
}
|
|
|
|
/**
|
|
* <p>Changes this drawbale to use a single color instead of a gradient.</p>
|
|
* <p><strong>Note</strong>: changing color will affect all instances
|
|
* of a drawable loaded from a resource. It is recommended to invoke
|
|
* {@link #mutate()} before changing the color.</p>
|
|
*
|
|
* @param argb The color used to fill the shape
|
|
*
|
|
* @see #mutate()
|
|
* @see #setColors(int[])
|
|
*/
|
|
public void setColor(int argb) {
|
|
mGradientState.setSolidColor(argb);
|
|
mFillPaint.setColor(argb);
|
|
invalidateSelf();
|
|
}
|
|
|
|
@Override
|
|
public int getChangingConfigurations() {
|
|
return super.getChangingConfigurations() | mGradientState.mChangingConfigurations;
|
|
}
|
|
|
|
@Override
|
|
public void setAlpha(int alpha) {
|
|
if (alpha != mAlpha) {
|
|
mAlpha = alpha;
|
|
invalidateSelf();
|
|
}
|
|
}
|
|
|
|
//@Override
|
|
public int getAlpha() {
|
|
return mAlpha;
|
|
}
|
|
|
|
//@Override
|
|
public void setDither(boolean dither) {
|
|
if (dither != mDither) {
|
|
mDither = dither;
|
|
invalidateSelf();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setColorFilter(ColorFilter cf) {
|
|
if (cf != mColorFilter) {
|
|
mColorFilter = cf;
|
|
invalidateSelf();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setTintList(ColorStateList tint) {
|
|
mGradientState.mTint = tint;
|
|
mTintFilter = updateTintFilter(mTintFilter, tint, mGradientState.mTintMode);
|
|
invalidateSelf();
|
|
}
|
|
|
|
@Override
|
|
public void setTintMode(PorterDuff.Mode tintMode) {
|
|
mGradientState.mTintMode = tintMode;
|
|
mTintFilter = updateTintFilter(mTintFilter, mGradientState.mTint, tintMode);
|
|
invalidateSelf();
|
|
}
|
|
|
|
//@Override
|
|
public int getOpacity() {
|
|
return mGradientState.mOpaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
|
|
}
|
|
|
|
@Override
|
|
protected void onBoundsChange(Rect r) {
|
|
super.onBoundsChange(r);
|
|
mRingPath = null;
|
|
mPathIsDirty = true;
|
|
mRectIsDirty = true;
|
|
}
|
|
|
|
//@Override
|
|
protected boolean onLevelChange(int level) {
|
|
//super.onLevelChange(level);
|
|
mRectIsDirty = true;
|
|
mPathIsDirty = true;
|
|
invalidateSelf();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* This checks mRectIsDirty, and if it is true, recomputes both our drawing
|
|
* rectangle (mRect) and the gradient itself, since it depends on our
|
|
* rectangle too.
|
|
* @return true if the resulting rectangle is not empty, false otherwise
|
|
*/
|
|
private boolean ensureValidRect() {
|
|
if (mRectIsDirty) {
|
|
mRectIsDirty = 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);
|
|
|
|
final int[] colors = st.mColors;
|
|
if (colors != null) {
|
|
RectF r = mRect;
|
|
float x0, x1, y0, y1;
|
|
|
|
if (st.mGradient == LINEAR_GRADIENT) {
|
|
final float level = st.mUseLevel ? (float)getLevel() / 10000.0f : 1.0f;
|
|
switch (st.mOrientation) {
|
|
case TOP_BOTTOM:
|
|
x0 = r.left;
|
|
y0 = r.top;
|
|
x1 = x0;
|
|
y1 = level * r.bottom;
|
|
break;
|
|
case TR_BL:
|
|
x0 = r.right;
|
|
y0 = r.top;
|
|
x1 = level * r.left;
|
|
y1 = level * r.bottom;
|
|
break;
|
|
case RIGHT_LEFT:
|
|
x0 = r.right;
|
|
y0 = r.top;
|
|
x1 = level * r.left;
|
|
y1 = y0;
|
|
break;
|
|
case BR_TL:
|
|
x0 = r.right;
|
|
y0 = r.bottom;
|
|
x1 = level * r.left;
|
|
y1 = level * r.top;
|
|
break;
|
|
case BOTTOM_TOP:
|
|
x0 = r.left;
|
|
y0 = r.bottom;
|
|
x1 = x0;
|
|
y1 = level * r.top;
|
|
break;
|
|
case BL_TR:
|
|
x0 = r.left;
|
|
y0 = r.bottom;
|
|
x1 = level * r.right;
|
|
y1 = level * r.top;
|
|
break;
|
|
case LEFT_RIGHT:
|
|
x0 = r.left;
|
|
y0 = r.top;
|
|
x1 = level * r.right;
|
|
y1 = y0;
|
|
break;
|
|
default: /* TL_BR */
|
|
x0 = r.left;
|
|
y0 = r.top;
|
|
x1 = level * r.right;
|
|
y1 = level * r.bottom;
|
|
break;
|
|
}
|
|
|
|
mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1,
|
|
colors, st.mPositions, Shader.TileMode.CLAMP));
|
|
} else if (st.mGradient == RADIAL_GRADIENT) {
|
|
x0 = r.left + (r.right - r.left) * st.mCenterX;
|
|
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
|
|
|
|
final float level = st.mUseLevel ? (float)getLevel() / 10000.0f : 1.0f;
|
|
|
|
mFillPaint.setShader(new RadialGradient(x0, y0,
|
|
level * st.mGradientRadius, colors, null,
|
|
Shader.TileMode.CLAMP));
|
|
} else if (st.mGradient == SWEEP_GRADIENT) {
|
|
/*x0 = r.left + (r.right - r.left) * st.mCenterX;
|
|
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
|
|
|
|
int[] tempColors = colors;
|
|
float[] tempPositions = null;
|
|
|
|
if (st.mUseLevel) {
|
|
tempColors = st.mTempColors;
|
|
final int length = colors.length;
|
|
if (tempColors == null || tempColors.length != length + 1) {
|
|
tempColors = st.mTempColors = new int[length + 1];
|
|
}
|
|
System.arraycopy(colors, 0, tempColors, 0, length);
|
|
tempColors[length] = colors[length - 1];
|
|
|
|
tempPositions = st.mTempPositions;
|
|
final float fraction = 1.0f / (float)(length - 1);
|
|
if (tempPositions == null || tempPositions.length != length + 1) {
|
|
tempPositions = st.mTempPositions = new float[length + 1];
|
|
}
|
|
|
|
final float level = (float)getLevel() / 10000.0f;
|
|
for (int i = 0; i < length; i++) {
|
|
tempPositions[i] = i * fraction * level;
|
|
}
|
|
tempPositions[length] = 1.0f;
|
|
}
|
|
mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions));*/
|
|
}
|
|
|
|
// If we don't have a solid color, the alpha channel must be
|
|
// maxed out so that alpha modulation works correctly.
|
|
if (!st.mHasSolidColor) {
|
|
mFillPaint.setColor(Color.BLACK);
|
|
}
|
|
}
|
|
}
|
|
return !mRect.isEmpty();
|
|
}
|
|
|
|
/* @Override
|
|
public void inflate(Resources r, XmlPullParser parser,
|
|
AttributeSet attrs)
|
|
throws XmlPullParserException, IOException {
|
|
|
|
final GradientState st = mGradientState;
|
|
|
|
TypedArray a = r.obtainAttributes(attrs,
|
|
com.android.internal.R.styleable.GradientDrawable);
|
|
|
|
super.inflateWithAttributes(r, parser, a,
|
|
com.android.internal.R.styleable.GradientDrawable_visible);
|
|
|
|
int shapeType = a.getInt(
|
|
com.android.internal.R.styleable.GradientDrawable_shape, RECTANGLE);
|
|
boolean dither = a.getBoolean(
|
|
com.android.internal.R.styleable.GradientDrawable_dither, false);
|
|
|
|
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);
|
|
}
|
|
|
|
a.recycle();
|
|
|
|
setShape(shapeType);
|
|
setDither(dither);
|
|
|
|
int type;
|
|
|
|
final int innerDepth = parser.getDepth() + 1;
|
|
int depth;
|
|
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
|
|
if (type != XmlPullParser.START_TAG) {
|
|
continue;
|
|
}
|
|
|
|
if (depth > innerDepth) {
|
|
continue;
|
|
}
|
|
|
|
String name = parser.getName();
|
|
|
|
if (name.equals("size")) {
|
|
a = r.obtainAttributes(attrs,
|
|
com.android.internal.R.styleable.GradientDrawableSize);
|
|
int width = a.getDimensionPixelSize(
|
|
com.android.internal.R.styleable.GradientDrawableSize_width, -1);
|
|
int height = a.getDimensionPixelSize(
|
|
com.android.internal.R.styleable.GradientDrawableSize_height, -1);
|
|
a.recycle();
|
|
setSize(width, height);
|
|
} else if (name.equals("gradient")) {
|
|
a = r.obtainAttributes(attrs,
|
|
com.android.internal.R.styleable.GradientDrawableGradient);
|
|
int startColor = a.getColor(
|
|
com.android.internal.R.styleable.GradientDrawableGradient_startColor, 0);
|
|
boolean hasCenterColor = a
|
|
.hasValue(com.android.internal.R.styleable.GradientDrawableGradient_centerColor);
|
|
int centerColor = a.getColor(
|
|
com.android.internal.R.styleable.GradientDrawableGradient_centerColor, 0);
|
|
int endColor = a.getColor(
|
|
com.android.internal.R.styleable.GradientDrawableGradient_endColor, 0);
|
|
int gradientType = a.getInt(
|
|
com.android.internal.R.styleable.GradientDrawableGradient_type,
|
|
LINEAR_GRADIENT);
|
|
|
|
st.mCenterX = getFloatOrFraction(
|
|
a,
|
|
com.android.internal.R.styleable.GradientDrawableGradient_centerX,
|
|
0.5f);
|
|
|
|
st.mCenterY = getFloatOrFraction(
|
|
a,
|
|
com.android.internal.R.styleable.GradientDrawableGradient_centerY,
|
|
0.5f);
|
|
|
|
st.mUseLevel = a.getBoolean(
|
|
com.android.internal.R.styleable.GradientDrawableGradient_useLevel, false);
|
|
st.mGradient = gradientType;
|
|
|
|
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;
|
|
}
|
|
} else {
|
|
TypedValue tv = a.peekValue(
|
|
com.android.internal.R.styleable.GradientDrawableGradient_gradientRadius);
|
|
if (tv != null) {
|
|
boolean radiusRel = tv.type == TypedValue.TYPE_FRACTION;
|
|
st.mGradientRadius = radiusRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
|
|
} else if (gradientType == RADIAL_GRADIENT) {
|
|
throw new XmlPullParserException(
|
|
a.getPositionDescription() + "<gradient> tag requires 'gradientRadius' "
|
|
+ "attribute with radial type");
|
|
}
|
|
}
|
|
|
|
a.recycle();
|
|
|
|
if (hasCenterColor) {
|
|
st.mColors = new int[3];
|
|
st.mColors[0] = startColor;
|
|
st.mColors[1] = centerColor;
|
|
st.mColors[2] = endColor;
|
|
|
|
st.mPositions = new float[3];
|
|
st.mPositions[0] = 0.0f;
|
|
// Since 0.5f is default value, try to take the one that isn't 0.5f
|
|
st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY;
|
|
st.mPositions[2] = 1f;
|
|
} else {
|
|
st.mColors = new int[2];
|
|
st.mColors[0] = startColor;
|
|
st.mColors[1] = endColor;
|
|
}
|
|
|
|
} 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);
|
|
} 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();
|
|
} 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();
|
|
} 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;
|
|
} else {
|
|
Log.w("drawable", "Bad element under <shape>: " + name);
|
|
}
|
|
}
|
|
|
|
mGradientState.computeOpacity();
|
|
}*/
|
|
|
|
private static float getFloatOrFraction(TypedArray a, int index, float defaultValue) {
|
|
TypedValue tv = a.peekValue(index);
|
|
float v = defaultValue;
|
|
if (tv != null) {
|
|
boolean vIsFraction = tv.type == TypedValue.TYPE_FRACTION;
|
|
v = vIsFraction ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
|
|
}
|
|
return v;
|
|
}
|
|
|
|
@Override
|
|
public int getIntrinsicWidth() {
|
|
return mGradientState.mWidth;
|
|
}
|
|
|
|
@Override
|
|
public int getIntrinsicHeight() {
|
|
return mGradientState.mHeight;
|
|
}
|
|
|
|
@Override
|
|
public ConstantState getConstantState() {
|
|
mGradientState.mChangingConfigurations = getChangingConfigurations();
|
|
return mGradientState;
|
|
}
|
|
|
|
@Override
|
|
public Drawable mutate() {
|
|
if (!mMutated && super.mutate() == this) {
|
|
mGradientState = new GradientState(mGradientState);
|
|
initializeWithState(mGradientState);
|
|
mMutated = true;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
final static class GradientState extends ConstantState {
|
|
public int mChangingConfigurations;
|
|
public int mShape = RECTANGLE;
|
|
public int mGradient = LINEAR_GRADIENT;
|
|
public Orientation mOrientation;
|
|
public int[] mColors;
|
|
public int[] mTempColors; // no need to copy
|
|
public float[] mTempPositions; // no need to copy
|
|
public float[] mPositions;
|
|
public boolean mHasSolidColor;
|
|
public int mSolidColor;
|
|
public int mStrokeWidth = -1; // if >= 0 use stroking.
|
|
public int mStrokeColor;
|
|
public float mStrokeDashWidth;
|
|
public float mStrokeDashGap;
|
|
public float mRadius; // use this if mRadiusArray is null
|
|
public float[] mRadiusArray;
|
|
public Rect mPadding;
|
|
public int mWidth = -1;
|
|
public int mHeight = -1;
|
|
public float mInnerRadiusRatio;
|
|
public float mThicknessRatio;
|
|
public int mInnerRadius;
|
|
public int mThickness;
|
|
private float mCenterX = 0.5f;
|
|
private float mCenterY = 0.5f;
|
|
private float mGradientRadius = 0.5f;
|
|
private boolean mUseLevel;
|
|
private boolean mUseLevelForShape;
|
|
private boolean mOpaque;
|
|
|
|
ColorStateList mTint = null;
|
|
PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
|
|
|
|
GradientState(Orientation orientation, int[] colors) {
|
|
mOrientation = orientation;
|
|
setColors(colors);
|
|
}
|
|
|
|
public GradientState(GradientState state) {
|
|
mChangingConfigurations = state.mChangingConfigurations;
|
|
mShape = state.mShape;
|
|
mGradient = state.mGradient;
|
|
mOrientation = state.mOrientation;
|
|
if (state.mColors != null) {
|
|
mColors = state.mColors.clone();
|
|
}
|
|
if (state.mPositions != null) {
|
|
mPositions = state.mPositions.clone();
|
|
}
|
|
mHasSolidColor = state.mHasSolidColor;
|
|
mSolidColor = state.mSolidColor;
|
|
mStrokeWidth = state.mStrokeWidth;
|
|
mStrokeColor = state.mStrokeColor;
|
|
mStrokeDashWidth = state.mStrokeDashWidth;
|
|
mStrokeDashGap = state.mStrokeDashGap;
|
|
mRadius = state.mRadius;
|
|
if (state.mRadiusArray != null) {
|
|
mRadiusArray = state.mRadiusArray.clone();
|
|
}
|
|
if (state.mPadding != null) {
|
|
mPadding = new Rect(state.mPadding);
|
|
}
|
|
mWidth = state.mWidth;
|
|
mHeight = state.mHeight;
|
|
mInnerRadiusRatio = state.mInnerRadiusRatio;
|
|
mThicknessRatio = state.mThicknessRatio;
|
|
mInnerRadius = state.mInnerRadius;
|
|
mThickness = state.mThickness;
|
|
mCenterX = state.mCenterX;
|
|
mCenterY = state.mCenterY;
|
|
mGradientRadius = state.mGradientRadius;
|
|
mUseLevel = state.mUseLevel;
|
|
mUseLevelForShape = state.mUseLevelForShape;
|
|
mOpaque = state.mOpaque;
|
|
|
|
mTint = state.mTint;
|
|
mTintMode = state.mTintMode;
|
|
}
|
|
|
|
@Override
|
|
public Drawable newDrawable() {
|
|
return new GradientDrawable(this);
|
|
}
|
|
|
|
@Override
|
|
public Drawable newDrawable(Resources res) {
|
|
return new GradientDrawable(this);
|
|
}
|
|
|
|
//@Override
|
|
public int getChangingConfigurations() {
|
|
return mChangingConfigurations;
|
|
}
|
|
|
|
public void setShape(int shape) {
|
|
mShape = shape;
|
|
computeOpacity();
|
|
}
|
|
|
|
public void setGradientType(int gradient) {
|
|
mGradient = gradient;
|
|
}
|
|
|
|
public void setGradientCenter(float x, float y) {
|
|
mCenterX = x;
|
|
mCenterY = y;
|
|
}
|
|
|
|
public void setColors(int[] colors) {
|
|
mHasSolidColor = false;
|
|
mColors = colors;
|
|
computeOpacity();
|
|
}
|
|
|
|
public void setSolidColor(int argb) {
|
|
mHasSolidColor = true;
|
|
mSolidColor = argb;
|
|
mColors = null;
|
|
computeOpacity();
|
|
}
|
|
|
|
private void computeOpacity() {
|
|
if (mShape != RECTANGLE) {
|
|
mOpaque = false;
|
|
return;
|
|
}
|
|
|
|
if (mRadius > 0 || mRadiusArray != null) {
|
|
mOpaque = false;
|
|
return;
|
|
}
|
|
|
|
if (mStrokeWidth > 0 && !isOpaque(mStrokeColor)) {
|
|
mOpaque = false;
|
|
return;
|
|
}
|
|
|
|
if (mHasSolidColor) {
|
|
mOpaque = isOpaque(mSolidColor);
|
|
return;
|
|
}
|
|
|
|
if (mColors != null) {
|
|
for (int i = 0; i < mColors.length; i++) {
|
|
if (!isOpaque(mColors[i])) {
|
|
mOpaque = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
mOpaque = true;
|
|
}
|
|
|
|
private static boolean isOpaque(int color) {
|
|
return ((color >> 24) & 0xff) == 0xff;
|
|
}
|
|
|
|
public void setStroke(int width, int color) {
|
|
mStrokeWidth = width;
|
|
mStrokeColor = color;
|
|
computeOpacity();
|
|
}
|
|
|
|
public void setStroke(int width, int color, float dashWidth, float dashGap) {
|
|
mStrokeWidth = width;
|
|
mStrokeColor = color;
|
|
mStrokeDashWidth = dashWidth;
|
|
mStrokeDashGap = dashGap;
|
|
computeOpacity();
|
|
}
|
|
|
|
public void setCornerRadius(float radius) {
|
|
if (radius < 0) {
|
|
radius = 0;
|
|
}
|
|
mRadius = radius;
|
|
mRadiusArray = null;
|
|
}
|
|
|
|
public void setCornerRadii(float[] radii) {
|
|
mRadiusArray = radii;
|
|
if (radii == null) {
|
|
mRadius = 0;
|
|
}
|
|
}
|
|
|
|
public void setSize(int width, int height) {
|
|
mWidth = width;
|
|
mHeight = height;
|
|
}
|
|
|
|
public void setGradientRadius(float gradientRadius) {
|
|
mGradientRadius = gradientRadius;
|
|
}
|
|
}
|
|
|
|
private GradientDrawable(GradientState state) {
|
|
mGradientState = state;
|
|
initializeWithState(state);
|
|
mRectIsDirty = true;
|
|
mMutated = false;
|
|
}
|
|
|
|
private void initializeWithState(GradientState state) {
|
|
if (state.mHasSolidColor) {
|
|
mFillPaint.setColor(state.mSolidColor);
|
|
} else if (state.mColors == null) {
|
|
// If we don't have a solid color and we don't have a gradient,
|
|
// the app is stroking the shape, set the color to the default
|
|
// value of state.mSolidColor
|
|
mFillPaint.setColor(0);
|
|
} else {
|
|
// Otherwise, make sure the fill alpha is maxed out.
|
|
mFillPaint.setColor(Color.BLACK);
|
|
}
|
|
mPadding = state.mPadding;
|
|
if (state.mStrokeWidth >= 0) {
|
|
mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
mStrokePaint.setStyle(Paint.Style.STROKE);
|
|
mStrokePaint.setStrokeWidth(state.mStrokeWidth);
|
|
mStrokePaint.setColor(state.mStrokeColor);
|
|
|
|
if (state.mStrokeDashWidth != 0.0f) {
|
|
DashPathEffect e = new DashPathEffect(
|
|
new float[] {state.mStrokeDashWidth, state.mStrokeDashGap}, 0);
|
|
mStrokePaint.setPathEffect(e);
|
|
}
|
|
}
|
|
}
|
|
}
|