2013-03-02 55 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

相關問題