2013-03-02 11 views
5

Mam JSrollPane z JPanel jako składnik ViewPort. Na tym JPanelu używam paintComponent do narysowania siatki o kwadratach 64x64px. JPanel jest dość duży, 28'672px na 14'336px, nadal siatka jest rysowana od razu i wszystko wydaje się w porządku. Problem polega na tym, że przewijanie w pionie lub w poziomie powoduje, że użycie procesora przeskakuje dość wysoko, im szybciej przewijam, tym wyżej. Wykorzystanie procesora wzrasta do 35-50% podczas przewijania. Przewijanie JPanela o tym samym rozmiarze bez narysowanej siatki, zużywa bardzo mało CPU, więc siatka jest z pewnością przyczyną problemu. Ta siatka jest najbardziej podstawową częścią tego, co planuję zrobić wewnątrz scrollpane, jeśli działa teraz źle, obawiam się, że nie będzie można jej użyć po dodaniu więcej "zawartości".JPanel z siatką namalowaną na nim, powodującą wysokie użycie procesora podczas przewijania

Moje pytanie (y) Dlaczego do przewijania tej siatki zużywa się tak dużo procesora, czy siatka jest odświeżana za każdym razem, gdy następuje zmiana położenia suwaka? Czy istnieje lepszy lub bardziej skuteczny sposób narysowania przewijanej siatki?

Wpadłem na pomysł, że rysuję tylko siatkę widocznego obszaru (przez współrzędne), a następnie przerysowuję ten widoczny obszar, gdy paski przewijania są przesuwane, ale to wywoływałoby odświeżanie. Jeśli to możliwe, chciałbym narysować całą siatkę przy starcie, a następnie tylko odświeżyć na polecenie.

Oto prosty przykład działania mojej siatki 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); 
      } 
     } 
    } 
} 

EDIT: Zaktualizowana/wersja ustalona tego kodu jest poniżej, w mojej odpowiedzi na pytanie.

+1

Ty rzeczywiście przetwarzanie i malowanie na 28672 x 14336 powierzchni. Możesz zmniejszyć go tylko do widocznego klipu. – tenorsax

Odpowiedz

4

dla przykład

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

świetny przykład o Rectagle.intersects(Rectagle) przez (HFOE tutaj) Encephalopathic z 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 bardzo dobrze, wygląda na to, prawdopodobnie rozwiąże mój problem. Wypróbuję to. Dzięki za ten przykład! – SLD

+1

Grałem z viewport getViewRect(), to jest dokładnie to, czego potrzebuję.Zamierzam przepisać mój kod, aby rysować tylko w bieżącym widocznym obszarze. Dzięki za pomoc! – SLD

1

Dzięki za odpowiedź mKorbel powyżej, udało mi się naprawić ten kod, za pomocą getViewRect().

Dla każdego, kto może chcieć zrobić coś podobnego, otrzymam Prostokąt z rzutni, a następnie użyj dla pętli, aby sprawdzić, czy x/y (górny róg) widoku jest w obrębie kafelka (1024x1024) przyrost. Jeśli tak, rysuję kwadraty siatki począwszy od płytki przyrostowej x/y do szerokości/wysokości kafelka +1 widoku ekranu (1024). Przewijanie od góry do dołu jednym szybkim ruchem używa tylko około 5% procesora, co jest dopuszczalne.

Oto kod zaktualizowane do siatki przewijalnej 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

+1 dla kodu -1mio dla AbsoluteLayout (setSize, setBounds ...) – mKorbel

+0

OK, poprawiłem układ i zaktualizowałem kod. Daj mi znać, jeśli cokolwiek innego jest podejrzane, nadal się uczę, jak idę. :) – SLD

Powiązane problemy