2013-03-02 5 views
5

Ich habe JSrollPane mit einem JPanel als ViewPort-Komponente. In diesem JPanel verwende ich paintComponent um ein Raster von 64x64px Quadraten zu zeichnen. Der JPanel ist ziemlich groß, 28'672px um 14'336px, immer noch wird das Raster sofort gezeichnet und alles scheint in Ordnung zu sein. Das Problem ist, dass das Scrollen vertikal oder horizontal dazu führt, dass die CPU-Auslastung ziemlich hoch steigt, je schneller ich blättern kann, je höher es geht. Die CPU-Auslastung beträgt beim Scrollen zwischen 35-50%. Wenn Sie JPanel in derselben Größe ohne das darauf gezeichnete Raster scrollen, wird nur sehr wenig CPU verwendet. Das Raster ist also sicherlich die Ursache des Problems. Dieses Raster ist der grundlegendste Teil dessen, was ich im Scrollfenster vorhabe. Wenn es jetzt schlecht läuft, befürchte ich, dass es unbrauchbar wird, wenn mehr "Inhalt" hinzugefügt wird.JPanel mit Gitter gemalt, verursacht hohe CPU-Auslastung beim Scrollen

Meine Frage (n) Warum benutzt das System so viel CPU, um über dieses Raster zu scrollen? Wird das Raster bei jeder Änderung der Position der Bildlaufleiste neu gezeichnet? Gibt es eine bessere oder effizientere Möglichkeit, ein scrollbares Raster zu zeichnen?

Ich hatte eine Idee, nur das Raster des sichtbaren Bereichs (durch Koordiaten) zu zeichnen und dann diesen sichtbaren Bereich neu zu zeichnen, wenn die Bildlaufleisten verschoben werden, aber dies würde repaint viel aufrufen. Wenn möglich, möchte ich das gesamte Raster beim Start zeichnen und dann nur auf Befehl neu zeichnen.

Hier ist ein barebones Arbeitsbeispiel meines JPanel Grid.

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: Die aktualisierte/fixed Version dieses Codes ist unten, auf die Frage in meiner Antwort.

+1

Sie sind tatsächlich die Verarbeitung und Malerei auf 28672 x 14336 Oberfläche. Sie können es auf nur einen sichtbaren Clip reduzieren. – tenorsax

Antwort

4

für Beispiel

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

EDIT

großartiges Beispiel über Rectagle.intersects(Rectagle) von (HFOE hier) Encephalopathic von 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

Ahh sehr gut, sieht so aus, wird wahrscheinlich mein Problem lösen. Ich werde es ausprobieren. Danke für dieses Beispiel! – SLD

+1

Ich spielte mit Viewport getViewRect(), das ist genau das, was ich brauche.Ich werde meinen Code neu schreiben, um nur im aktuellen sichtbaren Bereich zu zeichnen. Danke für Ihre Hilfe! – SLD

1

Dank mKorbel Antwort oben, ich war in der Lage, den Code zu beheben, indem getViewRect().

Für alle in der Zukunft, die etwas ähnliches tun möchten, bekomme ich ein Rectangle des Darstellungsbereichs, dann for loops zu überprüfen, ob die x/y (obere Ecke) des Viewrect innerhalb einer Kachel (1024x1024) ist) Zuwachs. Wenn dies der Fall ist, zeichne ich die Rasterquadrate ausgehend von der X/Y-Inkrement-Kachel auf die Breite/Höhe der Ansichtsfenster-Kachel (1024). Scrollen von oben nach unten in einem schnellen Swipe verwendet nur etwa 5% CPU, die akzeptabel ist.

Hier ist der aktualisierte Code für den JPanel scrollbaren Raster:

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

+1 für Code -1mio für AbsoluteLayout (setSize, setBounds ...) – mKorbel

+0

OK, ich habe das Layout korrigiert und den Code aktualisiert. Lassen Sie mich wissen, wenn etwas anderes fischig ist, lerne ich immer noch, während ich weitergehe. :) – SLD

Verwandte Themen