Question

I need to create an image slideshow in my app (i.e. the image should be changed at a regular interval of time with a fade in / fade out effect). I have tried some code, but it throws illegal exception.

Is there any option to change images in picture scroll field programatically? i.e. from a thread or something?

public class SlideTransition extends MainScreen {

    final Bitmap image000 = Bitmap.getBitmapResource("img1.jpg");
    final Bitmap image001 = Bitmap.getBitmapResource("img2.jpg");
    final Bitmap image002 = Bitmap.getBitmapResource("img3.jpg");

    final BitmapField animationField = new BitmapField(image000);
    int counter = 0;
    Timer animationTimer = new Timer();
    TimerTask animationTask;

    public SlideTransition() {
        animationTask = new TimerTask() {

            public void run() {
                if (counter == 0) {

                    animationField.setBitmap(image000);
                }

                if (counter == 1) {
                    animationField.setBitmap(image001);
                }
                if (counter == 2) {
                    animationField.setBitmap(image002);
                    counter = -1;
                }
                counter++;
            }
        };
        animationTimer.scheduleAtFixedRate(animationTask, 0, 100);
        add(animationField);
    }
}
Was it helpful?

Solution

The code posted will throw an IllegalStateException because it is attempting to modify a UI element directly from a background (timer) thread:

animationField.setBitmap(image000);

You need to use UiApplication.getUiApplication().invokeLater(), or something similar, to modify the UI from a background thread.

Also, you can have your timer continuously adjust the bitmap field's alpha value (opacity) to produce a fade effect.

Here's an example, starting with the code you provided:

public class SlideTransition extends MainScreen {

   private final Bitmap image000 = Bitmap.getBitmapResource("img1.jpg");
   private final Bitmap image001 = Bitmap.getBitmapResource("img2.jpg");
   private final Bitmap image002 = Bitmap.getBitmapResource("img3.jpg");
   private AlphaBitmapField animationField = new AlphaBitmapField(image000);
   private Timer animationTimer = new Timer();
   private TimerTask animationTask;
   private final int animationPeriodMsec = 40; // 25 Hz animation timer

   public SlideTransition() {
      animationTask = new AnimationTask();

      add(animationField);
      animationTimer.scheduleAtFixedRate(animationTask, 0, animationPeriodMsec);  
   }

   // I separated your anonymous timer task into its own class for readability
   private class AnimationTask extends TimerTask {

      private final int fadeDurationMsec = 500;
      private final int displayDurationMsec = 1500;
      private final int fadeInEndCount = fadeDurationMsec / animationPeriodMsec;
      private final int fadeOutStartCount = (fadeDurationMsec + displayDurationMsec) / animationPeriodMsec;
      private final int endCount = (2 * fadeDurationMsec + displayDurationMsec) / animationPeriodMsec;

      private int imgCounter = 0;
      private int cycleCounter = 0;

      public void run() {
         if (cycleCounter >= endCount) {
            cycleCounter = 0;
         }
         Bitmap newImage = null;
         if (cycleCounter == 0) {
            // time to switch the images
            if (imgCounter == 0) {
               newImage = image000;
            } else if (imgCounter == 1) {
               newImage = image001;
            } else if (imgCounter == 2) {
               newImage = image002;
               imgCounter = -1;
            }
            imgCounter++;
         }

         // assign final variables to use inside a UI thread Runnable
         final Bitmap currentImage = newImage;
         final int i = cycleCounter;

         UiApplication.getUiApplication().invokeLater(new Runnable() {
            public void run() {
               if (i == 0) {
                  // switch images, and start with the image invisible (alpha = 0)
                  animationField.setAlpha(0);
                  animationField.setBitmap(currentImage);
               } else if (i <= fadeInEndCount) {
                  // fade in by changing alpha
                  animationField.setAlpha(i * 255 / fadeInEndCount);
               } else if (i >= fadeOutStartCount) {
                  // fade out by changing alpha
                  animationField.setAlpha(255 + (fadeOutStartCount - i) * 255 / (endCount - fadeOutStartCount));
               }               
            }
         });

         cycleCounter++;
      }
   }

   // this class extends BitmapField to allow alpha adjustment, and automatic repainting
   private class AlphaBitmapField extends BitmapField {

      private int _alpha;

      public AlphaBitmapField(Bitmap img) {
         super(img);
      }

      public void setAlpha(int alpha) {
         if (alpha != _alpha) {
            _alpha = alpha;
            // force a repaint
            invalidate();
         }
      }

      protected void paint(Graphics graphics) {
         graphics.setGlobalAlpha(_alpha);         
         super.paint(graphics);
      }
   }
}

I changed the time values from your code, but you can modify them to whatever you like. I had the animation timer run at 25 cycles per second. I find that this is a reasonable value for smartphone animations. It's fast enough to look fairly smooth, but is only about twice the rate that the human eye can process information like this ... any faster is probably wasteful.

I defined constants that you can use to control the length of time that the fade in and fade out takes (fadeDurationMsec) and the time that each image is displayed between fade in and fade out (displayDurationMsec). All values are in milliseconds.

Helpful References

BlackBerry AnimatedGIFField sample code.

BlackBerry forums thread about fade in / fade out

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top