Fade on change Label
August 13th, 2007 by Christos Fragoulides

Here is the concept: We want to extend the JLabel swing component and make it perform a simple fade effect each time it’s value changes. Using this kind of label can be helpful to build a user interface for an application having the need to notify the user for important state changes.
The following applet demonstrates the component:

A good example is an application which continuously tracks a number of important data and represents values on the screen, each one on a separate label. It will be easier for the user to notice a change if the labels perform some kind of animation in response to an update of their value.

It is obvious that to make the above happen, we must “inject” animation code inside JLabel. The task is to figure out a way to start an animation thread whenever the text value of the label is updated. Well you can say this one is easy, because there’s the setText() method which we can override like this:

public void setText(String text){
  super.setText(text);
  if (!text.equals(lastValue)){
    lastValue = text;
    animate();
  }
}

Alright, now that we have set up a mechanism to start the animation, we have to implement the animation procedure itself. The question is: What do we have to put inside animate() method?
When I was trying to find an answer to the above question, I was sure for one thing only: There must be a thread to carry on the animation, witch I had to initiate during the call of animate() method. So I decided to make the class implement the Runnable Interface, and put my core animation code inside the run() method. I ended up with the following body for animate():

private void animate() {
// Only animate if there's not another animation in progress
// and this Label is actually added to a parent Container, i.e.
// if it is visible.
  if(!animating && this.getParent()!=null){
    Thread t = new Thread(this);
    t.start();
  }
}

Now we have to deal with the animation procedure, which involves the manipulation of the paintComponent() method. But this is the most tricky part: Simply having a loop repeating a call to paintComponent() is not a solution, because it requires a Graphics object reference to the screen area where the label resides. This object is normally passed to the method by the Event Model and you actually don’t have control over the last. The right way to make paintComponent() get called with an appropriate Graphics object is to call the repaint() method of the Component. This will add a request to the event queue and eventually will lead to a call of paintComponent().

Ok, so we can have an animation loop inside run() which calls repaint() at the end of every animation frame. Unfortunately, this approach will not work. Subsequent calls to repaint() with sort intervals between them will get stacked in the event queue, but will be treated as one request. Let’s say we make 10 calls of repaint() with an interval of 200 milliseconds. The most probable result is to get only one call of paintComponent() because it’s up to the Event Model to decide when to dispatch the event.

Here is the workaround I figured out:
First, override paintComponent() and add the necessary code to perform the drawing for every frame of the fade effect. Also add a paintCalled flag to be informed about the method call.

public void paintComponent(Graphics g){
  // Let the Label perform its normal painting.
  super.paintComponent(g);
  // Now make the fade effect.
  if(fade != 0){
    Insets i = this.getInsets();
    g.setColor(fadeColor);
    g.fillRect(i.left, i.top,
      getWidth() - i.left - i.right,
      getHeight() - i.top - i.bottom);
  }
  // paintComponent() called, we can continue to the next
  // animation frame.
  paintCalled = true;
}

Second, inside the animation loop, wait for paintComponent() to be called before proceeding to the next animation frame by checking the value of paintCalled flag. A sleep interval of 100 milliseconds proved to be satisfying.

public void run() {
  animating = true;
  fade = initFade;
  try {
    while (fade!=0) { //The animation stops when fade reaches 0.
      ... // Animation control code.
      repaint();
      // Now wait until paintComponent() gets called.
      while(!paintCalled && fade!=0){
        Thread.sleep(100);
      }
    }
    animating = false;
  }
  catch (Exception e) {
    ...
  }
}

And that’s all. It works like a charm!
You can find the full source code of FadeOnChangeLabel here: FadeOnChangeLabel source code. The full class has a method to set fade options(setParams()) and there’s also an additional method named setRepaintContainer() that can be used in circumstances where the JLabel doesn’t control it’s painting, like when you make custom Renderers for list or tree items. Call setRepaintContainer() on the label and pass the Container object that controls it’s painting.
I’ve fully documented the file using comments and hope it would be easy for everyone to understand how it works.

A couple of links to broaden your knowledge on the field:

  1. Painting in AWT and Swing
  2. Using Swing Components

Posted in Swing, User Interface

Pablo

Great entry. Very useful and clear code too. Thanks!

Posted at June 12th, 2009 - 8:34 am

gumuruh

waw….
i never thought about this gonna need
changes on paintComponent() method to be overrided.

anyway, great thanks!
do u have another trick using Trident library for making the jlabel fadeout? :D

Posted at July 3rd, 2011 - 11:25 pm

Post a Comment Below »
Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.

Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.