EdgeEffectpublic class EdgeEffect extends Object This class performs the graphical effect used at the edges of scrollable widgets
when the user scrolls beyond the content bounds in 2D space.
EdgeEffect is stateful. Custom widgets using EdgeEffect should create an
instance for each edge that should show the effect, feed it input data using
the methods {@link #onAbsorb(int)}, {@link #onPull(float)}, and {@link #onRelease()},
and draw the effect using {@link #draw(Canvas)} in the widget's overridden
{@link android.view.View#draw(Canvas)} method. If {@link #isFinished()} returns
false after drawing, the edge effect's animation is not yet complete and the widget
should schedule another drawing pass to continue the animation.
When drawing, widgets should draw their main content and child views first,
usually by invoking super.draw(canvas) from an overridden draw
method. (This will invoke onDraw and dispatch drawing to child views as needed.)
The edge effect may then be drawn on top of the view's content using the
{@link #draw(Canvas)} method. |
Fields Summary |
---|
private static final String | TAG | private static final int | RECEDE_TIME | private static final int | PULL_TIME | private static final int | PULL_DECAY_TIME | private static final float | MAX_ALPHA | private static final float | MAX_GLOW_SCALE | private static final float | PULL_GLOW_BEGIN | private static final int | MIN_VELOCITY | private static final int | MAX_VELOCITY | private static final float | EPSILON | private static final double | ANGLE | private static final float | SIN | private static final float | COS | private float | mGlowAlpha | private float | mGlowScaleY | private float | mGlowAlphaStart | private float | mGlowAlphaFinish | private float | mGlowScaleYStart | private float | mGlowScaleYFinish | private long | mStartTime | private float | mDuration | private final android.view.animation.Interpolator | mInterpolator | private static final int | STATE_IDLE | private static final int | STATE_PULL | private static final int | STATE_ABSORB | private static final int | STATE_RECEDE | private static final int | STATE_PULL_DECAY | private static final float | PULL_DISTANCE_ALPHA_GLOW_FACTOR | private static final int | VELOCITY_GLOW_FACTOR | private int | mState | private float | mPullDistance | private final android.graphics.Rect | mBounds | private final android.graphics.Paint | mPaint | private float | mRadius | private float | mBaseGlowScale | private float | mDisplacement | private float | mTargetDisplacement |
Constructors Summary |
---|
public EdgeEffect(android.content.Context context)Construct a new EdgeEffect with a theme appropriate for the provided context.
mPaint.setAntiAlias(true);
final TypedArray a = context.obtainStyledAttributes(
com.android.internal.R.styleable.EdgeEffect);
final int themeColor = a.getColor(
com.android.internal.R.styleable.EdgeEffect_colorEdgeEffect, 0xff666666);
a.recycle();
mPaint.setColor((themeColor & 0xffffff) | 0x33000000);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
mInterpolator = new DecelerateInterpolator();
|
Methods Summary |
---|
public boolean | draw(android.graphics.Canvas canvas)Draw into the provided canvas. Assumes that the canvas has been rotated
accordingly and the size has been set. The effect will be drawn the full
width of X=0 to X=width, beginning from Y=0 and extending to some factor <
1.f of height.
update();
final int count = canvas.save();
final float centerX = mBounds.centerX();
final float centerY = mBounds.height() - mRadius;
canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0);
final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
float translateX = mBounds.width() * displacement / 2;
canvas.clipRect(mBounds);
canvas.translate(translateX, 0);
mPaint.setAlpha((int) (0xff * mGlowAlpha));
canvas.drawCircle(centerX, centerY, mRadius, mPaint);
canvas.restoreToCount(count);
boolean oneLastFrame = false;
if (mState == STATE_RECEDE && mGlowScaleY == 0) {
mState = STATE_IDLE;
oneLastFrame = true;
}
return mState != STATE_IDLE || oneLastFrame;
| public void | finish()Immediately finish the current animation.
After this call {@link #isFinished()} will return true.
mState = STATE_IDLE;
| public int | getColor()Return the color of this edge effect in argb.
return mPaint.getColor();
| public int | getMaxHeight()Return the maximum height that the edge effect will be drawn at given the original
{@link #setSize(int, int) input size}.
return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f);
| public boolean | isFinished()Reports if this EdgeEffect's animation is finished. If this method returns false
after a call to {@link #draw(Canvas)} the host widget should schedule another
drawing pass to continue the animation.
return mState == STATE_IDLE;
| public void | onAbsorb(int velocity)Call when the effect absorbs an impact at the given velocity.
Used when a fling reaches the scroll boundary.
When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
the method getCurrVelocity will provide a reasonable approximation
to use here.
mState = STATE_ABSORB;
velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY);
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = 0.15f + (velocity * 0.02f);
// The glow depends more on the velocity, and therefore starts out
// nearly invisible.
mGlowAlphaStart = 0.3f;
mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
// Growth for the size of the glow should be quadratic to properly
// respond
// to a user's scrolling speed. The faster the scrolling speed, the more
// intense the effect should be for both the size and the saturation.
mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
// Alpha should change for the glow as well as size.
mGlowAlphaFinish = Math.max(
mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
mTargetDisplacement = 0.5f;
| public void | onPull(float deltaDistance)A view should call this when content is pulled away from an edge by the user.
This will update the state of the current visual effect and its associated animation.
The host view should always {@link android.view.View#invalidate()} after this
and draw the results accordingly.
Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
of the pull point is known.
onPull(deltaDistance, 0.5f);
| public void | onPull(float deltaDistance, float displacement)A view should call this when content is pulled away from an edge by the user.
This will update the state of the current visual effect and its associated animation.
The host view should always {@link android.view.View#invalidate()} after this
and draw the results accordingly.
final long now = AnimationUtils.currentAnimationTimeMillis();
mTargetDisplacement = displacement;
if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
return;
}
if (mState != STATE_PULL) {
mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
}
mState = STATE_PULL;
mStartTime = now;
mDuration = PULL_TIME;
mPullDistance += deltaDistance;
final float absdd = Math.abs(deltaDistance);
mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
if (mPullDistance == 0) {
mGlowScaleY = mGlowScaleYStart = 0;
} else {
final float scale = Math.max(0, 1 - 1 /
FloatMath.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3f) / 0.7f;
mGlowScaleY = mGlowScaleYStart = scale;
}
mGlowAlphaFinish = mGlowAlpha;
mGlowScaleYFinish = mGlowScaleY;
| public void | onRelease()Call when the object is released after being pulled.
This will begin the "decay" phase of the effect. After calling this method
the host view should {@link android.view.View#invalidate()} and thereby
draw the results accordingly.
mPullDistance = 0;
if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
return;
}
mState = STATE_RECEDE;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
| public void | setColor(int color)Set the color of this edge effect in argb.
mPaint.setColor(color);
| public void | setSize(int width, int height)Set the size of this edge effect in pixels.
final float r = width * 0.75f / SIN;
final float y = COS * r;
final float h = r - y;
final float or = height * 0.75f / SIN;
final float oy = COS * or;
final float oh = or - oy;
mRadius = r;
mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
| private void | update()
final long time = AnimationUtils.currentAnimationTimeMillis();
final float t = Math.min((time - mStartTime) / mDuration, 1.f);
final float interp = mInterpolator.getInterpolation(t);
mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
if (t >= 1.f - EPSILON) {
switch (mState) {
case STATE_ABSORB:
mState = STATE_RECEDE;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
// After absorb, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
break;
case STATE_PULL:
mState = STATE_PULL_DECAY;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = PULL_DECAY_TIME;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
// After pull, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
break;
case STATE_PULL_DECAY:
mState = STATE_RECEDE;
break;
case STATE_RECEDE:
mState = STATE_IDLE;
break;
}
}
|
|