简体   繁体   English

当我在 JScrollPane 中滚动水平和垂直滚动条时未调用paintComponent

[英]paintComponent not called when I scroll both Horizontal and Vertical scrollbars in a JScrollPane

I am having trouble with Swing and the JScrollPane.我在使用 Swing 和 JScrollPane 时遇到问题。 I am having a strange behaviour.我有一个奇怪的行为。

I extended JScrollPane .我扩展JScrollPane I display an image in it and draw rectangles over it to define areas.我在其中显示图像并在其上绘制矩形以定义区域。 With a big image, I have an Horizontal and a Vertical scrollbars.使用大图像,我有一个水平滚动条和一个垂直滚动条。

I - ok - When I move one scrollbar or the other I see my image move too as it should.我 - 好的 - 当我移动一个滚动条或另一个滚动条时,我看到我的图像也会移动。 II - not ok - When I move one scrollbar an leave it in between max and min position, then when I move my second scrollbar my image disappears. II - 不好 - 当我移动一个滚动条并将其保留在最大和最小 position 之间时,当我移动第二个滚动条时,我的图像就会消失。

With some debug prints, I found out that paintComponent , is not called when in case II.通过一些调试打印,我发现在情况 II 时不调用paintComponent

I would like to know why it is not calling paintComponent and how I can fix it.我想知道为什么它不调用paintComponent以及如何修复它。

Here below is my class:下面是我的 class:

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;
    }
}

EDIT: Bellow is the class (simplified) that uses DrawingPanel so you can have a reproducible exemple.编辑:波纹管是使用 DrawingPanel 的 class (简化),因此您可以获得可重现的示例。

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

Here's a simplified example of a drawing JPanel that's larger than the scrolling JPanel .这是一个比滚动JPanel大的绘图JPanel的简化示例。

大型绘图面板

You can see in the image that the drawing JPanel is larger both horizontally and vertically than the scrolling JPanel .您可以在图像中看到绘图JPanel在水平和垂直方向上都比滚动JPanel大。

I don't call the paintComponent method at all in this code.在这段代码中,我根本没有调用paintComponent方法。 Because I set up the GUI properly, Swing itself calls the repaint method when you move the scroll bars.因为我正确设置了 GUI,所以当您移动滚动条时,Swing 本身会调用repaint方法。

Here are the important things I did.以下是我所做的重要事情。

  1. I started the Swing GUI with a call to the SwingUtilities invokeLater method.我通过调用SwingUtilities invokeLater方法启动了 Swing GUI。 This method makes sure that the Swing components are created and executed on the Event Dispatch Thread .此方法确保在Event Dispatch Thread上创建和执行 Swing 组件。

  2. I used a JFrame , two JPanels , and a JScrollPane .我使用了JFrame 、两个JPanels和一个JScrollPane I extended JPanel to create the drawing panel.我扩展了JPanel来创建绘图面板。 I used a JScrollPane , JPanel , and JFrame .我使用了JScrollPaneJPanelJFrame The only time you extend a Swing component, or any Java class, is when you want to override one or more class methods.唯一一次扩展 Swing 组件或任何 Java class 时,是您想要覆盖一个或多个 ZBCBA2F2ED4F8EB4C21 方法时。

  3. I used Swing layout managers .我使用了 Swing 布局管理器 I used a BorderLayout for the JFrame and scrolling JPanel .我为JFrame和滚动JPanel使用了BorderLayout

Here's the complete runnable code.这是完整的可运行代码。 Why, you can even call it a minimal reproducible example!为什么,您甚至可以称其为最小的可重现示例!

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;
            }
        }
        
    }
    
}

Edited to add: The OP posted additional questions in a comment.编辑添加:OP 在评论中发布了其他问题。

Thanks for the example but it doesn't show me how to print an image in a JPanel .感谢您的示例,但它没有告诉我如何在JPanel中打印图像。

Pretty simple.很简单。 Read an image using the ImageIO class, save the image in an application model consisting of one or more plain Java getter / setter classes and use the Graphics drawImage method to draw the image.使用ImageIO class 读取图像,将图像保存在应用程序 model 中,该应用程序由一个或多个普通 Java getter / setter 类组成,并使用Graphics drawImage方法。

Your preview has only one scrollbar moved not both - which is my problem.您的预览只移动了一个滚动条,而不是两者都移动 - 这是我的问题。

Did you actually run the code I provided?你真的运行了我提供的代码吗? I can only move one scrollbar at a time.我一次只能移动一个滚动条。 The drawing JPanel extends both horizontally and vertically.绘图JPanel水平和垂直扩展。

And it doesn't explain why my example doesn't work.它并没有解释为什么我的例子不起作用。

Your example is riddled with errors.你的例子充满了错误。 Start over, building a Swing application one Swing component at a time using sound principles.重新开始,使用合理的原则一次构建一个 Swing 应用程序一个 Swing 组件。 The Oracle tutorial, Creating a GUI With JFC/Swing , will show you the correct way to create a Swing application. Oracle 教程,使用 JFC/Swing 创建 GUI ,将向您展示创建 Swing 应用程序的正确方法。 You can skip the Netbeans section.您可以跳过 Netbeans 部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM