【问题标题】:paintComponent not called when I scroll both Horizontal and Vertical scrollbars in a JScrollPane当我在 JScrollPane 中滚动水平和垂直滚动条时未调用paintComponent
【发布时间】:2021-02-18 15:52:50
【问题描述】:

我在使用 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;
    }
}

编辑:Bellow 是使用 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);
    }
}

【问题讨论】:

  • 不确定为什么要扩展 JScrollPane 而不是 JPanel。发布正确的minimal reproducible example 来证明问题。
  • 我不是赌徒,但如果是,我的钱会花在 “我扩展了JScrollPane。” 上。 为什么你认为你需要扩展这个对成千上万其他人完美工作的类?
  • @Andrew Thompson - 这并没有说明它为什么会这样。如果我不管理paintComponent,我怎么能在里面绘制矩形?
  • @camickr - 当我想使用 JScrollPane 时,我为什么要使用 JPanel?
  • 一个面板被添加到一个滚动面板。如果您想做自定义绘画,您可以在面板上进行自定义绘画。您发布了一个应用程序,而不是 minimal reproducible example。 “MRE”中的第一个词是最小的。您也在使用 3rd 方 API,所以不,我无法测试您的代码以查看您在做什么。

标签: java swing jscrollpane paintcomponent repaint


【解决方案1】:

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

您可以在图像中看到JPanel 的绘图在水平和垂直方向上都比滚动的JPanel 更大。

我在这段代码中根本没有调用paintComponent 方法。因为我正确设置了 GUI,所以当您移动滚动条时,Swing 本身会调用repaint 方法。

这是我所做的重要事情。

  1. 我通过调用 SwingUtilities invokeLater 方法启动了 Swing GUI。此方法确保在 Event Dispatch Thread 上创建和执行 Swing 组件。

  2. 我使用了一个JFrame、两个JPanels 和一个JScrollPane。我扩展了JPanel 来创建绘图面板。我使用了JScrollPaneJPanelJFrame。扩展 Swing 组件或任何 Java 类的唯一时间是当您想要覆盖一个或多个类方法时。

  3. 我使用了Swing layout managers。我使用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 在评论中发布了其他问题。

感谢您的示例,但它没有告诉我如何打印图像 JPanel

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

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

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

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

您的示例充满了错误。重新开始,使用合理的原则一次构建一个 Swing 应用程序。 Oracle 教程Creating a GUI With JFC/Swing 将向您展示创建 Swing 应用程序的正确方法。您可以跳过 Netbeans 部分。

【讨论】:

  • 感谢您的示例,但它没有告诉我如何在 JPanel 中打印图像。您的预览只移动了一个滚动条,而不是两者都移动 - 这是我的问题。它并没有解释为什么我的例子不起作用。我会继续尝试您的解决方案并四处寻找缺失的部分。
猜你喜欢
  • 2012-08-14
  • 2013-12-20
  • 2011-11-30
  • 2014-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-14
相关资源
最近更新 更多