views:

409

answers:

2

I've got an editor with lots of image thumbnails. I'd like a double-click on an image to display the full resolution image using a modal undecorated dialog. Ideally, this would be animated, to show the image zooming up to full resolution on the center of the screen, then any click would make the image go away, either zooming back out or fading away.

I'm not concerned with establishing an exact behavior, I just want something slick. I've found plenty of JavaScript examples for this, but is there anything built for Swing?

A: 

You can create a custom control that displays the image at the scale you want.

1) Create a BufferedImage from the image file you want using ImageIO.read(file) (you can also create it from an InputStream)

2) Extend the JComponent or Canvas class and overload the paint function to draw the animated image using Graphics.DrawImage() and set the width and height based on how long the window has been open. Set a timer or use another thread to repeatedly have the component redraw itself for however long you want the animation to play.

I haven't done much with customized modal dialogs (I mostly find them annoying), but you can use a JDialog and and your component to it.

Chad Okere
Custom painting is done by overrideing the paintComponent() method NOT the paint() method.
camickr
Uh... I've always used paint, and it works fine. There's also update(), or you can just call getGraphics() and draw on a component without even overriding it. -- There are a lot of ways to draw on components. It doesn't make that much difference. -- If you read the documentation for JComponent.paint(), it actually calls paintComponent(), so unless you have child elements there's no reason to override paintComponent(), rather then paint()
Chad Okere
Paint does a whole lot more than just call paintComponent, including setting clip regions, applying a repaint manager (if the component is using one) and painting the border as well.
Ash
@Chad: If the zoomed-in image should be larger than the JFrame of my app, how would I accomplish that? Should I use an undecorated JFrame or JDialog and set the size and position for each iteration of the animation?
Sam Barnum
+1  A: 

This piece of code does more or less the trick... There is still a problem in the way I'm setting the dialog's location...

Hope it helps.

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.lang.reflect.InvocationTargetException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class OpenImageZooming {

 private static final int NB_STEPS = 30;

 private static final long OPENING_TOTAL_DURATION = 3000;

 public static void main(String[] args) {
  OpenImageZooming me = new OpenImageZooming();
  me.openImage(args[0]);
 }

 private JFrame frame;
 private JDialog dialog;
 private JPanelZooming panelZooming;

 private void openImage(final String imagePath) {
  SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    frame = new JFrame();
    frame.setTitle("Open image with zoom");
    JPanel p = new JPanel(new BorderLayout());
    p.add(new JLabel("click on button to display image"), BorderLayout.CENTER);
    JButton button = new JButton("Display!");
    frame.setContentPane(p);
    button.addActionListener(new ActionListener() {

     public void actionPerformed(ActionEvent e) {
      Thread t = new Thread() {

       @Override
       public void run() {
        displayImaggeWithProgressiveZoom(imagePath);
       }

      };
      t.start();

     }

    });
    p.add(button, BorderLayout.SOUTH);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(300, 100);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
   }
  });
 }

 protected void displayImaggeWithProgressiveZoom(String imagePath) {
  try {
   final BufferedImage image = ImageIO.read(new File(imagePath));

   for (int i = 0; i < NB_STEPS; i++) {
    displayDialog(i, NB_STEPS, image);

    Thread.sleep(OPENING_TOTAL_DURATION / NB_STEPS);
   }

  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }

 private void displayDialog(final int i, final int nbSteps, final BufferedImage image) {

  try {
   SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
     if (dialog == null) {
      dialog = new JDialog(frame);
      dialog.setUndecorated(true);
      dialog.setModal(false);
      panelZooming = new JPanelZooming(image);
      dialog.setContentPane(panelZooming);
      dialog.setSize(0, 0);
      dialog.setLocationRelativeTo(frame);
      dialog.setVisible(true);

     }
     int w = (i + 1) * image.getWidth() / nbSteps;
     int h = (i + 1) * image.getHeight() / nbSteps;

     panelZooming.setScale((double) (i + 1) / nbSteps);
     dialog.setSize(w, h);
     dialog.setLocationRelativeTo(null);
    }
   });
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }

 @SuppressWarnings("serial")
 public static class JPanelZooming extends JPanel {

  private BufferedImage image;

  private double scale = 1.0d;

  public JPanelZooming(BufferedImage image) {
   this.image = image;
  }

  @Override
  protected void paintComponent(Graphics g) {
   super.paintComponent(g);
   Graphics2D g2 = (Graphics2D) g;
   AffineTransform at = g2.getTransform();
   AffineTransform oldTransform = (AffineTransform) at.clone();
   at.scale(scale, scale);
   g2.setTransform(at);
   g2.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
   g2.setTransform(oldTransform);

  }

  public void setScale(double scale) {
   this.scale = scale;
  }

 }
}
Laurent K
Thanks for the code Laurent. The dialog animation is pretty choppy, and disappears and reappears between animations (on OS X). I tried using the animation framework, and it's a bit better, but still choppy. Maybe results would be better using the glass pane and a component instead of a dialog?
Sam Barnum
So to clarify, how can I draw a custom component on the full window? Do I need a transparent glassPane on the JRootPane? That doesn't seem to fill in the entire window, only the top frame.
Sam Barnum
Or should I use a transparent undecorated JDialog that takes up the entire window? I've seen that technique used before, and it seems like a hack. Because the dialog can't be completely transparent, it seems.
Sam Barnum