5

ViewPort コンポーネントとして JPanel を持つ JSrollPane があります。この JPanel では、paintComponent を使用して 64x64px の正方形のグリッドを描画します。JPanel は 28,672 ピクセル x 14,336 ピクセルと非常に大きく、それでもグリッドは即座に描画され、すべて問題ないように見えます。問題は、縦または横にスクロールすると CPU 使用率が非常に高くなることです。スクロール中に CPU 使用率が最大 35 ~ 50% になります。グリッドを描画せずに同じサイズの JPanel をスクロールすると、CPU の使用量が非常に少なくなるため、グリッドが問題の原因であることは間違いありません。このグリッドは、スクロールペイン内で私が計画していることの最も基本的な部分です。現在パフォーマンスが悪い場合、さらに「コンテンツ」が追加された後に使用できなくなるのではないかと心配しています。

私の質問 このグリッドをスクロールするのに CPU を大量に使用するのはなぜですか? スクロールバーの位置が変わるたびにグリッドが再描画されますか? スクロール可能なグリッドを描画するためのより良い、またはより効率的な方法はありますか?

可視領域のグリッドのみを(座標で)描画し、スクロールバーが移動したときにその可視領域を再描画するという考えがありましたが、これは多くの再描画を呼び出すことになります。可能であれば、起動時にグリッド全体を描画し、コマンドでのみ再描画したいと思います。

これは、私の JPanel グリッドの最低限の作業例です。

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

編集:このコードの更新/修正バージョンは、質問に対する私の回答の下にあります。

4

2 に答える 2

4

例えば

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ここで)による素晴らしい例Encephalopathicold.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);
        }
    }
}
于 2013-03-02T18:40:10.157 に答える
1

上記の mKorbel の回答のおかげで、getViewRect() を使用してコードを修正できました。

将来、同様のことをしたい人のために、ビューポートの Rectangle を取得し、for ループを使用して、viewrect の x/y (上隅) がタイル (1024x1024) の増分内にあるかどうかを確認します。そうであれば、x/y インクリメント タイルからビューポートの幅/高さ +1 タイル (1024) までのグリッド スクエアを描画します。1 回の素早いスワイプで上から下にスクロールしても、許容できる CPU 使用率は約 5% のみです。

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);
            }
        }
    }
}
于 2013-03-03T18:20:17.380 に答える