2

我正在尝试使用 Java 6/7 编写可重用的 JPanel 图像查看器。如果在调整大小期间不移动 JPanel的左上角(视口原点),我可以使用 mouseDragged 实现图像拖动,也可以调整 JPanel 的大小以裁剪或显示图像。注意我不想调整图像大小以适应面板。我希望图像在调整面板大小以裁剪它时保持固定在屏幕上。

如果我拖动框架的左边缘并因此移动视口原点,即使我在调整大小期间动态重新计算图像原点,在调整大小操作期间也会出现相当大的图像抖动。这似乎是由于在调用paintComponent() 的时间和通过调用drawImage() 实际渲染图像的时间之间的视口原点发生了变化。关于如何消除所说的抖动的任何想法?提前致谢。

下面的两个类应该演示该行为:

/***** ImageViewer.java *****/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;

import javax.imageio.ImageIO;

public class ImageViewer extends JFrame
{
    private ImagePanel      canvas;
    private BufferedImage   theImage;
    private JFileChooser    theChooser;

    public ImageViewer( BufferedImage bi )
    {
    canvas= new ImagePanel( );
    add( canvas, BorderLayout.CENTER );

    JPanel control= new JPanel();
    JButton loadBtn= new JButton( "Load Image" );
    control.add( loadBtn );
    add( control, BorderLayout.NORTH );
    loadBtn.addActionListener( new ActionListener() {
        public void actionPerformed( ActionEvent e )
        { doLoad(); }
    });

    theChooser= new JFileChooser();
    }

    public void doLoad()
    {
    File iFile = null;

    int retVal = theChooser.showOpenDialog(this);
    if (retVal == JFileChooser.APPROVE_OPTION) {
        iFile = theChooser.getSelectedFile();
        try {
        theImage = ImageIO.read(iFile);
        } catch (FileNotFoundException ie) {
        System.err.println("File not found");
        System.exit(1);
        } catch (IOException ie) {
        System.err.println("IOException");
        System.exit(1);
        }
        canvas.setImage( theImage );
        repaint();
    }
    }

    /**
     * @param args
     */
    public static void main(String[] args)
    {
    BufferedImage bImage= null;

    ImageViewer theApp= new ImageViewer( bImage );
    theApp.setDefaultCloseOperation(EXIT_ON_CLOSE);

    theApp.pack();
    theApp.setVisible( true );
    }
}

/***** ImagePanel.java *****/
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.awt.geom.*;

import javax.swing.JPanel;

public class ImagePanel extends JPanel
{
    private BufferedImage theImage;
    private Dimension preferredSize;
    private double scaleF = 1.0; // image scaling factor
    private Point2D.Double deltaLoc; // dx, dy in scaled units for image upper
                     // left corner from origin to current loc
                         // inside or outside viewport
    private Point panelLastLoc; // last location of upper left corner of the
                // viewport in screen coordinates
    private int lastX;
    private int lastY;
    private PanelState state = PanelState.IDLE;

    public enum PanelState
    {
    IDLE, RESIZING, PMOVING, IMOVING
    };

    public ImagePanel()
    {
    setBackground(Color.GRAY);
    preferredSize = new Dimension(400, 400);

    addHierarchyListener(new HierarchyListener() {
        public void hierarchyChanged(HierarchyEvent he)
        {
        doComponentChanged(he);
        }

    });

    addHierarchyBoundsListener(new HierarchyBoundsAdapter() {
        public void ancestorMoved(HierarchyEvent he)
        {
        doComponentMoved();
        }

        public void ancestorResized(HierarchyEvent he)
        {
        doAncestorResizing();
        }
    });

    addComponentListener(new ComponentAdapter() {
        public void componentResized(ComponentEvent ce)
        {
        doComponentResized();
        }
    });

    addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent me)
        {
        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        state = PanelState.IMOVING;
        lastX = me.getX();
        lastY = me.getY();
        }

