2013-03-02 19 views
5

我有一个带有JPanel作为ViewPort组件的JSrollPane。在这个JPanel上,我使用paintComponent绘制一个64x64px的正方形网格。 JPanel非常大,28'672px×14'336px,网格仍然是即时绘制的,一切看起来都很好。问题在于,垂直或水平滚动会导致CPU使用率跳得相当高,滚动得越快,滚动越快。滚动时,CPU使用率可达35-50%之间。滚动相同大小的JPanel而没有绘制网格,使用的CPU很少,所以网格肯定是问题的原因。这个网格是我打算在scrollpane内部做的最基本的部分,如果它现在表现不好,我担心在添加更多“内容”之后它将不可用。带有网格的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); 
      } 
     } 
    } 
} 

编辑:此代码的更新/固定版本如下,在我的问题的答案。

+1

你实际上是在加工和画上28672 X 14336面。您可以将其缩小为仅限可见剪辑。 – tenorsax 2013-03-02 18:30:39

回答

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

编辑从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); 
     } 
    } 
} 
+0

啊,非常好,看起来像这可能会解决我的问题。我会尝试一下。感谢这个例子! – SLD 2013-03-02 19:08:17

+1

我用viewport getViewRect()玩过,这正是我需要的。我将重写我的代码以仅在当前可见区域内绘制。谢谢你的帮助! – SLD 2013-03-03 01:48:15

1

由于上述mKorbel的回答

很好的例子,通过(HFOE这里)EncephalopathicRectagle.intersects(Rectagle),我能修复了代码,使用getViewRect()。

对于未来的任何人,可能想要做类似的事情,我会得到视口的Rectangle,然后使用for循环来检查视图的x/y(顶角)是否在tile(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

对于代码-1mio为AbsoluteLayout +1(setSize,setBounds ...) – mKorbel 2013-03-03 19:04:42

+0

好吧,我修正了布局并更新了代码。让我知道是否还有什么可疑的,我一直在学习。 :) – SLD 2013-03-04 00:36:34

相关问题