当我在 JScrollPane 中滚动水平和垂直滚动条时,paintComponent 不会被调用

2024-02-25

我在使用 Swing 和 JScrollPane 时遇到问题。 我的行为很奇怪。

我延长了JScrollPane。我在其中显示图像并在其上绘制矩形以定义区域。 对于大图像,我有一个水平和一个垂直滚动条。

我 - 好吧 - 当我移动一个或另一个滚动条时,我看到我的图像也按其应有的方式移动。 II - 不好 - 当我移动一个滚动条并将其保留在最大和最小位置之间时,然后当我移动第二个滚动条时,我的图像消失。

通过一些调试打印,我发现paintComponent,在情况 II 中不会被调用。

我想知道为什么它不打电话paintComponent以及我该如何解决它。

下面是我的课程:

package GUI;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import javax.swing.JScrollPane;

public class DrawingPanel extends JScrollPane {

    private static final long serialVersionUID = 1L;
    private static final Color DRAWING_COLOR = new Color(255, 100, 200);
    private static final Color FINAL_DRAWING_COLOR = Color.red;
    private static final double ZOOMING_STEP = 1.1;

    private Image sImg;
    private Point startPt;
    private Point endPt;
    private Point currentPt;

    private int prefW;
    private int prefH;
    private double zoomFactor = 1;
    private boolean zoomer    = false;
    private boolean loaded    = false;

    public DrawingPanel() {
        setFocusable(true);
        setFocusTraversalKeysEnabled(false);
    }

    public void loadImage(Image img) {
        sImg       = img;
        prefW      = sImg.getWidth(null);
        prefH      = sImg.getHeight(null);
        zoomFactor = getSize().getWidth() / prefW;
        zoomer     = true;
        loaded     = true;

        repaint();
        revalidate();
    }

    int countPaint = 0;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        System.out.println("paintComponent " + countPaint);

        if (loaded) {
            int zoomWidth  = (int) (prefW * zoomFactor);
            int zoomHeight = (int) (prefH * zoomFactor);

            if (zoomer) {
                ((Graphics2D) g).scale(zoomFactor, zoomFactor);
                setSize(zoomWidth, zoomHeight);
                zoomer = false;
            }

            g.drawImage(sImg, 0, 0, zoomWidth, zoomHeight, null);
            drawRectangle(g);
        }

        g.dispose();
        countPaint++;
    }

    @Override
    public Dimension getPreferredSize() {
        return loaded ?
            this.getSize() :
            new Dimension((int) (prefW*zoomFactor), (int) (prefH*zoomFactor));
    }

    private void drawRectangle(Graphics g) {
        Point secondPoint = (currentPt != null) ? currentPt : endPt;
        Color color       = (currentPt != null) ? DRAWING_COLOR : FINAL_DRAWING_COLOR;

        if (startPt!=null && secondPoint!=null) {
            int x = Math.min(startPt.x, secondPoint.x);
            int y = Math.min(startPt.y, secondPoint.y);
            int rectangleWidth = Math.abs(startPt.x - secondPoint.x);
            int rectangleHeight = Math.abs(startPt.y - secondPoint.y);

            g.setColor(color);
            g.drawRect(x, y, rectangleWidth, rectangleHeight);
        }
    }

    public void deleteRectangle(){
        startPt = null;
        endPt   = null;
    }

    public void increaseZoom(Point p) {
        double oldZoom = zoomFactor;
        zoomFactor    *= ZOOMING_STEP;

        repositonPointAfterZoom(oldZoom, zoomFactor);
    }

    public void decreaseZoom(Point p) {
        double oldZoom = zoomFactor;
        zoomFactor    /= ZOOMING_STEP;

        repositonPointAfterZoom(oldZoom, zoomFactor);
    }

    public void repositonPointAfterZoom(double oldZoom, double newZoom) {
        double evolution = newZoom/oldZoom;

        if (startPt!=null) {
            startPt.setLocation(startPt.x * evolution, startPt.y * evolution);
        }

        if (endPt!=null) {
            endPt.setLocation(endPt.x * evolution, endPt.y * evolution);
        }

        repaint();
    }

    // Getter et setter

    public void setStartPt(Point startPt) {
        this.startPt = startPt;
    }

    public void setEndPt(Point endPt) {
        this.endPt = endPt;
    }

    public void setCurrentPt(Point currentPt) {
        this.currentPt = currentPt;
    }

    public int getZoomCalculateX(int value){
        return (int) (value / zoomFactor);
    }

    public int getZoomCalculateY(int value){
        return (int) (value / zoomFactor);
    }

    public void setZoomer(boolean zoomer) {
        this.zoomer = zoomer;
    }
}

