JPanel with a grid drawn on it, which leads to high CPU usage when scrolling

I have a JSrollPane with JPanel as a component of ViewPort. In this JPanel, I use paintComponent to draw a 64x64px square grid. JPanel is quite large, 28'672px at 14'336px, and yet the grid is drawn instantly and everything seems beautiful. The problem is that scrolling vertically or horizontally causes the CPU usage to increase very much, the faster I scroll higher. When scrolling, the processor load increases to 35-50%. Scrolling through a JPanel of the same size without a grid drawn on it uses a very small processor, so the grid is certainly the cause of the problem. This grid is the most basic part of what I plan to do inside scrollpane, if it works poorly now, I am afraid that it will be unusable after adding more content.

My question is Why does he use so much CPU to scroll this grid, is the grid redrawn every time the scroll bar is moved? Is there a better or more efficient way to draw a scrollable mesh?

I had the idea of ​​only drawing the grid of the visible area (by coordinate), and then redrawing this visible area while moving the scroll bars, but this will cause the alot to redraw. If possible, I would like to draw the entire grid at startup, and then only redraw on command.

Here is an example of how my JPanel mesh works on barebones.

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

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.EmptyBorder;

public class GridTest extends JFrame
{
    static JScrollPane scrollPane;
    static JPanel contentPane,gridPane;

    public static void main(String[] args) {
        GridTest frame = new GridTest();
        frame.setVisible(true);
    }

    public GridTest(){
        setTitle("Grid Test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        setBounds(300, 100, 531, 483);

        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        scrollPane = new JScrollPane();
        scrollPane.setBounds(0, 0, 526, 452);
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        contentPane.add(scrollPane);
        gridPane = new JPanel() {
        public void paintComponent( Graphics g ){
            super.paintComponent(g);
            drawGrid(g);
            g.dispose();
         }};
        Dimension gridPaneSize = new Dimension(28672,14336);
        //Dimension gridPaneSize = new Dimension(4096,4096);
    gridPane.setBackground(Color.BLACK);
    gridPane.setPreferredSize(gridPaneSize);
    scrollPane.setViewportView(gridPane);
    }
    public static void drawGrid(Graphics g)
    {
        int width = gridPane.getWidth();
        int height = gridPane.getHeight();

        g.setColor(Color.gray);
        // draw horizontal long lines
        for(int h = 0; h < height; h+=64){
            g.drawLine(0, h, width, h);
        }
        // draw even grid vert lines
        for(int w = 0; w < width; w+=64){
            for(int h = 0; h < height; h+=128){
                g.drawLine(w, h, w, h+64);
            }
        }
        // draw odd grid vert lines
        for(int w = 32; w < width; w+=64){
            for(int h = 64; h < height; h+=128){
                g.drawLine(w, h, w, h+64);
            }
        }
    }
}

EDIT: An updated / fixed version of this code is given below in my answer to the question.

+5
source share
2 answers

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.*;

public class TilePainter extends JPanel implements Scrollable {

    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame("Tiles");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(new TilePainter()));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
    private final int TILE_SIZE = 50;
    private final int TILE_COUNT = 100;
    private final int visibleTiles = 10;
    private final boolean[][] loaded;
    private final boolean[][] loading;
    private final Random random;

    public TilePainter() {
        setPreferredSize(new Dimension(TILE_SIZE * TILE_COUNT, TILE_SIZE * TILE_COUNT));
        loaded = new boolean[TILE_COUNT][TILE_COUNT];
        loading = new boolean[TILE_COUNT][TILE_COUNT];
        random = new Random();
    }

    public boolean getTile(final int x, final int y) {
        boolean canPaint = loaded[x][y];
        if (!canPaint && !loading[x][y]) {
            loading[x][y] = true;
            Timer timer = new Timer(random.nextInt(500),
                    new ActionListener() {

                        @Override
                        public void actionPerformed(ActionEvent e) {
                            loaded[x][y] = true;
                            repaint(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
                        }
                    });
            timer.setRepeats(false);
            timer.start();
        }
        return canPaint;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Rectangle clip = g.getClipBounds();
        int startX = clip.x - (clip.x % TILE_SIZE);
        int startY = clip.y - (clip.y % TILE_SIZE);
        for (int x = startX; x < clip.x + clip.width; x += TILE_SIZE) {
            for (int y = startY; y < clip.y + clip.height; y += TILE_SIZE) {
                if (getTile(x / TILE_SIZE, y / TILE_SIZE)) {
                    g.setColor(Color.GREEN);
                } else {
                    g.setColor(Color.RED);
                }
                g.fillRect(x, y, TILE_SIZE - 1, TILE_SIZE - 1);
            }
        }
    }

    @Override
    public Dimension getPreferredScrollableViewportSize() {
        return new Dimension(visibleTiles * TILE_SIZE, visibleTiles * TILE_SIZE);
    }

    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        return TILE_SIZE * Math.max(1, visibleTiles - 1);
    }

