简体   繁体   中英

JPanel inside JScrollPane with graphics glitches while scrolling

So i made a simple graphic GUI where the rigth part is a JScrollPane with a JPanel (canvas) inside (mainGUI class). When I try to draw some box (using Box class) and run it, when scrolling i get this repeating windowsXP graphic error, where the boxes kind of render too slow or something, kind of dificult to explain. How can I get an uniform non-glitching drawing after scrolling? Is there a rendering option or something?

Code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;


public class Grafin{
    public static void main(String args[]){
        mainGUI gui = new mainGUI();
    }
}

class mainGUI{
    private JFrame mainFrame;
    private JPanel toolsPanel;
    private JPanel canvasPanel;

    public mainGUI(){
        try{
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
        }catch(Exception e){
            System.out.println(e.getMessage());
        }

        mainFrame = new JFrame("Grafin | untitled");
        mainFrame.setLayout(new GridBagLayout());
        mainFrame.setSize(920, 580);
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.setLocationRelativeTo(null);
        mainFrame.setVisible(true);     

        loadGUI();
    }

    public void loadGUI(){
        GridBagConstraints c = new GridBagConstraints();

        //toolsPanel (izq) y canvasPanel (der) dentro de mainFrame
        toolsPanel = new JPanel(new GridBagLayout());
        toolsPanel.setPreferredSize(new Dimension(250,580));

        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 0.1;
        c.weighty = 1;
        c.fill = GridBagConstraints.BOTH;
        mainFrame.add(toolsPanel, c);


        canvasPanel = new JPanel(new GridBagLayout());
        canvasPanel.setBackground(Color.white);
        canvasPanel.setPreferredSize(new Dimension(1500,1000));

        JScrollPane canvasScroll = new JScrollPane(canvasPanel);
        canvasScroll.setPreferredSize(new Dimension(670,580));

        c.gridx = 1;
        c.gridy = 0;
        c.weightx = 0.9;
        mainFrame.add(canvasScroll, c);
        canvasScroll.setSize(canvasScroll.getPreferredSize());
        canvasScroll.setBackground(Color.white);

        //CanvasPanel:
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 1;
        c.weighty = 1;

        Box b1 = new Box(10, 200, 30, 128);
        Box b2 = new Box(200, 10, 120, 40);
        canvasPanel.add(b1, c);
        canvasPanel.add(b2, c);
        mainFrame.pack();
    }
}

    class Box extends JPanel{
    private Color borderColor;
    private Color fillColor;
    public int x;
    public int y;
    public int width;
    public int height;

    public Box(){
        borderColor = Color.black;
        fillColor = new Color(242, 242, 242);
        x = y = 1;
        width = height = 30;
    }

    public Box(int px, int py, int w, int h){
        x = px;
        y = py;
        borderColor = Color.black;
        fillColor = new Color(242, 242, 242);
        width = w;
        height = h; 
    }

    public void changeFillColor(Color c){
        fillColor = c;
    }

    public void changeBorderColor(Color c){
        borderColor = c;
    }

    public void paint(Graphics g){
        g.setColor(fillColor);
        g.fillRect(x, y, width, height);
        g.setColor(borderColor);
        g.drawRect(x, y, width, height);
    }
}

Thanks in advance Happy holidays

First and foremost, you're overriding the wrong painting method. You should not be overriding JPanel's paint method but rather its paintComponent method, since this is a more specific painting method and has double buffering by default, a plus when doing animation (which you're kind of doing with your scrolling).

Even more important is that you're not calling the super's painting method within your override, super.paintComponent(g) if overriding paintComponent, or super.paint(g) if (incorrectly) overriding paint. This prevents your JPanel from cleaning dirty pixels.

Other issues: You're adding two components to a GridBagLayout-using component, but are using the very same GridBagConstraints for both, something that you shouldn't be doing.

Also, your Box class should be a logical class and not a component class. In other words, it shouldn't extend any Swing GUI component, and should be drawn within the canvasPanel.

For example:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

@SuppressWarnings("serial")
public class Grafin2 extends JPanel {
    private static final int PREF_W = 920;
    private static final int PREF_H = 580;
    private static final Dimension TOOLS_SIZE = new Dimension(250, PREF_H);
    private static final Dimension CANVAS_SIZE = new Dimension(1500, 1000);
    private JPanel toolsPanel = new JPanel();
    private CanvasPanel canvasPanel = new CanvasPanel();

    public Grafin2() {
        MyBox box1 = new MyBox(10, 200, 30, 128);
        MyBox box2 = new MyBox(200, 10, 120, 40);

        box1.changeFillColor(new Color(255, 120, 120));
        box2.changeFillColor(new Color(220, 220, 255));

        canvasPanel.addMyBox(box1);
        canvasPanel.addMyBox(box2);

        toolsPanel.setPreferredSize(TOOLS_SIZE);
        canvasPanel.setBackground(Color.white);
        canvasPanel.setPreferredSize(CANVAS_SIZE);
        JScrollPane canvasScroll = new JScrollPane(canvasPanel);

        setLayout(new BorderLayout());
        add(toolsPanel, BorderLayout.LINE_START);
        add(canvasScroll, BorderLayout.CENTER);
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private class CanvasPanel extends JPanel {
        // a collection to hold all the boxes
        private List<MyBox> boxes = new ArrayList<>();

        public void addMyBox(MyBox myBox) {
            boxes.add(myBox);
        }

        @Override // again, this is the proper painting method
        protected void paintComponent(Graphics g) {
            super.paintComponent(g); // never forget this!
            for (MyBox myBox : boxes) {
                myBox.draw(g); // draw all the boxes that we hold
            }
        }
    }

    private static void createAndShowGui() {
        Grafin2 mainPanel = new Grafin2();

        JFrame frame = new JFrame("Grafin2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

// this is a logical class, **not** a component class.
class MyBox {
    private Color borderColor;
    private Color fillColor;
    public int x;
    public int y;
    public int width;
    public int height;

    public MyBox() {
        borderColor = Color.black;
        fillColor = new Color(242, 242, 242);
        x = y = 1;
        width = height = 30;
    }

    public MyBox(int px, int py, int w, int h) {
        x = px;
        y = py;
        borderColor = Color.black;
        fillColor = new Color(242, 242, 242);
        width = w;
        height = h;
    }

    public void changeFillColor(Color c) {
        fillColor = c;
    }

    public void changeBorderColor(Color c) {
        borderColor = c;
    }

    public void draw(Graphics g) {
        // no need to call a super method
        // because there is none for this class
        g.setColor(fillColor);
        g.fillRect(x, y, width, height);
        g.setColor(borderColor);
        g.drawRect(x, y, width, height);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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