Merge "Reduce number of saveLayer calls in RippleDrawable" into lmp-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b724314516
@@ -26,6 +26,7 @@ import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Xfermode;
|
||||
import android.util.MathUtils;
|
||||
import android.view.HardwareCanvas;
|
||||
import android.view.RenderNodeAnimator;
|
||||
@@ -58,8 +59,10 @@ class Ripple {
|
||||
/** Bounds used for computing max radius. */
|
||||
private final Rect mBounds;
|
||||
|
||||
/** Full-opacity color for drawing this ripple. */
|
||||
private int mColorOpaque;
|
||||
/** ARGB color for drawing this ripple. */
|
||||
private int mColor;
|
||||
|
||||
private Xfermode mXfermode;
|
||||
|
||||
/** Maximum ripple radius. */
|
||||
private float mOuterRadius;
|
||||
@@ -120,9 +123,7 @@ class Ripple {
|
||||
mStartingY = startingY;
|
||||
}
|
||||
|
||||
public void setup(int maxRadius, int color, float density) {
|
||||
mColorOpaque = color | 0xFF000000;
|
||||
|
||||
public void setup(int maxRadius, float density) {
|
||||
if (maxRadius != RippleDrawable.RADIUS_AUTO) {
|
||||
mHasMaxRadius = true;
|
||||
mOuterRadius = maxRadius;
|
||||
@@ -216,6 +217,10 @@ class Ripple {
|
||||
* Draws the ripple centered at (0,0) using the specified paint.
|
||||
*/
|
||||
public boolean draw(Canvas c, Paint p) {
|
||||
// Store the color and xfermode, we might need them later.
|
||||
mColor = p.getColor();
|
||||
mXfermode = p.getXfermode();
|
||||
|
||||
final boolean canUseHardware = c.isHardwareAccelerated();
|
||||
if (mCanUseHardware != canUseHardware && mCanUseHardware) {
|
||||
// We've switched from hardware to non-hardware mode. Panic.
|
||||
@@ -261,8 +266,8 @@ class Ripple {
|
||||
private boolean drawSoftware(Canvas c, Paint p) {
|
||||
boolean hasContent = false;
|
||||
|
||||
p.setColor(mColorOpaque);
|
||||
final int alpha = (int) (255 * mOpacity + 0.5f);
|
||||
final int paintAlpha = p.getAlpha();
|
||||
final int alpha = (int) (paintAlpha * mOpacity + 0.5f);
|
||||
final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
|
||||
if (alpha > 0 && radius > 0) {
|
||||
final float x = MathUtils.lerp(
|
||||
@@ -270,8 +275,8 @@ class Ripple {
|
||||
final float y = MathUtils.lerp(
|
||||
mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
|
||||
p.setAlpha(alpha);
|
||||
p.setStyle(Style.FILL);
|
||||
c.drawCircle(x, y, radius, p);
|
||||
p.setAlpha(paintAlpha);
|
||||
hasContent = true;
|
||||
}
|
||||
|
||||
@@ -374,8 +379,9 @@ class Ripple {
|
||||
final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
|
||||
final Paint paint = getTempPaint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(mColorOpaque);
|
||||
paint.setAlpha((int) (255 * mOpacity + 0.5f));
|
||||
paint.setColor(mColor);
|
||||
paint.setXfermode(mXfermode);
|
||||
paint.setAlpha((int) (Color.alpha(mColor) * mOpacity + 0.5f));
|
||||
paint.setStyle(Style.FILL);
|
||||
mPropPaint = CanvasProperty.createPaint(paint);
|
||||
mPropRadius = CanvasProperty.createFloat(startRadius);
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Xfermode;
|
||||
import android.util.MathUtils;
|
||||
import android.view.HardwareCanvas;
|
||||
import android.view.RenderNodeAnimator;
|
||||
@@ -60,11 +61,10 @@ class RippleBackground {
|
||||
/** Bounds used for computing max radius. */
|
||||
private final Rect mBounds;
|
||||
|
||||
/** Full-opacity color for drawing this ripple. */
|
||||
private int mColorOpaque;
|
||||
/** ARGB color for drawing this ripple. */
|
||||
private int mColor;
|
||||
|
||||
/** Maximum alpha value for drawing this ripple. */
|
||||
private int mColorAlpha;
|
||||
private Xfermode mXfermode;
|
||||
|
||||
/** Maximum ripple radius. */
|
||||
private float mOuterRadius;
|
||||
@@ -106,10 +106,7 @@ class RippleBackground {
|
||||
mBounds = bounds;
|
||||
}
|
||||
|
||||
public void setup(int maxRadius, int color, float density) {
|
||||
mColorOpaque = color | 0xFF000000;
|
||||
mColorAlpha = Color.alpha(color) / 2;
|
||||
|
||||
public void setup(int maxRadius, float density) {
|
||||
if (maxRadius != RippleDrawable.RADIUS_AUTO) {
|
||||
mHasMaxRadius = true;
|
||||
mOuterRadius = maxRadius;
|
||||
@@ -124,10 +121,6 @@ class RippleBackground {
|
||||
mDensity = density;
|
||||
}
|
||||
|
||||
public boolean isHardwareAnimating() {
|
||||
return mHardwareAnimating;
|
||||
}
|
||||
|
||||
public void onHotspotBoundsChanged() {
|
||||
if (!mHasMaxRadius) {
|
||||
final float halfWidth = mBounds.width() / 2.0f;
|
||||
@@ -151,6 +144,10 @@ class RippleBackground {
|
||||
* Draws the ripple centered at (0,0) using the specified paint.
|
||||
*/
|
||||
public boolean draw(Canvas c, Paint p) {
|
||||
// Store the color and xfermode, we might need them later.
|
||||
mColor = p.getColor();
|
||||
mXfermode = p.getXfermode();
|
||||
|
||||
final boolean canUseHardware = c.isHardwareAccelerated();
|
||||
if (mCanUseHardware != canUseHardware && mCanUseHardware) {
|
||||
// We've switched from hardware to non-hardware mode. Panic.
|
||||
@@ -169,8 +166,7 @@ class RippleBackground {
|
||||
}
|
||||
|
||||
public boolean shouldDraw() {
|
||||
final int outerAlpha = (int) (mColorAlpha * mOuterOpacity + 0.5f);
|
||||
return mCanUseHardware && mHardwareAnimating || outerAlpha > 0 && mOuterRadius > 0;
|
||||
return (mCanUseHardware && mHardwareAnimating) || (mOuterOpacity > 0 && mOuterRadius > 0);
|
||||
}
|
||||
|
||||
private boolean drawHardware(HardwareCanvas c) {
|
||||
@@ -201,12 +197,13 @@ class RippleBackground {
|
||||
private boolean drawSoftware(Canvas c, Paint p) {
|
||||
boolean hasContent = false;
|
||||
|
||||
p.setColor(mColorOpaque);
|
||||
final int outerAlpha = (int) (mColorAlpha * mOuterOpacity + 0.5f);
|
||||
if (outerAlpha > 0 && mOuterRadius > 0) {
|
||||
p.setAlpha(outerAlpha);
|
||||
p.setStyle(Style.FILL);
|
||||
c.drawCircle(mOuterX, mOuterY, mOuterRadius, p);
|
||||
final int paintAlpha = p.getAlpha();
|
||||
final int alpha = (int) (paintAlpha * mOuterOpacity + 0.5f);
|
||||
final float radius = mOuterRadius;
|
||||
if (alpha > 0 && radius > 0) {
|
||||
p.setAlpha(alpha);
|
||||
c.drawCircle(mOuterX, mOuterY, radius, p);
|
||||
p.setAlpha(paintAlpha);
|
||||
hasContent = true;
|
||||
}
|
||||
|
||||
@@ -262,7 +259,7 @@ class RippleBackground {
|
||||
// outer(t) = mOuterOpacity + t * WAVE_OUTER_OPACITY_VELOCITY / 1000
|
||||
final int inflectionDuration = Math.max(0, (int) (1000 * (1 - mOuterOpacity)
|
||||
/ (WAVE_OPACITY_DECAY_VELOCITY + outerOpacityVelocity) + 0.5f));
|
||||
final int inflectionOpacity = (int) (mColorAlpha * (mOuterOpacity
|
||||
final int inflectionOpacity = (int) (Color.alpha(mColor) * (mOuterOpacity
|
||||
+ inflectionDuration * outerOpacityVelocity * outerSizeInfluence / 1000) + 0.5f);
|
||||
|
||||
if (mCanUseHardware) {
|
||||
@@ -277,8 +274,9 @@ class RippleBackground {
|
||||
|
||||
final Paint outerPaint = getTempPaint();
|
||||
outerPaint.setAntiAlias(true);
|
||||
outerPaint.setColor(mColorOpaque);
|
||||
outerPaint.setAlpha((int) (mColorAlpha * mOuterOpacity + 0.5f));
|
||||
outerPaint.setXfermode(mXfermode);
|
||||
outerPaint.setColor(mColor);
|
||||
outerPaint.setAlpha((int) (Color.alpha(mColor) * mOuterOpacity + 0.5f));
|
||||
outerPaint.setStyle(Style.FILL);
|
||||
mPropOuterPaint = CanvasProperty.createPaint(outerPaint);
|
||||
mPropOuterRadius = CanvasProperty.createFloat(mOuterRadius);
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
package android.graphics.drawable;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.ColorStateList;
|
||||
@@ -34,11 +39,6 @@ import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -156,13 +156,6 @@ public class RippleDrawable extends LayerDrawable {
|
||||
/** Whether bounds are being overridden. */
|
||||
private boolean mOverrideBounds;
|
||||
|
||||
/**
|
||||
* Whether the next draw MUST draw something to canvas. Used to work around
|
||||
* a bug in hardware invalidation following a render thread-accelerated
|
||||
* animation.
|
||||
*/
|
||||
private boolean mNeedsDraw;
|
||||
|
||||
/**
|
||||
* Constructor used for drawable inflation.
|
||||
*/
|
||||
@@ -203,21 +196,15 @@ public class RippleDrawable extends LayerDrawable {
|
||||
public void jumpToCurrentState() {
|
||||
super.jumpToCurrentState();
|
||||
|
||||
boolean needsDraw = false;
|
||||
|
||||
if (mRipple != null) {
|
||||
needsDraw |= mRipple.isHardwareAnimating();
|
||||
mRipple.jump();
|
||||
}
|
||||
|
||||
if (mBackground != null) {
|
||||
needsDraw |= mBackground.isHardwareAnimating();
|
||||
mBackground.jump();
|
||||
}
|
||||
|
||||
needsDraw |= cancelExitingRipples();
|
||||
|
||||
mNeedsDraw = needsDraw;
|
||||
cancelExitingRipples();
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@@ -497,8 +484,7 @@ public class RippleDrawable extends LayerDrawable {
|
||||
mBackground = new RippleBackground(this, mHotspotBounds);
|
||||
}
|
||||
|
||||
final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
|
||||
mBackground.setup(mState.mMaxRadius, color, mDensity);
|
||||
mBackground.setup(mState.mMaxRadius, mDensity);
|
||||
mBackground.enter(focused);
|
||||
}
|
||||
|
||||
@@ -534,8 +520,7 @@ public class RippleDrawable extends LayerDrawable {
|
||||
mRipple = new Ripple(this, mHotspotBounds, x, y);
|
||||
}
|
||||
|
||||
final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
|
||||
mRipple.setup(mState.mMaxRadius, color, mDensity);
|
||||
mRipple.setup(mState.mMaxRadius, mDensity);
|
||||
mRipple.enter();
|
||||
}
|
||||
|
||||
@@ -559,23 +544,17 @@ public class RippleDrawable extends LayerDrawable {
|
||||
* background. Nothing will be drawn after this method is called.
|
||||
*/
|
||||
private void clearHotspots() {
|
||||
boolean needsDraw = false;
|
||||
|
||||
if (mRipple != null) {
|
||||
needsDraw |= mRipple.isHardwareAnimating();
|
||||
mRipple.cancel();
|
||||
mRipple = null;
|
||||
}
|
||||
|
||||
if (mBackground != null) {
|
||||
needsDraw |= mBackground.isHardwareAnimating();
|
||||
mBackground.cancel();
|
||||
mBackground = null;
|
||||
}
|
||||
|
||||
needsDraw |= cancelExitingRipples();
|
||||
|
||||
mNeedsDraw = needsDraw;
|
||||
cancelExitingRipples();
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@@ -631,56 +610,41 @@ public class RippleDrawable extends LayerDrawable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized for drawing ripples with a mask layer and optional content.
|
||||
*/
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
final boolean hasMask = mMask != null;
|
||||
final boolean drawNonMaskContent = mLayerState.mNum > (hasMask ? 1 : 0);
|
||||
final boolean drawMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
|
||||
final boolean hasRipples = mRipple != null || mExitingRipplesCount > 0
|
||||
|| (mBackground != null && mBackground.shouldDraw());
|
||||
|
||||
// Clip to the dirty bounds, which will be the drawable bounds if we
|
||||
// have a mask or content and the ripple bounds if we're projecting.
|
||||
final Rect bounds = getDirtyBounds();
|
||||
final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
|
||||
canvas.clipRect(bounds);
|
||||
|
||||
// If we have content, draw it into a layer first.
|
||||
final int contentLayer;
|
||||
if (drawNonMaskContent) {
|
||||
contentLayer = drawContentLayer(canvas, bounds, SRC_OVER);
|
||||
} else {
|
||||
contentLayer = -1;
|
||||
}
|
||||
// If we have content, draw it first. If we have ripples and no mask,
|
||||
// we'll draw it into a SRC_OVER layer so that we can mask ripples
|
||||
// against it using SRC_IN.
|
||||
final boolean hasContentLayer = drawContent(canvas, bounds, hasRipples, hasMask);
|
||||
|
||||
// Next, try to draw the ripples (into a layer if necessary). If we need
|
||||
// to mask against the underlying content, set the xfermode to SRC_ATOP.
|
||||
final PorterDuffXfermode xfermode = (hasMask || !drawNonMaskContent) ? SRC_OVER : SRC_ATOP;
|
||||
// Next, try to draw the ripples. If we have a non-opaque mask, we'll
|
||||
// draw the ripples into a SRC_OVER layer, draw the mask into a DST_IN
|
||||
// layer, and blend.
|
||||
if (hasRipples) {
|
||||
final boolean hasNonOpaqueMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
|
||||
final boolean hasRippleLayer = drawBackgroundAndRipples(canvas, bounds,
|
||||
hasNonOpaqueMask, hasContentLayer);
|
||||
|
||||
// If we have a background and a non-opaque mask, draw the masking layer.
|
||||
final int backgroundLayer = drawBackgroundLayer(canvas, bounds, xfermode, drawMask);
|
||||
if (backgroundLayer >= 0) {
|
||||
if (drawMask) {
|
||||
// If drawing ripples created a layer, we have a non-opaque mask
|
||||
// that needs to be blended on top of the ripples with DST_IN.
|
||||
if (hasRippleLayer) {
|
||||
drawMaskingLayer(canvas, bounds, DST_IN);
|
||||
}
|
||||
canvas.restoreToCount(backgroundLayer);
|
||||
}
|
||||
|
||||
// If we have ripples and a non-opaque mask, draw the masking layer.
|
||||
final int rippleLayer = drawRippleLayer(canvas, bounds, xfermode);
|
||||
if (rippleLayer >= 0) {
|
||||
if (drawMask) {
|
||||
drawMaskingLayer(canvas, bounds, DST_IN);
|
||||
}
|
||||
canvas.restoreToCount(rippleLayer);
|
||||
}
|
||||
|
||||
// If we failed to draw anything and we just canceled animations, at
|
||||
// least draw a color so that hardware invalidation works correctly.
|
||||
if (contentLayer < 0 && backgroundLayer < 0 && rippleLayer < 0 && mNeedsDraw) {
|
||||
canvas.drawColor(Color.TRANSPARENT);
|
||||
|
||||
// Request another draw so we can avoid adding a transparent layer
|
||||
// during the next display list refresh.
|
||||
invalidateSelf();
|
||||
}
|
||||
mNeedsDraw = false;
|
||||
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
@@ -714,28 +678,27 @@ public class RippleDrawable extends LayerDrawable {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int drawContentLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
|
||||
private boolean drawContent(Canvas canvas, Rect bounds, boolean hasRipples, boolean hasMask) {
|
||||
final ChildDrawable[] array = mLayerState.mChildren;
|
||||
final int count = mLayerState.mNum;
|
||||
|
||||
// We don't need a layer if we don't expect to draw any ripples or
|
||||
// a background, we have an explicit mask, or if the non-mask content
|
||||
// is all opaque.
|
||||
boolean needsLayer = false;
|
||||
if ((mExitingRipplesCount > 0 || (mBackground != null && mBackground.shouldDraw()))
|
||||
&& mMask == null) {
|
||||
|
||||
if (hasRipples && !hasMask) {
|
||||
// If we only have opaque content, we don't really need a layer
|
||||
// because the ripples will be clipped to the drawable bounds.
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (array[i].mId != R.id.mask
|
||||
&& array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
|
||||
if (array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
|
||||
needsLayer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Paint maskingPaint = getMaskingPaint(mode);
|
||||
final int restoreToCount = needsLayer ? canvas.saveLayer(bounds.left, bounds.top,
|
||||
bounds.right, bounds.bottom, maskingPaint) : -1;
|
||||
if (needsLayer) {
|
||||
canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
|
||||
getMaskingPaint(SRC_OVER));
|
||||
}
|
||||
|
||||
// Draw everything except the mask.
|
||||
for (int i = 0; i < count; i++) {
|
||||
@@ -744,82 +707,52 @@ public class RippleDrawable extends LayerDrawable {
|
||||
}
|
||||
}
|
||||
|
||||
return restoreToCount;
|
||||
return needsLayer;
|
||||
}
|
||||
|
||||
private int drawBackgroundLayer(
|
||||
Canvas canvas, Rect bounds, PorterDuffXfermode mode, boolean drawMask) {
|
||||
int saveCount = -1;
|
||||
|
||||
if (mBackground != null && mBackground.shouldDraw()) {
|
||||
// TODO: We can avoid saveLayer here if we push the xfermode into
|
||||
// the background's render thread animator at exit() time.
|
||||
if (drawMask || mode != SRC_OVER) {
|
||||
saveCount = canvas.saveLayer(bounds.left, bounds.top, bounds.right,
|
||||
bounds.bottom, getMaskingPaint(mode));
|
||||
}
|
||||
|
||||
final float x = mHotspotBounds.exactCenterX();
|
||||
final float y = mHotspotBounds.exactCenterY();
|
||||
canvas.translate(x, y);
|
||||
mBackground.draw(canvas, getRipplePaint());
|
||||
canvas.translate(-x, -y);
|
||||
private boolean drawBackgroundAndRipples(
|
||||
Canvas canvas, Rect bounds, boolean hasNonOpaqueMask, boolean hasContentLayer) {
|
||||
if (hasNonOpaqueMask) {
|
||||
final Paint p = getMaskingPaint(SRC_OVER);
|
||||
canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, p);
|
||||
}
|
||||
|
||||
return saveCount;
|
||||
}
|
||||
final PorterDuffXfermode mode = hasContentLayer ? SRC_ATOP : SRC_OVER;
|
||||
final float x = mHotspotBounds.exactCenterX();
|
||||
final float y = mHotspotBounds.exactCenterY();
|
||||
canvas.translate(x, y);
|
||||
|
||||
private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
|
||||
boolean drewRipples = false;
|
||||
int restoreToCount = -1;
|
||||
int restoreTranslate = -1;
|
||||
final Paint p = getRipplePaint();
|
||||
p.setXfermode(mode);
|
||||
|
||||
// Grab the color for the current state and cut the alpha channel in
|
||||
// half so that the ripple and background together yield full alpha.
|
||||
final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
|
||||
final int alpha = (Color.alpha(color) / 2) << 24;
|
||||
p.setColor(color & 0xFFFFFF | alpha);
|
||||
|
||||
final RippleBackground background = mBackground;
|
||||
if (background != null && background.shouldDraw()) {
|
||||
background.draw(canvas, p);
|
||||
}
|
||||
|
||||
// Draw ripples and update the animating ripples array.
|
||||
final int count = mExitingRipplesCount;
|
||||
final Ripple[] ripples = mExitingRipples;
|
||||
for (int i = 0; i <= count; i++) {
|
||||
final Ripple ripple;
|
||||
if (i < count) {
|
||||
ripple = ripples[i];
|
||||
} else if (mRipple != null) {
|
||||
ripple = mRipple;
|
||||
} else {
|
||||
continue;
|
||||
if (count > 0) {
|
||||
final Ripple[] ripples = mExitingRipples;
|
||||
for (int i = 0; i < count; i++) {
|
||||
ripples[i].draw(canvas, p);
|
||||
}
|
||||
|
||||
// If we're masking the ripple layer, make sure we have a layer
|
||||
// first. This will merge SRC_OVER (directly) onto the canvas.
|
||||
if (restoreToCount < 0) {
|
||||
final Paint maskingPaint = getMaskingPaint(mode);
|
||||
final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
|
||||
final int alpha = Color.alpha(color);
|
||||
maskingPaint.setAlpha(alpha / 2);
|
||||
|
||||
// TODO: We can avoid saveLayer here if we're only drawing one
|
||||
// ripple and we don't have content or a translucent mask.
|
||||
restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
|
||||
bounds.right, bounds.bottom, maskingPaint);
|
||||
|
||||
// Translate the canvas to the current hotspot bounds.
|
||||
restoreTranslate = canvas.save();
|
||||
canvas.translate(mHotspotBounds.exactCenterX(), mHotspotBounds.exactCenterY());
|
||||
}
|
||||
|
||||
drewRipples |= ripple.draw(canvas, getRipplePaint());
|
||||
}
|
||||
|
||||
// Always restore the translation.
|
||||
if (restoreTranslate >= 0) {
|
||||
canvas.restoreToCount(restoreTranslate);
|
||||
final Ripple active = mRipple;
|
||||
if (active != null) {
|
||||
active.draw(canvas, p);
|
||||
}
|
||||
|
||||
// If we created a layer with no content, merge it immediately.
|
||||
if (restoreToCount >= 0 && !drewRipples) {
|
||||
canvas.restoreToCount(restoreToCount);
|
||||
restoreToCount = -1;
|
||||
}
|
||||
canvas.translate(-x, -y);
|
||||
|
||||
return restoreToCount;
|
||||
// Returns true if a layer was created.
|
||||
return hasNonOpaqueMask;
|
||||
}
|
||||
|
||||
private int drawMaskingLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
|
||||
@@ -838,6 +771,7 @@ public class RippleDrawable extends LayerDrawable {
|
||||
if (mRipplePaint == null) {
|
||||
mRipplePaint = new Paint();
|
||||
mRipplePaint.setAntiAlias(true);
|
||||
mRipplePaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
return mRipplePaint;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user