Question

I am creating a Java app that will allow users to view images and to pan the image using their mouse. To implement the panning of the image I use a combination of mouseClicked and mouseDragged events using JViewports. The bulk of the code is in the mouseDragged method

public void mouseDragged(MouseEvent e, WindowWrapper w) {
    final JViewport vp = someFieldViewPort;

    //Getting the point that the mouse is dragged to to
    Point cp = e.getPoint();
    final Point vPoint = vp.getViewPosition();

    //I found the image went off the content to show the white border so I included this 
    // Here pp is a field that I sent when the mouse is clicked in a separate method

    if(vPoint.getX()+pp.x-cp.x>=0 & vPoint.getY()+pp.y-cp.y>=0)
        vPoint.translate(pp.x-cp.x, pp.y-cp.y);
    else if(vPoint.getX()+pp.x-cp.x>=0 & vPoint.getY()+pp.y-cp.y<0)
        vPoint.translate(pp.x-cp.x, (int) -vPoint.getY());
    else if(vPoint.getX()+pp.x-cp.x<0 & vPoint.getY()+pp.y-cp.y>=0)
        vPoint.translate((int) -vPoint.getX(), pp.y-cp.y);

    //finally set the position of the viewport
    vp.setViewPosition(vPoint);
    vp.repaint();
}

While this works I feel that there must be an easier way to do all of this. If not all of it, could the code to prevent the viewport going off the image to the surrounding border be replaced?

Was it helpful?

Solution

Try using scrollRectToVisible(...) method instead of JViewport#setViewPosition(...):

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
public class HandScrollDemo {
  static class HandScrollListener extends MouseAdapter {
    private final Point pp = new Point();
    @Override public void mouseDragged(MouseEvent e) {
      JViewport vport = (JViewport)e.getSource();
      JComponent label = (JComponent)vport.getView();
      Point cp = e.getPoint();
      Point vp = vport.getViewPosition();
      vp.translate(pp.x-cp.x, pp.y-cp.y);
      label.scrollRectToVisible(new Rectangle(vp, vport.getSize()));
      //vport.setViewPosition(vp);
      pp.setLocation(cp);
    }
    @Override public void mousePressed(MouseEvent e) {
      pp.setLocation(e.getPoint());
    }
  }
  public JComponent makeUI() {
    JLabel label = new JLabel(new Icon() {
      TexturePaint TEXTURE = makeCheckerTexture();
      @Override public void paintIcon(Component c, Graphics g, int x, int y) {
        Graphics2D g2 = (Graphics2D)g.create();
        g2.setPaint(TEXTURE);
        g2.fillRect(x,y,c.getWidth(),c.getHeight());
        g2.dispose();
      }
      @Override public int getIconWidth()  { return 2000; }
      @Override public int getIconHeight() { return 2000; }
    });
    label.setBorder(BorderFactory.createLineBorder(Color.RED, 20));
    JScrollPane scroll = new JScrollPane(label);
    JViewport vport = scroll.getViewport();
    MouseAdapter ma = new HandScrollListener();
    vport.addMouseMotionListener(ma);
    vport.addMouseListener(ma);
    return scroll;
  }
  private static TexturePaint makeCheckerTexture() {
    int cs = 20;
    int sz = cs*cs;
    BufferedImage img = new BufferedImage(sz,sz,BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = img.createGraphics();
    g2.setPaint(Color.GRAY);
    for(int i=0; i*cs<sz; i++) { for(int j=0; j*cs<sz; j++) {
      if((i+j)%2==0) { g2.fillRect(i*cs, j*cs, cs, cs); }
    }}
    g2.dispose();
    return new TexturePaint(img, new Rectangle(0,0,sz,sz));
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() { createAndShowGUI(); }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new HandScrollDemo().makeUI());
    f.setSize(320, 320);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}

OTHER TIPS

I would do it in a different way. I would probably define an object called Image or similar. It would define a BufferedImage and two int values: x and y.

The Image object would also have a draw() method that would just know how to draw an image to a Graphics2D object at the x, y location.

On mouse events, I would modify the x and y values inside the Image object and under the paint of the component I would call image.draw(g2).

+1 to @Dans answer.

Here is an example I made, basically uses JPanel with added MouseAdapter and overrides mousePressed() and mouseDragged() methods. mouseDragged() method will increment x and y co-ordinates of image accordingly and will be drawn via paintComponent(...) of JPanel and Graphics2D#drawImage(Image img,int x,int y,ImageObserver io).

Before click and drag of mouse:

enter image description here

After click and drag of mouse:

enter image description here

//necessary imports
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    /**
     * Default constructor
     */
    public Test() {
        initComponents();
    }

    /**
     * Initialize GUI and components (including ActionListeners etc)
     */
    private void initComponents() {
        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);

        PanPanel pp = null;
        try {
            pp = new PanPanel(ImageIO.read(new URL("http://www.sellcar.co.za/wp-content/uploads/2011/01/Porsche_911_Turbo.jpg")));
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        frame.add(pp);
        //pack frame (size JFrame to match preferred sizes of added components and set visible
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {

        /**
         * Create GUI and components on Event-Dispatch-Thread
         */
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    //set nimbus look and feel
                    for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                        if ("Nimbus".equals(info.getName())) {
                            UIManager.setLookAndFeel(info.getClassName());
                            break;
                        }
                    }
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
                    e.printStackTrace();
                }
                //create GUI instance
                Test test = new Test();
            }
        });
    }
}

class PanPanel extends JPanel {

    private int x, y;
    private int width = 400, height = 400;
    BufferedImage img;
    private final static RenderingHints textRenderHints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    private final static RenderingHints imageRenderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    private final static RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    static int startX, startY;

    public PanPanel(BufferedImage img) {
        x = 0;
        y = 0;
        this.img = img;

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent me) {
                super.mousePressed(me);
                startX = me.getX();
                startY = me.getY();
            }
        });

        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent me) {
                super.mouseDragged(me);

                if (me.getX() < startX) {//moving image to right
                    x -= 2;
                } else if (me.getX() > startX) {//moving image to left
                    x += 2;
                }

                if (me.getY() < startY) {//moving image up
                    y -= 2;
                } else if (me.getY() > startY) {//moving image to down
                    y += 2;
                }
                repaint();
            }
        });
    }

    @Override
    protected void paintComponent(Graphics grphcs) {
        super.paintComponent(grphcs);
        Graphics2D g2d = (Graphics2D) grphcs;

        //turn on some nice effects
        applyRenderHints(g2d);

        g2d.drawImage(img, x, y, null);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    public static void applyRenderHints(Graphics2D g2d) {
        g2d.setRenderingHints(textRenderHints);
        g2d.setRenderingHints(imageRenderHints);
        g2d.setRenderingHints(renderHints);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top