Android: Getting a Bitmap to Glow/Flash with a Specified Color

For Android 2.2, I needed to have a small set of bitmaps gently glow/flash at some specified rate within a custom view. The method described below seems to work, although there might be better ways to do it (and let me know what they are!).

You can see it in action by installing this free Virtual Bubble Wrap app from the market, popping a bubble or two, and then waiting for about 10 seconds until the app lets you know where the unpopped bubbles are by having them briefly glow.

Note: For performance reasons, you'll want to minimize the instantiation of classes in the "Draw" method of the View, and there are probably some other optimizations that could be made.

The basic components of the process to get a "flashing" glow applied to a bitmap seem to be:
  • an Android Paint class whose color filter is set to the desired "glow" color that will be added to the bitmap
  • an Interpolator object used to have the "glow" last just a little bit longer when it is near its brightest
  • a helper class to interpolate and optionally scale the end color

The actual call to render the "glowing" bitmap is just this:
 canvas.drawBitmap(bitmap, null, dst,mPaintForGlow);

where bitmap is your bitmap, and dst is the destination rectangle for the bitmap (it's an instance of RectF).

Configuring (Android) Paint for Glowing

Nothing really unusual here, except setting the ColorFilter property:

mPaintForGlow = new Paint();
mPaintForGlow.setDither(true);
mPaintForGlow.setAntiAlias(true);
mPaintForGlow.setFilterBitmap(true);  
ColorFilter colorFilterTint = new LightingColorFilter(Color.WHITE, glowColor);
mPaintForGlow.setColorFilter(colorFilterTint);
where glowColor is the (int) color that will be used to shade the bitmap. This is a dynamic quantity.

Calculating the (Dynamic) Glow Color

The dynamic glow color will vary depending on where you are in the "glowing cycle". This starts at transparent (Color.TRANSPARENT) for no glowing, and ending at a color that represents the glow at its brightest.  For the Bubble Wrap app, I actually had an additional scale factor that was used to fine tune this color as I was playing with it, as it was a bit tricky to get the "glow" looking right.  In this case, the "end" color ended up being #80FFA500 (a light orange), to which I scale the RGB components by 20%.

The (int) color glowColor is obtained via


glowColor =  mColorInterpolator.getColor(
                  mInterpolatorForColor.getInterpolation(t)
                 );
The three components being used here to get the glow color are:
  1. the helper class ColorInterpolator (custom but trivial)
  2. one of the Android interpolators that implements the Interpolator interface(mInterpolatorForColor)
  3. a time t in the interval 0 to 1 reflecting where we are in the "flashing" cycle

1. Helper ColorInterpolator Class

A custom helper class ColorInterpolator is used to interpolate the RGB colors between two different colors.  All this class does is return the interpolated color for any value t (clamped to between 0 and 1)Here's the relevant bits of this class:



  public int getInterpolatedColor(float t) {
    if (t<0) {t=0f;}
    if (t>1) {t=1f;}

    int lEnd_Red = (int) (mScaleFactor*mEnd_Red);
    int lEnd_Green = (int) (mScaleFactor*mEnd_Green);
    int lEnd_Blue = (int) (mScaleFactor*mEnd_Blue);

    return Color.argb(
      interpolateInt(mStart_Alpha,mEnd_Alpha-mStart_Alpha,t),
      interpolateInt(mStart_Red,lEnd_Red,t),
      interpolateInt(mStart_Green,lEnd_Green,t),
      interpolateInt(mStart_Blue,lEnd_Blue,t));
  }
where the convenience function interpolateInt is just

 public int interpolateInt(int a, int b, float t) {
    return (int) (a + (b-a)*t);
 }
Note that Android level 11 (3.0) has an additional helper class for color interpolation that you'd probably want to look at if your programming at or above that level - android.animation.ArgbEvaluator.

2. Android Interpolator

The variable mInterpolatorForColor is just a vanilla Android AccerelateDeceleterateInterpolator from android.view.animation:


mInterpolatorForColor = new AccelerateDecelerateInterpolator();

This provides a fairly satisfactory effect of the glow "hanging on" at its brightest.

3. Determining Where you Are in the Flashing Cycle

The flashing cycle is parameterized by a number between 0 and 1 (the variable whereInFlashCycle in the snippet below), with 0 representing no glow applied at all, and 1 representing full glow.


long now_ms System.currentTimeMillis();
float time_sec = (now_ms - mWhenStartedGlowing_ms)/1000.0f;
float cycleNumber_float =  time_sec/howLongToFullGlow_Seconds;
int cycleNumber = (int) cycleNumber_float;

if ((cycleNumber % 2) ==0) {
  //getting brighter
  whereInFlashCycle = cycleNumber_float - cycleNumber;
}
else {
  //getting dimmer
  whereInFlashCycle = (cycleNumber+1) - cycleNumber_float;
}
The (float) variable howLongToFullGlow_Seconds determines the rate at which the bitmap glows/flashes.

Note that all time is based on clock-time, and some of this can probably be simplified by using some other Android functionality.

And that's it.

I used this approach in a custom view for the bubble wrap app using more than a hundred small bitmaps, updating 10 times a second (which is all we needed), and it looked fine on a Samsung Fascinate with Android 2.2. You might hit performance issues or interesting quirks trying to get higher frame rates, or using larger bitmaps, or on a larger tablet display. We heard of no issues in beta testing, which included users with Xoom tablets.

No comments:

Post a Comment

Popular Posts