调整 JPanel 大小时调用 Java drawImage()

标签 java swing

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

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

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

/***** 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);
    }
}

最佳答案

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

另见 this question .

关于调整 JPanel 大小时调用 Java drawImage(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10711636/

相关文章:

java - Swing 中的进度对话框

java - 为什么我的 spring mvc View 无法加载?

java - 从游标返回错误值

java - 如何在代码内激活 JButton ActionListener(单元测试目的)?

java - 在 Java Swing 程序中使用命令行参数

java - JComboBox[] 循环在 JApplet 上显示不起作用

java - 在 URL 中使用时区参数发送日期和时间的可接受方式

java - 删除两个字符之间的子字符串

java - 这个双重泛型返回类型 (public <T> TypedQuery<T>) 表示什么?

java Swing 背景图片