        public void mouseReleased(MouseEvent me)
        {
        setCursor(Cursor.getDefaultCursor());
        state = PanelState.IDLE;
        }
    });

    addMouseMotionListener(new MouseMotionAdapter() {
        public void mouseDragged(MouseEvent me)
        {
        doImageMoved(me);
        }
    });
    }

    public ImagePanel(BufferedImage bi)
    {
    theImage = bi;
    preferredSize = new Dimension(theImage.getWidth(), theImage.getHeight());
    }

    public Dimension getPreferredSize()
    {
    return preferredSize;
    }

    private void doComponentChanged(HierarchyEvent he)
    {
    if (isShowing()) {
        panelLastLoc = getLocationOnScreen();
    }
    state = PanelState.IDLE;
    }

    private void doImageMoved(MouseEvent me)
    {
    if (!isShowing() || theImage == null)
        return;

    switch (state)
    {
        case RESIZING:
        error("IMOVING->RESIZING");
        break;
        case PMOVING:
        error("IMOVING->PMOVING");
        break;
        case IDLE:
        error("IMOVING->IDLE");
        break;
        case IMOVING:
        if (contains(me.getX(), me.getY())) {
            int dx = me.getX() - lastX;
            int dy = me.getY() - lastY;
            lastX = me.getX();
            lastY = me.getY();
            deltaLoc.x += dx;
            deltaLoc.y += dy;
            repaint();
        }
        return;
    }

    }

    private void doComponentMoved()
    {
    if (!isShowing() || theImage == null)
        return;

    switch (state)
    {
        case RESIZING:
        return;
        case IMOVING:
        error("IMOVING->PMOVING");
        break;
        case PMOVING:
        case IDLE:
        panelLastLoc = getLocationOnScreen();
        state = PanelState.PMOVING;
    }
    return;
    }

    private void doAncestorResizing()
    {
    if (!isShowing() || theImage == null)
        return;

    switch (state)
    {
        case IMOVING:
        error("IMOVING->RESIZING");
        return;
        case RESIZING:
        case PMOVING:
        case IDLE:
        Point cLoc = getLocationOnScreen();
        state = PanelState.RESIZING;
        if (theImage != null) {
            deltaLoc.x -= cLoc.x - panelLastLoc.x;
            deltaLoc.y -= cLoc.y - panelLastLoc.y;
        }
        panelLastLoc = cLoc;
    }
    }

    private void doComponentResized()
    {
    Point cLoc = getLocationOnScreen();
    switch (state)
    {
        case RESIZING:
        state = PanelState.IDLE;
        break;
        case IMOVING:
        error("IMOVING->RESIZED");
        break;
        case PMOVING:
        state = PanelState.IDLE;
        break;
        case IDLE:
        break;
    }

    if (theImage != null) {
        deltaLoc.x -= cLoc.x - panelLastLoc.x;
        deltaLoc.y -= cLoc.y - panelLastLoc.y;
    }
    panelLastLoc = cLoc;
    }

    public void setImage(BufferedImage nI)
    {
    theImage = nI;
    scaleF = 1.0;
    state = PanelState.IDLE;
    deltaLoc = new Point2D.Double(Math.round((getWidth() - theImage.getWidth()) / 2.0),
        Math.round((getHeight() - theImage.getHeight()) / 2.0));
    panelLastLoc = getLocationOnScreen();
    }

    private void error(String em)
    {
    System.err.println(em);
    System.exit(1);
    }

    public void paintComponent(Graphics g)
    {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    if (theImage == null)
        return;
    /*
     * Check if the viewport has resized or moved. The check below is
     * performed on every repaint.
     */

    Point cLoc = getLocationOnScreen();
    Dimension cDim = getSize();

    if (state == PanelState.RESIZING || state == PanelState.IDLE) {
        deltaLoc.x -= cLoc.x - panelLastLoc.x;
        deltaLoc.y -= cLoc.y - panelLastLoc.y;
    }
    /*
    System.err.println("image left edge= "
        + Math.round(cLoc.x + deltaLoc.x) + "; state= " + state
        + "; cDim= " + cDim + "; cLoc= " + cLoc + "; pLLoc= "
        + panelLastLoc + "; dLoc= " + deltaLoc);
        */
    // In all cases update panelLastLoc
    panelLastLoc = cLoc;

    g2.drawImage(theImage, (int) Math.round(deltaLoc.x),
        (int) Math.round(deltaLoc.y), null);
    }
}
4

2 回答 2

0

您是否尝试过覆盖以在您的图像上paintComponent调用repaint(或可能)?revalidate这将确保drawImage在更改后在屏幕上显示任何内容之前调用。

另请参阅此问题

于 2012-05-22T23:52:26.143 回答
0

不是答案,而是我认为我会分享的失败尝试。如果必须,请投反对票。

