So the background should be animated from the current state (which is #ff333333)
to change its color to #ff3355dd within 200ms
than change its color to #ff333333 again within 200ms.
Using Android Honeycomb this can easily done with property animation:
private static final int GLOW_ANIM_DURATION = 400;
private void tabGlowAnimation(View targetTab) {
final ObjectAnimator objAnim =
ObjectAnimator.ofObject(targetTab,
"backgroundColor", // we want to modify the backgroundColor
new ArgbEvaluator(), // this can be used to interpolate between two color values
targetTab.getContext().getResources().getColor(R.color.tab_background), // start color defined in resources as #ff333333
targetTab.getContext().getResources().getColor(R.color.tab_glow) // end color defined in resources as #ff3355dd
);
objAnim.setDuration(GLOW_ANIM_DURATION / 2);
objAnim.setRepeatMode(ValueAnimator.REVERSE); // start reverse animation after the "growing" phase
objAnim.setRepeatCount(1);
objAnim.start();
}
This is very convenient. However it still has 2 problems:
- It doesn't work on preHC devices
- the start color is predefined during coding time (which is not a problem in this case). It is not easy to determine the color of the background, because the background is a drawable and not a color at this point. And/or you needed to implement a custom Evaluator
Unfortunately on earlier devices it is - lets say - challenging to implement a same effect. My solution is an intermittent view layer between the foreground and background of the button. This view has a custom drawable, which alpha value is animated as required. Actually with this we can have any fancy glow (circular, tiger shaped, etc.). I used an idea found in TransitionDrawable.java.
I've seen this post: http://nathanael.hevenet.com/android-dev-fading-background-animation/, but since I tried to use it on a tabview, it messed up the layout.
public class GlowEffect implements Runnable {
private static final int MAX_ALPHA = 255;
/**
* time interval to reschedule a redraw.
*/
private static final int MIN_TICK_MILLIS = 30;
/**
* The alpha of the drawable of this view will be adjusted for the glow effect
*/
private View mGlowLayer;
/**
* the time, when the animation started. (Or should have started if we restart the glow animation)
*/
private long mStartTimeMillis;
/**
* duration of the whole animation = glow increase + glow decrease
*/
private int mDuration;
/**
* inner representation of the required alpha value. Used when the animation restarts.
* We have to store it, because Drawable.getAlpha() doesn't exist.
*/
private double mAlpha;
/**
* The time between each animation steps.
*/
private int mTickMillis;
public GlowEffect() {
}
public void glow(View glowLayer, int durationMillis) {
mGlowLayer = glowLayer;
// correct duration time if the animation has been restarted and the previous is still in progress
// we continue the animation from that point
final long suspectedTimeFromStart = Math.round(mAlpha * mDuration / 2);
mDuration = durationMillis;
mStartTimeMillis = SystemClock.uptimeMillis() - suspectedTimeFromStart;
mGlowLayer.setVisibility(View.VISIBLE);
mGlowLayer.getBackground().setAlpha(0);
mTickMillis = Math.max(MIN_TICK_MILLIS, durationMillis / MAX_ALPHA);
mGlowLayer.postDelayed(this, mTickMillis);
}
@Override
public void run() {
boolean done = true;
final Drawable who = mGlowLayer.getBackground();
float normalized = (float) (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration;
normalized = Math.min(normalized, 1.0f);
done = normalized >= 1.0f;
// linear increase for half of the duration, than linear decrease
//noinspection MagicNumber
mAlpha = (0.5 - Math.abs(normalized - 0.5f)) * 2.0f;
int alpha = (int)Math.round(mAlpha * MAX_ALPHA);
alpha = Math.max(0, alpha);
alpha = Math.min(MAX_ALPHA, alpha);
who.setAlpha(alpha);
who.invalidateSelf();
if (!done) {
mGlowLayer.postDelayed(this, mTickMillis);
} else {
mGlowLayer.setVisibility(View.GONE);
}
}
}
Whenever a glow is needed, the
glow() should be called.
No comments:
Post a Comment