I'm trying to write a simple class that extends from JPanel that can zoom in and zoom out on an image and then uses a JScrollPane at the top level to allow someone to scroll back and forth across the zoomed image. I've having trouble with the JScrollPane portion.
When clicking one of the zoom buttons (whether it be in, out, or reset), the button must be clicked twice in order for the JScrollBars to appear(or in the case of the reset button, disappear). Even after they've appear if you continue to zoom in or out the bars do not update according to the new level of zoom. Resizing the window fixes this issue but I'm looking for a more concrete solution.
ImagePanel Code:
package com.zephyr.graphics;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel implements PropertyChangeListener{
private File imageFile;
private BufferedImage image;
private double zoomMultiplier;
public ImagePanel()
{
imageFile = null;
image = null;
zoomMultiplier = 1;
}
public ImagePanel(File imageFile)
{
this.imageFile = imageFile;
try {
image = ImageIO.read(imageFile);
} catch (IOException e) {
image = null;
}
zoomMultiplier = 1;
}
public void setImageFile(File imageFile)
{
this.imageFile = imageFile;
if(imageFile == null)
{
image = null;
}
else
{
try {
image = ImageIO.read(imageFile);
} catch (IOException e) {
image = null;
}
}
zoomMultiplier = 1;
this.revalidate();
this.repaint();
}
public File getImageFile()
{
return imageFile;
}
public void paintComponent(Graphics g)
{
g.clearRect(0, 0, this.getWidth(), this.getHeight());
if(image == null)
{
this.setPreferredSize(new Dimension(this.getParent().getWidth(), this.getParent().getHeight()));
g.setColor(Color.black);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
else
{
double preferredWidth = this.getParent().getWidth() * zoomMultiplier;
double preferredHeight = this.getParent().getHeight() * zoomMultiplier;
this.setPreferredSize(new Dimension((int)preferredWidth, (int)preferredHeight));
Image scaled = image.getScaledInstance((int)preferredWidth, (int)preferredHeight, Image.SCALE_DEFAULT);
g.drawImage(scaled, 0, 0, null);
}
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(evt.getPropertyName().equals("Image File"))
{
setImageFile((File)evt.getNewValue());
}
else if(evt.getPropertyName().equals("Zoom In"))
{
double additive = ((Number)evt.getNewValue()).doubleValue();
zoomMultiplier += additive;
this.revalidate();
this.repaint();
}
else if(evt.getPropertyName().equals("Zoom Out"))
{
double subtractive = ((Number)evt.getNewValue()).doubleValue();
zoomMultiplier -= subtractive;
this.revalidate();
this.repaint();
}
else if(evt.getPropertyName().equals("Zoom Reset"))
{
zoomMultiplier = 1;
this.revalidate();
this.repaint();
}
}
}
Main Class
package com.zephyr.graphics;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main {
public static void main(String[] args)
{
PropertyChangeSupport pcs = new PropertyChangeSupport(Main.class);
JFileChooser chooser = new JFileChooser(System.getProperty("user.home"));
chooser.setDialogTitle("Select an image");
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
File file = null;
if(chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)
{
file = chooser.getSelectedFile();
}
else
{
System.err.println("NO FILE WAS SELECTED OR THE WINDOW WAS CLOSED");
return;
}
JFrame frame = new JFrame("ImagePanel Tester");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ImagePanel iPanel = new ImagePanel(file);
pcs.addPropertyChangeListener(iPanel);
JScrollPane iScroll = new JScrollPane(iPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
iScroll.setPreferredSize(new Dimension(1024, 768));
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
JButton zoomIn = new JButton("Zoom In");
zoomIn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
pcs.firePropertyChange("Zoom In", null, new Double(.25));
frame.revalidate();
frame.repaint();
}
});
controlPanel.add(zoomIn);
JButton zoomOut = new JButton("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pcs.firePropertyChange("Zoom Out", null, new Double(.25));
frame.revalidate();
frame.repaint();
}
});
controlPanel.add(zoomOut);
JButton zoomReset = new JButton("Zoom Reset");
zoomReset.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pcs.firePropertyChange("Zoom Reset", null, null);
frame.revalidate();
frame.repaint();
}
});
controlPanel.add(zoomReset);
JButton changeImage = new JButton("Change Image");
changeImage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(chooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION)
{
File tempFile = chooser.getSelectedFile();
pcs.firePropertyChange("Image File", null, tempFile);
frame.revalidate();
frame.repaint();
}
}
});
controlPanel.add(changeImage);
frame.add(iScroll, BorderLayout.CENTER);
frame.add(controlPanel, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.revalidate(); //Shouldn't really be necessary, but seems to be helping
}
}