Search code examples
javaswingawtjpaneljscrollpane

JPanel&JScrollPane and error rendering


With my English is bad, but I'll try to explain. I have a question. At JPanel I draw the line .... they should go beyond the region of the object JPanel and JScrollPane theoretically should show the entire panel, but it is not updated and does not display all the lines that were released for the scope of the panel.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Console;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class TestFrame extends JFrame {

    static int speed = 10;
    static int floor = 22;
    public static void createGUI() {
        JFrame frame = new JFrame("Test frame");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final Font font = new Font("Verdana", Font.PLAIN, 25);

        JPanel butPanel = new JPanel();     

        JButton biginButton = new JButton("start");
        biginButton.setFont(font);
        biginButton.setFocusable(false);
        butPanel.add(biginButton);

        JButton remButton = new JButton("repaint");
        remButton.setFont(font);
        remButton.setFocusable(false);
        butPanel.add(remButton);

        final JPanel labPanel = new JPanel();
        final JScrollPane scrollPane = new JScrollPane(labPanel);
        labPanel.setLayout(new BoxLayout(labPanel, BoxLayout.Y_AXIS));

        biginButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                //labPanel.getGraphics().drawString("Ololo", 50, 50);
                int floH = 100;
                for(int i = 0; i < floor; i++){
                    labPanel.getGraphics().drawLine(0, i*floH, 300, i*floH);
                    labPanel.getGraphics().setColor(Color.RED);
                    scrollPane.revalidate();    
                }
            }
        });

        remButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                    labPanel.repaint();
                    scrollPane.revalidate();                                
            }           
        });

        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(butPanel, BorderLayout.NORTH);
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        frame.setPreferredSize(new Dimension(screenSize.width-10, screenSize.height-10));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame.setDefaultLookAndFeelDecorated(true);
                createGUI();
            }
        });
    }
}

Solution

  • I'm guessing that you're trying to draw to a JPanel and then expect that the JPanel should get larger to accommodate your drawing, and that not only is this not working for you, but your losing the drawing if you call repaint() on the JPanel.

    If so, then,

    • Just because you draw beyond the size of a JPanel or other JComponent will not automatically make that component larger since the component itself has no "knowledge" that you're drawing beyond its bounds. If you want to resize a component, you have to do this yourself.
    • It is best to have the JPanel set its own size by overriding its getPreferredSize() method. This will prevent other code from resetting its size to something else.
    • You do not want to get a component's graphics by calling getGraphics() on it. The Graphics object so obtained is short lasting and anything drawn will disappear as soon as the next time that paint(...) or paintComponent() is called. It's OK to call getGraphics() on a BufferedImage, but not on a Component or JComponent (unless you have a darn good reason for doing so and know how to protect against the Graphics object going null).
    • Override JPanel or some other JComponent and draw in the paintComponent(...) override method of this component. If you're using a BufferedImage, then you draw the BufferedImage in this paintComponent method.

    For example:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.image.BufferedImage;
    
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class TestDrawing extends JPanel {
       private static final int PREF_W = 800;
       private static final int PREF_H = 600;
       private DrawingPanel drawingPanel = new DrawingPanel();
    
       public TestDrawing() {
          JPanel northPanel = new JPanel();
          northPanel.add(new JButton(new AbstractAction("Draw Lines") {
    
             @Override
             public void actionPerformed(ActionEvent arg0) {
                drawingPanel.drawLines();
                drawingPanel.repaint();
             }
          }));
    
          setLayout(new BorderLayout());
          add(new JScrollPane(drawingPanel));
          add(northPanel, BorderLayout.PAGE_START);
       }
    
       @Override
       public Dimension getPreferredSize() {
          return new Dimension(PREF_W, PREF_H);
       }
    
       private static void createAndShowGui() {
          TestDrawing mainPanel = new TestDrawing();
    
          JFrame frame = new JFrame("TestDrawing");
          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();
             }
          });
       }
    }
    
    class DrawingPanel extends JPanel {
       private static final int BI_W = 500;
       private static final int FLOOR = 22;
       private static final int FLO_H = 100;
       private BufferedImage img = new BufferedImage(BI_W, FLO_H * FLOOR, BufferedImage.TYPE_INT_ARGB);
    
       public DrawingPanel() {
    
       }
    
       public void drawLines() {
    
          Graphics g = img.getGraphics();
          g.setColor(Color.black);
          for (int i = 0; i < FLOOR; i++) {
             g.drawLine(0, i * FLO_H, 300, i * FLO_H);
             g.setColor(Color.RED);
          }
          g.dispose();
       }
    
       @Override
       public Dimension getPreferredSize() {
          return new Dimension(BI_W, FLO_H * FLOOR);
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          if (img != null) {
             g.drawImage(img, 0, 0, this);
          }
       }
    }