编辑:波纹管是使用 DrawingPanel 的类(简化),因此您可以有一个可重现的示例。

package GUI;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import fileHandler.*;

public class GUI {

    private JFrame frame;
    private MenuBar menubar;
    private DrawingPanel panelImage;
    private JScrollPane scroll;
    private GroundTruth openFile;
    private int[] panelImageDown = new int[2];
    private int[] panelImageUp   = new int[2];
    private Menu CoordinateMenu1 = new Menu();
    private Menu CoordinateMenu2 = new Menu();
    private int actualPagePdf;
    private PDFRenderer renderer;

    public static void main(String[] args) throws IOException {
        new GUI();
    }

    public GUI() throws IOException {
        JFrame frame = CreateFrame();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private JFrame CreateFrame() throws IOException {
        frame = new JFrame();

        frame.setMenuBar(CreateMenuBar());
        frame.setContentPane(SplitScreen());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("GTA - Ground Truth Annotator");
        frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);

        return frame;
    }

    private MenuBar CreateMenuBar() {
        menubar = new MenuBar();

        menubar.add(CreateFileMenu());
        menubar.add(new Menu("Selection coordinates:"));
        menubar.add(CoordinateMenu1);
        menubar.add(new Menu("Width/Height:"));
        menubar.add(CoordinateMenu2);

        return menubar;
    }

    private Menu CreateFileMenu() {
        Menu mFile = new Menu("File");
        MenuItem miOpenImage = new MenuItem("Open Image/PDF File");
        mFile.add(miOpenImage);
        miOpenImage.addActionListener(OpenFileActionListener);
        mFile.addSeparator();
        MenuItem miExit = new MenuItem("Exit Program");
        mFile.add(miExit);
        miExit.addActionListener(ExitActionListener);

        return mFile;
    }

    private JPanel SplitScreen() throws IOException {
        JPanel splittedScreen = new JPanel(new GridLayout(1, 2));

        splittedScreen.add(CreateLeftPanel());
        splittedScreen.add(CreateRightPanel());

        return splittedScreen;
    }

    private JLayeredPane CreateLeftPanel() throws IOException {
        JLayeredPane panel = new JLayeredPane();

        panel.setLayout(new BorderLayout());
        panel.setBorder(BorderFactory.createLineBorder(Color.gray));
        panel.add(CreateImageScrollPane());

        return panel;
    }

    private JScrollPane CreateImageScrollPane() throws IOException {
        scroll  = new JScrollPane(CreateImagePanel((String) null));

        scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        scroll.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE);

        return scroll;
    }

    private DrawingPanel CreateImagePanel(String path) throws IOException {
        if (panelImage == null) {
            panelImage = new DrawingPanel();
        }

        if (path != null) {
            panelImage.loadImage(ImageIO.read(new File(path)));
        }

        panelImage.addMouseListener(PanelImageMouseListener);
        panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
        panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
        panelImage.setOpaque(false);
        panelImage.revalidate();
        panelImage.repaint();
        panelImage.requestFocus();

        return panelImage;
    }

    private DrawingPanel CreateImagePanel(Image image) throws IOException {
        if (panelImage == null) {
            panelImage = new DrawingPanel();
        }

        panelImage.loadImage(image);
        panelImage.addMouseListener(PanelImageMouseListener);
        panelImage.addMouseWheelListener(PanelImageMouseWheelListener);
        panelImage.addMouseMotionListener(PanelImageMouseMotionAdapter);
        panelImage.setOpaque(false);
        panelImage.revalidate();
        panelImage.repaint();

        return panelImage;
    }

    private JPanel CreateRightPanel() {
        JPanel panel = new JPanel(new GridLayout(0, 1));

        //...

        return panel;
    }

    ActionListener OpenFileActionListener = new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
           OpenFile();
        }
    };

    ActionListener ExitActionListener = new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            Object[] options = {"Yes, quit now", "No, go back"};
            int n = JOptionPane.showOptionDialog(
                    frame, "ATTENTION: closing without saving will cause any unsaved files to be lost. Do you want to proceed?",
                    "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]
            );

            switch (n) {
                case JOptionPane.YES_OPTION:
                    System.exit(0);
                case JOptionPane.NO_OPTION:

                case JOptionPane.CANCEL_OPTION:
            }
        }

    };

    MouseListener PanelImageMouseListener = new MouseListener() {
        public void mousePressed(MouseEvent me) {
            panelImageDown = new int[]{
                    panelImage.getZoomCalculateX(me.getX()), panelImage.getZoomCalculateY(me.getY())
            };
            panelImageUp = null;

            CoordinateMenu1.setLabel(String.format("%s:%s", panelImageDown[0],  panelImageDown[1]));
            CoordinateMenu2.setLabel("");
            panelImage.setStartPt(me.getPoint());
            panelImage.repaint();
        }

        public void mouseReleased(MouseEvent me) {
            panelImageUp = new int[]{
                    Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
                    Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
            };

            CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
            panelImage.setEndPt(me.getPoint());
            panelImage.setCurrentPt(null);
            panelImage.repaint();
        }

        public void mouseClicked(MouseEvent arg0) {
        }

        public void mouseEntered(MouseEvent arg0) {
        }

        public void mouseExited(MouseEvent arg0) {
        }
    };


    MouseMotionAdapter PanelImageMouseMotionAdapter = new MouseMotionAdapter() {
        @Override
        public void mouseDragged(MouseEvent me) {
            panelImageUp = new int[]{
                    Math.abs(panelImage.getZoomCalculateX(me.getX()) - panelImageDown[0]),
                    Math.abs(panelImageDown[1] - panelImage.getZoomCalculateY(me.getY()))
            };

            CoordinateMenu2.setLabel(String.format("%s:%s", panelImageUp[0], panelImageUp[1]));
            panelImage.setCurrentPt(me.getPoint());
            panelImage.repaint();
        }
    };

    MouseWheelListener PanelImageMouseWheelListener = new MouseWheelListener() {
        public void mouseWheelMoved(MouseWheelEvent me) {
            if (me.isAltDown()) {
                if (me.getWheelRotation() < 0) {
                    panelImage.setZoomer(true);
                    panelImage.increaseZoom();
                    panelImage.repaint();
                    panelImage.requestFocus();
                    //scroll.repaint();
                //Zoom out
                } else if(me.getWheelRotation() > 0) {
                    panelImage.setZoomer(true);
                    panelImage.decreaseZoom();
                    panelImage.repaint();
                    panelImage.requestFocus();
                    //scroll.repaint();
                }
            }
        }
    };

    private void OpenFile() {
        openFile                 = new GroundTruth();
        JFileChooser fileChooser = new JFileChooser();

        fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
        fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        fileChooser.setAcceptAllFileFilterUsed(false);
        fileChooser.addChoosableFileFilter(new FileNameExtensionFilter(
                "Images / PDF Scan",
                "bmp", "jpeg", "jpg", "png", "tif", "tiff", "pdf"
        ));

        if (fileChooser.showOpenDialog(frame) != 0) {
            return;
        }

        openFile.setFilename(fileChooser.getSelectedFile().getAbsolutePath());

        if (getExtension(fileChooser.getSelectedFile().getName()).equals("pdf")) {
            try {
                PDDocument doc = PDDocument.load(fileChooser.getSelectedFile());
                numberPagePdf  = doc.getNumberOfPages();
                actualPagePdf  = 0;
                renderer       = new PDFRenderer(doc);

                setPdfPage(actualPagePdf);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            try {
                CreateImagePanel(fileChooser.getSelectedFile().getAbsolutePath());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void setPdfPage(int pageNumber){
        try {
            BufferedImage bim = renderer.renderImageWithDPI(pageNumber, 300);

            refreshInfoPageSection();
            CreateImagePanel(bim);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // refresh the label who indicate the page display and set visible or inivisble the previous and next button
    private void refreshInfoPageSection(){
        panelImage.deleteRectangle();
    }

    public String getExtension(String filename) {
        return FilenameUtils.getExtension(filename);
    }
}

这是一个简化的绘图示例JPanel比滚动更大JPanel.

您可以在图像中看到绘图JPanel水平和垂直方向都比滚动条大JPanel.

我不打电话给paintComponent这段代码中根本没有方法。因为我正确设置了 GUI,所以 Swing 本身调用了repaint移动滚动条时的方法。

以下是我所做的重要事情。

  1. 我通过调用来启动 Swing GUISwingUtilities invokeLater方法。该方法确保 Swing 组件在事件调度线程 https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html.

  2. 我用了一个JFrame, two JPanels, and a JScrollPane。我延长了JPanel创建绘图面板。我用了一个JScrollPane, JPanel, and JFrame。扩展 Swing 组件或任何 Java 类的唯一时间是当您想要重写一个或多个类方法时。

  3. I used Swing 布局管理器 https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html。我用了一个BorderLayout为了JFrame并滚动JPanel.

这是完整的可运行代码。为什么,您甚至可以将其称为最小的可复制示例!

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class LargeDrawingPanel implements Runnable {

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

    @Override
    public void run() {
        JFrame frame = new JFrame("Large Drawing Panel");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.add(createMainPanel(), BorderLayout.CENTER);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
    
    private JPanel createMainPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        panel.setPreferredSize(new Dimension(400, 400));
        
        JScrollPane scrollPane = new JScrollPane(new DrawingPanel());
        panel.add(scrollPane, BorderLayout.CENTER);
        
        return panel;
    }

    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        
        public DrawingPanel() {
            this.setBackground(Color.WHITE);
            this.setPreferredSize(new Dimension(2000, 2000));
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            
            g.setColor(Color.BLUE);
            int x = 100;
            int y = 100;
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 10; j++) {
                    g.fillRect(x, y, 100, 100);
                    x += 200;
                }
                y += 200;
                x = 100;
            }
        }
        
    }
    
}

编辑添加:OP 在评论中发布了其他问题。

感谢您提供的示例,但它没有告诉我如何在中打印图像 AJPanel.

很简单。使用读取图像ImageIO类,将图像保存在由一个或多个普通 Java getter/setter 类组成的应用程序模型中,并使用Graphics drawImage方法来绘制图像。

你的预览只移动了一个滚动条,而不是同时移动了两个 - 这是我的 问题。

您实际上运行了我提供的代码吗?我一次只能移动一个滚动条。绘图JPanel水平和垂直延伸。

它并没有解释为什么我的例子不起作用。

你的例子充满了错误。重新开始,使用合理的原则一次构建一个 Swing 组件的 Swing 应用程序。 Oracle 教程,使用 JFC/Swing 创建 GUI https://docs.oracle.com/javase/tutorial/uiswing/index.html,将向您展示创建 Swing 应用程序的正确方法。您可以跳过 Netbeans 部分。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当我在 JScrollPane 中滚动水平和垂直滚动条时,paintComponent 不会被调用 的相关文章

随机推荐