我有一个带有JPanel的JSrollPane作为ViewPort组件。在这个JPanel上,我使用paintComponent绘制一个64x64px正方形的网格。 JPanel相当大,28'672px乘以14'336px,仍然可以立即绘制网格,一切看起来都很好。问题是垂直或水平滚动导致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);
}
}
}
}
编辑:在我对问题的回答中,此代码的更新/修订版本如下。
答案 0 :(得分:4)
绘制到JViewport中可见的对象
在所有情况下,您都可以确定if(Rectangle(viewport.getViewRect()).intersects(Re ctangleFromDesiredObject))
返回true
,如果是,则在Objects
JViewport
例如
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)
的{{1}} 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);
}
}
}
答案 1 :(得分:1)
感谢mKorbel上面的回答,我能够使用getViewRect()来修复代码。
对于将来可能想要做类似事情的人,我得到一个视口的矩形,然后使用for循环检查视图的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);
}
}
}
}