    @Override
    public boolean getScrollableTracksViewportHeight() {
        return false;
    }

    @Override
    public boolean getScrollableTracksViewportWidth() {
        return false;
    }

    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        return TILE_SIZE;
    }
}

Rectagle.intersects(Rectagle) (HFOE ) Encephalopathic old.sun.forum57

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class IsRectVisible {

    private static void createAndShowUI() {
        JFrame frame = new JFrame("IsRectVisible");
        frame.getContentPane().add(new IsRectVisibleGui());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowUI();
            }
        });
    }
}

class IsRectVisibleGui extends JPanel {

    public static final Rectangle RECT = new Rectangle(250, 200, 100, 100);
    public static final Dimension INNER_PANEL_SIZE = new Dimension(600, 800);
    private static final Dimension SCROLLPANE_SIZE = new Dimension(250, 300);
    private static final String NOT_VISIBLE = "Not Visible";
    private static final String VISIBLE = "Visible";
    private static final long serialVersionUID = 1L;
    private InnerPanel innerPanel = new InnerPanel();
    private JViewport viewport = new JViewport();
    private JLabel statusLabel = new JLabel(NOT_VISIBLE);

    IsRectVisibleGui() {
        JScrollPane scrollpane = new JScrollPane();
        scrollpane.setViewport(viewport);
        viewport.add(innerPanel);
        scrollpane.setPreferredSize(SCROLLPANE_SIZE);
        viewport.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                Rectangle viewRect = viewport.getViewRect();
                if (viewRect.intersects(RECT)) {
                    statusLabel.setText(VISIBLE);
                } else {
                    statusLabel.setText(NOT_VISIBLE);
                }
            }
        });
        setLayout(new BorderLayout());
        add(scrollpane, BorderLayout.CENTER);
        add(statusLabel, BorderLayout.SOUTH);
    }

    class InnerPanel extends JPanel {

        private static final long serialVersionUID = 1L;

        InnerPanel() {
            setPreferredSize(INNER_PANEL_SIZE);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.red);
            g2.setStroke(new BasicStroke(4));
            g2.draw(RECT);
        }
    }
}
+4

mKorbel , , getViewRect().

, - , , , , x/y ( ) (1024x1024) . , , x/y / +1 (1024). 5% CPU, .

JPanel:

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

public class GridTest extends JFrame
{
    private static final long serialVersionUID = 6632092242560855625L;
    static JPanel gridPane;
    static JViewport view;

    public static void main(String[] args) {
        GridTest frame = new GridTest();
        frame.setVisible(true);
    }

    public GridTest(){
        setTitle("Grid Test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(600,600);
        setLocationRelativeTo(null);
        setLayout(new BorderLayout());

        JScrollPane scrollPane = new JScrollPane();
        setContentPane(scrollPane);
        view = scrollPane.getViewport();
        gridPane = new JPanel() {
            private static final long serialVersionUID = 2900962087641689502L;
            public void paintComponent( Graphics g ){
            super.paintComponent(g);
            drawGrid(g, view.getViewRect());
         }};
        Dimension paneSize = new Dimension(28672,14336);
        gridPane.setPreferredSize(paneSize);
        gridPane.setBackground(Color.gray);
        scrollPane.setViewportView(gridPane);
    }

    static void drawGrid(Graphics g, Rectangle view){
        int wMax = gridPane.getWidth();
        int hMax = gridPane.getHeight();

        g.setColor(Color.black);
        Rectangle tile = view;
        // set corner tile x/y to the tile increment.
        for(int w = 0; w < wMax; w+= 1024)
        {
            if(tile.x >= w && tile.x < w+1024) { tile.x = (w); }
            for(int h = 0; h < hMax; h+= 1024)
            {
                if(tile.y >= h && tile.y < h+1024) { tile.y = (h); }
            }
        }
        int xTop = tile.x;
        int yTop = tile.y;
        int width = (int) tile.getWidth();
        int height = (int) tile.getHeight();
        width = xTop + width;
        height = yTop + height;
        // Draw even grid squares within visible tiles, starting at top corner tile.
        for(int w = xTop; w < width+1024; w+=64)
        {
            for(int h = yTop; h < height+1024; h+=128)
            {
                g.fillRect(w+1, h+1, 63, 63);
            }
        }
        // Draw odd grid squares within visible tiles, starting at top corner tile.
        for(int w = xTop-32; w < width+1024; w+=64)
        {
            for(int h = yTop+64; h < height+1024; h+=128)
            {
                g.fillRect(w+1, h+1, 63, 63);
            }
        }
    }
}
+1

All Articles