我以为我会很聪明,并尝试使用持有 JLabel/ImageIcon 的 JScrollPane 为我做运动,但它仍然没有用。例如,

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.*;

@SuppressWarnings("serial")
public class ImageViewer2 extends JPanel {
   private static final int SP_WIDTH = 500;
   private static final int SP_HEIGHT = SP_WIDTH;
   private JLabel viewportView = new JLabel();
   private JViewport viewport = new JViewport();
   private JScrollPane scrollpane = new JScrollPane();
   private Point viewLocOnScrn = null;

   public ImageViewer2() {
      viewport.setView(viewportView);
      scrollpane.setViewport(viewport);
      viewport.setBackground(Color.gray);
      viewport.addComponentListener(new MyComponentListener());

      scrollpane.setPreferredSize(new Dimension(SP_WIDTH, SP_HEIGHT));
      scrollpane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      scrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);

      MyMouseAdapter mouseAdapter = new MyMouseAdapter();
      viewport.addMouseListener(mouseAdapter);
      viewport.addMouseMotionListener(mouseAdapter);

      JButton loadImageBtn = new JButton(new LoadImageAction("Load Image"));
      JPanel topPanel = new JPanel();
      topPanel.add(loadImageBtn);

      setLayout(new BorderLayout());
      add(scrollpane, BorderLayout.CENTER);
      add(topPanel, BorderLayout.PAGE_START);
   }

   private class MyComponentListener extends ComponentAdapter {

      @Override
      public void componentMoved(ComponentEvent compEvt) {
         compEvtOccurred(compEvt);
      }

      @Override
      public void componentResized(ComponentEvent compEvt) {
         compEvtOccurred(compEvt);
      }

      private void compEvtOccurred(ComponentEvent compEvt) {
         Point newViewLocOnScn = viewportView.getLocationOnScreen();
         if (viewLocOnScrn != null && !newViewLocOnScn.equals(viewLocOnScrn)) {
            Point scrollPaneLocOnScrn = scrollpane.getLocationOnScreen();
            int x = scrollPaneLocOnScrn.x - viewLocOnScrn.x;
            int y = scrollPaneLocOnScrn.y - viewLocOnScrn.y;
            viewport.setViewPosition(new Point(x, y));
         }
      }

   }

   private class MyMouseAdapter extends MouseAdapter {
      private Point delta;

      @Override
      public void mousePressed(MouseEvent mEvt) {
         Point mousePt = mEvt.getPoint();
         Point viewPos = viewport.getViewPosition();
         delta = new Point(viewPos.x + mousePt.x, viewPos.y + mousePt.y);
      }

      @Override
      public void mouseDragged(MouseEvent e) {
         if (delta ==  null ) {
            return;
         }

         Point p = e.getPoint();
         viewport.setViewPosition(new Point(delta.x - p.x, delta.y - p.y));
         viewLocOnScrn = viewportView.getLocationOnScreen();
      }

      @Override
      public void mouseReleased(MouseEvent e) {
         delta = null;
      }
   }

   private class LoadImageAction extends AbstractAction {
      public LoadImageAction(String text) {
         super(text);
      }

      @Override
      public void actionPerformed(ActionEvent actEvt) {
         JFileChooser jfc = new JFileChooser("img003.jpg");
         int jfcResult = jfc.showOpenDialog(ImageViewer2.this);
         if (jfcResult == JFileChooser.APPROVE_OPTION) {
            File file = jfc.getSelectedFile();
            try {
               BufferedImage bufImg = ImageIO.read(file);
               ImageIcon icon = new ImageIcon(bufImg);
               viewportView.setIcon(icon);
               Dimension vpSize = viewport.getSize();
               int vpX = (bufImg.getWidth() - vpSize.width) / 2;
               int vpY = (bufImg.getHeight() - vpSize.height) / 2;
               Point viewPos = new Point(vpX, vpY);
               viewport.setViewPosition(viewPos);
               viewLocOnScrn = viewportView.getLocationOnScreen();
            } catch (IOException e) {
               e.printStackTrace();
            }

         }

      }
   }

   private static void createAndShowGui() {
      ImageViewer2 mainPanel = new ImageViewer2();

      JFrame frame = new JFrame("Image Viewer 2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

欢迎评论和批评!

于 2012-05-24T04:45:26.277 回答