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