Odświeżanie i podwójne buforowanie

0

Block

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

public class Block extends JPanel implements MouseListener, MouseMotionListener
{
	private static final long serialVersionUID = 1L;
	private static final int STANDARD_WIDTH = 300, STANDARD_HEIGHT = 70;
	private int blockWidth, blockHeight;
	private Block upperSibling;
	private BlocksPane parent;
	
	public Block(BlocksPane parent){
		this.parent = parent;
		setBackground(Color.BLACK);
		setBlockSize(STANDARD_WIDTH, STANDARD_HEIGHT);
		setSize(getBlockWidth(), getBlockHeight());
		addMouseListener(this);
		addMouseMotionListener(this);
		setMouseOverBorder(false);
	}
	
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		if(parent.getComponentCount() > 1)
			setBounds( 0, getUpperSibling() == null ? 0 : getUpperSiblingsHeight() - 1, parent.getWidth() - 1, getBlockHeight() );
		else
			setSize(parent.getWidth() - 1, parent.getHeight() - 1);
	}
	
	public void setBlockSize(int blockWidth, int blockHeight){
		this.blockWidth = blockWidth;
		this.blockHeight = blockHeight;
	}
	
	public int getBlockWidth(){
		return blockWidth;
	}
	
	public int getBlockHeight(){
		return blockHeight;
	}
	
	public void setUpperSibling(Block upperSibling){
		this.upperSibling = upperSibling;
	}
	
	public Block getUpperSibling(){
		return upperSibling;
	}
	
	public int getUpperSiblingsHeight(){
		int allBlocksHeight = 1;
		
		if(getUpperSibling() != null)
			allBlocksHeight = getUpperSibling().getBlockHeight() + getUpperSibling().getUpperSiblingsHeight();
		
		return allBlocksHeight;
	}
	
	public void setMouseOverBorder(boolean setBorder){
		if(setBorder)
			setBorder(BorderFactory.createLineBorder(Color.GREEN, 2));
		else
			setBorder(BorderFactory.createLineBorder(Color.WHITE));
	}
	
	public void mouseClicked(MouseEvent e){}
	public void mouseEntered(MouseEvent e){
		setMouseOverBorder(true);
	}
	public void mouseExited(MouseEvent e){
		setMouseOverBorder(false);
	}
	public void mousePressed(MouseEvent e){}
	public void mouseReleased(MouseEvent e){}
	public void mouseDragged(MouseEvent e){}
	public void mouseMoved(MouseEvent e){}
}

BlocksPane

import java.awt.Graphics;
import java.awt.GridLayout;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JPanel;

public class BlocksPane extends JPanel
{
	private static final long serialVersionUID = 1L;
	private Block lastAddedBlock = null;
	private JPanel drawPanel = null;
	private List<Block> children = new LinkedList<Block>();
	private int width, height;
	private int mX = 0, mY = 0;

	public BlocksPane(JPanel drawPanel){
		this.drawPanel = drawPanel;
		setLayout(new GridLayout());
		setBackground(drawPanel.getBackground());
		setVisible(true);
	}
	
	public void paintComponent(Graphics g){			
        super.paintComponent(g);
        mX = (getParent().getWidth() - getWidth()) / 2;
        mY = (getParent().getHeight() - getHeight()) / 2;
        setBounds(mX, mY, width, height);
        for(Block block : children)
			block.paintComponent(block.getGraphics());
    }
	
	public void addBlock(Block block){
		if(getComponentCount() > 0)
			setHeight(getHeight() + block.getHeight());
		else{
			setWidth(block.getWidth() + 1);
			setHeight(block.getHeight() + 1);
		}
		
		add(block);
		children.add(block);
		
		if(lastAddedBlock != null)
			block.setUpperSibling(lastAddedBlock);
		
		lastAddedBlock = block;
	}
	
	public void setMatrixLocation(int x, int y){
		mX = x;
		mY = y;
	}
	
	public List<Block> getChildrenList(){
		return children;
	}
	
	public int getWidth(){
		return width;
	}
	
	public void setWidth(int width){
		this.width = width;
	}
	
	public int getHeight(){
		return height;
	}
	
	public void setHeight(int height){
		this.height = height;
	}
	
	public void setMatrixSize(int width, int height){
		this.width = width;
		this.height = height;
	}
	
	public int getmX(){
		return mX;
	}
	
	public void setmX(int x){
		mX = x;
	}
	
	public int getmY(){
		return mY;
	}
	
	public void setmY(int y){
		mY = y;
	}
}

TestWindow

import java.awt.Color;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TestWindow extends JFrame
{
	private static final long serialVersionUID = 1L;
	private static final int BLOCK_COUNT = 4;
	private JPanel drawPanel = null;
	
	public TestWindow(){
		setTitle("TEST");
		setExtendedState(getExtendedState()|JFrame.MAXIMIZED_BOTH);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBackground(Color.BLACK);
		setVisible(true);
		add(getDrawPanel());
		createBlocks();
	}
	
	public static void main(String[] args){
		SwingUtilities.invokeLater(new Runnable(){
            public void run(){ 
    			new TestWindow();
            }
        });
	}
	
	public JPanel getDrawPanel(){
		if(drawPanel == null){
			drawPanel = new JPanel(true);
			drawPanel.setBackground(Color.ORANGE);
		}
		return drawPanel;
	}
		
	public void createBlocks()
	{
		BlocksPane matrix = new BlocksPane(drawPanel);
		for(int i = 0; i < BLOCK_COUNT; i++)
			matrix.addBlock(new Block(matrix));
		
		add(matrix);
	}
}

Mam kilka pytań do powyższego kodu. Najpierw chciałbym rozwiązać pierwszą kwestię.
W klasie BlocksPane w metodzie paintComponent używam takiego fragmentu kodu:

for(Block block : children)
			block.paintComponent(block.getGraphics());

Nie jest to najlepszy pomysł, ale jeśli nie użyję powyższego kodu, to wewnątrz panelu rysują się tylko 2 bloki, a ostatnie 2 nie są odmalowywane. Nie rozumiem dlaczego. W momencie kiedy wykonywany jest paint klasy BlocksPane powinna też być odrysowana cała jego zawartość, ale nie jest - w związku z czym ostatnie 2 bloki są rysowane poza obrębem panelu i nie są przez to widoczne.

0

Poprawki do kodu. Pierwszy problem wynikał z małego niedopatrzenia, ale nie z jego powodu założyłem temat.

Klasa TestWindow - zmiana metody createBlocks

public void createBlocks()
{
BlocksPane matrix = new BlocksPane(drawPanel);
for(int i = 0; i < BLOCK_COUNT; i++)
matrix.addBlock(new Block(matrix));

	drawPanel.add(matrix);
}

W klasie BlocksPane można już usunąć

for(Block block : children)
block.paintComponent(block.getGraphics());

Dlaczego nikt nie podał rozwiązania ? Wynika to z fobii przed grafiką w Javie i nikt się nie podjął analizy kodu, czy po prostu błąd wydał się tak oczywisty, że nikomu nie chciało się odpowiadać ? Mam nadzieję, że nie z pierwszego powodu - poniżej podaje 2 właściwe problemy..

0

Problem 1

W konstruktorze TestWindow zmienić argumenty metody add tworząc widoczny JScrollPane

add( new JScrollPane(getDrawPanel(), JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) );

Wartość stałej BLOCK_COUNT zmienić na np. 30.

Czego brakuje, żeby scroll zadziałał ? Według mnie problem leży w odświeżaniu ... program chyba pobiera niewłaściwe wartości wysokości bloków.

Problem 2

Bloki migają. Próbuję zastosować podwójne buforowanie nadpisując metodę paint klasy TestWindow

public void paint(Graphics g)
	{
		if(diagramImage == null || diagramImage.getWidth() != getWidth() || diagramImage.getHeight() != getHeight())
			diagramImage = (BufferedImage)createImage(getWidth(), getHeight());
		
		Graphics gBI = diagramImage.getGraphics();
		gBI.setClip(g.getClip());
			
        super.paint(gBI);
        
        Graphics2D g2d = (Graphics2D)g;
        g2d.drawImage(diagramImage, 0, 0, null);
	}

ale to nic nie daje. Jak rozwiązać problem migania ?

0

Twój problem zaczyna się już w konstruktorze TestWindow. Wywołanie setVisible(true) oznacza koniec konstrukcji na obiektach reprezentujących w Javie komponenty ekranowe - to wywołanie musi być absolutnie na końcu za wszelkimi add, remove i set(...) na JFrame lub dowolnym elemencie, który się na nim znajdzie.

Jeżeli jakiekolwiek zmiany w układzie elementów Swinga zostaną wykonane później (po konstrukcji na ekranie), to trzeba wykonać metodę validate() lub validateTree(), aby wygenerować zdarzenie powodujące uaktualnienie zmienionych danych obiektów Swinga na ekranie (w uproszczeniu). Zmiana stanu zmiennych obiektów Swinga po ich wyświetleniu nie oznacza automatycznego uaktualnienia czegokolwiek na ekranie.

Druga sprawa. Używanie metod setBounds i setSize wewnątrz metody paint lub paintComponent, to tak jak próba przestawiania szyn z jadącego pociągu za pomocą kija. Metody odrysowujące powinny wyłącznie rysować i nic innego poza nim nie robić.
Przemyśl sobie co chciałeś osiągnąć i zapytaj o to.

ps. Jeszcze co do migania. Migotanie to nic innego jak rysowanie czegoś przez Twój kod, a następnie rysowanie innego "rysunku" (np. domyślnej powierzchni tła) przez mechanizmy Swinga lub system. Swing i GUI ogólnie nie jest zaprojektowany do synchronicznego rysowania, lecz do asynchronicznego reagowania na urządzenia wejściowe (mysz, klawisze).
Komunikaty z tych urządzeń wchodzące przez zarejestrowane procedury obsługi powinny zmieniać stan zmiennych programu na podstawie których metoda paint coś rysuje. To oznacza, że jeżeli chcesz coś aktywnie zmieniać na ekranie, to musisz zmieniać stan tych zmiennych i wtedy wywoływać repaint(). Jednak zbyt dużo repaint-ów w krótkim czasie jest przez Swinga łączone w jedno wywołanie ponieważ Swing jest asynchroniczny. Zanim wywoła cały łańcuch paint-ów trochę czasu upływa.

0

Druga sprawa. Używanie metod setBounds i setSize wewnątrz metody paint lub paintComponent, to tak jak próba przestawiania szyn z jadącego pociągu za pomocą kija. Metody odrysowujące powinny wyłącznie rysować i nic innego poza nim nie robić.
Przemyśl sobie co chciałeś osiągnąć i zapytaj o to.

Ten program to testowa wersja czegoś większego. W oryginalnej wersji te panele można dynamicznie przemieszczać i skalować. Rozumiem, że zamiast ustalania wartości setBounds w paincie powinienem to zrobić poza nim, ale dodatkowo za każdym razem wywołać metodę validate() ? Jeśli tak to na tym etapie tworzenia aplikacji będzie za duży problem. Jak to rozwiązać zachowując setBounds w paincie ?

Jeszcze co do migania. Migotanie to nic innego jak rysowanie czegoś przez Twój kod, a następnie rysowanie innego "rysunku" (np. domyślnej powierzchni tła) przez mechanizmy Swinga lub system.

Ale nadpisuję metodę paint. Wywołuję nadrzędną z argumentym, który jest buforem i sądziłem, że mechanizmy Swinga będą rysowały właśnie na nim. A potem sobie wkleję już cały efekt bez migania poszczególnych bloków, bo miałem nadzieję, że to będzie graficznie stanowiło już całość.

Rozwiązanie tych dwóch problemów ratuje mi życie. Piszę po 20 dniach, bo musiałem w międzyczasie dopracować inną część tej aplikacji.

0

Rozumiem, że zamiast ustalania wartości setBounds w paincie powinienem to zrobić poza nim, ale dodatkowo za każdym razem wywołać metodę validate() ?

Nie. Przeczytaj dokumentację metody validate().

Co do przesuwania i skalowania paneli - przecież aby móc to zrobić musi być wywołana jakaś akcja w reakcji na jakieś zdarzenia. I właśnie tam, w procedurze obsługi, informuje się Swinga o konieczności przemalowania lub zaktualizowania swoich komponentów. Pierwsze osiąga się przez repaint, a pozostałe przez różne wersje validate, revalidate itp. I dopiero w reakcji na tego typu przyczyny Swing może wywołać procedury update, paint, paintComponent itp.

Jak to rozwiązać zachowując setBounds w paincie ?

Nijak. setBounds w paincie, to tak jak brać przydrożne panienki do lasu. Nieprzyjemne skutki są tylko kwestią czasu.
Aplikacja z tego typu wywołaniami w paincie, to po prostu zła aplikacja. I to bez względu na to kto taką napisał. Programista nie ma kontroli nad tym ile paintów i kiedy zostanie wywołanych (chyba, że spróbuje te sprzętowe zablokować. W skrajnym wypadku setBounds w paincie spowoduje nieskończony ciąg wywołań setBounds-paint ponieważ to pierwsze prawie regularnie powoduje wywołanie tego drugiego (pośrednio). Jeżeli więc ten drugi powoduje wywołanie pierwszego, to mamy nieskończoną "rekurencję" wzajemnych wywołań bez żadnych bodźców z zewnątrz. Skutkiem jest zwykle przepełnienie kolejki Swinga i bardzo nietypowe błędy.

0

Analizując jednak działanie tego konkretnego przypadku. Dlaczego nie należy używać setBounds wewnątrz ? Ale tłumacząc logicznie, nie porównawczo :]. Jeśli każdorazowo, gdy wywoływany jest repaint() bloku wywoływany jest setBounds, a skoro w migawkach widać szereg mniejszych, czarnych kwadratów na samej górze panelu to znaczy, że czasem źle dobiera wartości rozmiaru i położenia. Kiedy usunę całkowicie setBounds z każdego painta, to wtedy pokazuje się ten nieprawidłowy obraz, jaki widać w migawkach (najlepiej zmienić kolor BlocksPane - np. na zielony). Więc czasem paintComponent uwzględnia złe wartości x, y, width, height, albo po prostu w ogóle nie wywołuje setBounds. Próbowałem ustalić w setBounds takie wartości, zeby uzyskać ten błędny obraz w migawkach, ale mi się nie udało. Pozostaje więc uznać, że co któryś paint nie wywołuje setBound - byłoby to możliwe, gdyby paintComponent był wywoływany na poziomie klasy JPanel, ale przecież paintComponent jest nadpisane, więc jak to możliwe ?

Ps. Wrzuciłem super.paintComponent(g) na sam koniec. Ale to niczego nie zmienia.

0

Jeśli każdorazowo, gdy wywoływany jest repaint() bloku wywoływany jest setBounds

Dlatego, że to jest błędne założenie. Metoda repaint() nie powoduje odmalowania obszaru (czyli wywołania paint()), ona nawet nie nakazuje takiego odmalowania. Jedyne o czym informujesz Swinga wywołując tę metodę, to że jakiś obszar komponentu przestał być aktualny i może być potrzebne jego odmalowanie. To Swing decyduje czy wywołanie nastąpi i kiedy, a także czy będzie ono połączone z innymi żądaniami repaint().

Co gorsze, repaint pobiera obcinany do odmalowania obszar właśnie ze zmiennych trzymających rozmiar komponentu - tych samych, które ustawia się przez setSize() lub setBounds(). Na podstawie tamtych wymiarów z tamtej chwili wycinany jest obszar ekranu, który podlega zmianie podczas działania serii procedur validate, update i paint. Jeżeli w trakcie działania jednej z tych procedur zmienisz wymiary komponentu, to system graficzny zgłupieje. Oczywiście "poradzi sobie" przez narysowanie czegoś w złym miejscu, albo poza obszarem, który będzie odwzorowany na pamięć obrazu (bufora). Tyle, że powstaną w ten sposób najgorsze błędy jakie mogą istnieć - niewykrywalne. Zazwyczaj kończy się to tym, że mający kilka(dziesiąt) tysięcy wierszy program od czasu do czasu spowoduje niewytłumaczalne zachowanie lub załamanie i utratę danych.

a skoro w migawkach widać szereg mniejszych, czarnych kwadratów na samej górze panelu to znaczy, że czasem źle dobiera wartości rozmiaru i położenia.
To o czym piszesz, to styl programowania przypadkowego. Nie tylko nie wiesz dlaczego program nie działa prawidłowo, ale nie wiesz też dlaczego w większości przypadków działa tak jak należy.
Krótko mówiąc wszelkie poprawianie tak napisanego kodu nie ma znaczenia dopóki od nowa punkt po punkcie nie zanegujesz każdego założenia - chyba, że udowodnisz sobie (dosłownie), że jest prawdziwe.
Prawdopodobnie podjąłeś cały szereg założeń, które nie są prawdziwe lub są prawdziwe tylko w szczególnych przypadkach.

Więc czasem paintComponent uwzględnia złe wartości x, y, width, height, albo po prostu w ogóle nie wywołuje setBounds.

Powinieneś przede wszystkim wiedzieć, że pola obiektu (komponentu), które zapisujesz metodą setBounds nie są tymi zmiennymi skąd Swing oraz system operacyjny pobierają dane do rysowania. To tylko pola pośrednie, które są źródłem dla zmiany (kompletnie niedostępnego dla Ciebie) komponentu ekranowego.

Pozostaje więc uznać, że co któryś paint nie wywołuje setBound - byłoby to możliwe, gdyby paintComponent był wywoływany na poziomie klasy JPanel, ale przecież paintComponent jest nadpisane, więc jak to możliwe ?

Przyczyn może być wiele, ale jedną z nich jest fakt iż dane wchodzące do paint nie są tymi samymi danymi, które podajesz jako argumenty setBounds. Dlaczego tak jest dowiesz się z dokumentacji. Ale niczego w niej nie nad interpretuj. Jeżeli czegoś nie napisano to tego nie ma.
Na przykład możesz tak wywołać procedurę repaint(), żeby paint kompletnie niczego nie narysował.
Musisz przyjąć (na razie) na wiarę, że obiekty Swinga, na których rzecz wywołujesz metody nie są tymi rzeczami, które widzisz na ekranie. One je tylko w pośredni sposób reprezentują.

Na przykład obiekt powstały przez wywołanie new JFrame nie jest okienkiem, które widzisz na ekranie po wywołaniu setVisible(). setVisible() powoduje tylko zażądanie od systemu operacyjnego (obiekty ciężkie) lub Swinga (obiekty lekkie) utworzenia komponentu ekranowego (swojego), którego parametry zostaną przepisane z obiektu JFrame, który utworzyłeś w Javie. Tak mniej więcej to w uproszczeniu działa.

0

Przerobiłem trochę program - jest tylko mignięcie na początku - prawidłowo ustawione - odpowiednia lokalizacja i rozmiar, ale nadal mignięcie. Czemu panel znika, skoro ma już ustawione odpowiednie parametry ? A metoda createBlocks() prawidłowo jest wywoływana w oknie zaraz po setVisible(true) z tego względu, że te bloki w nietestowej aplikacji są dodawane dynamicznie po wyświetleniu okna.

TestWindow

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class TestWindow extends JFrame
{
	private static final long serialVersionUID = 1L;
	private static final int BLOCK_COUNT = 4;
	private JPanel drawPanel = null;
	
	public TestWindow(){
		setTitle("TEST");
		setWindowFullSize();
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBackground(Color.BLACK);
		add( new JScrollPane(getDrawPanel(), JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) );
		setVisible(true);
		
		
		createBlocks();
	}
	
	private void setWindowFullSize(){
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Dimension screenSize = toolkit.getScreenSize();
		
		setBounds(0, 0, screenSize.width, screenSize.height);
		setExtendedState(getExtendedState()|JFrame.MAXIMIZED_BOTH);
	}
	
	public JPanel getDrawPanel(){
		if(drawPanel == null){
			drawPanel = new JPanel(true);
			drawPanel.setBackground(Color.ORANGE);
		}
		return drawPanel;
	}
		
	public void createBlocks(){
		BlocksPane matrix = new BlocksPane(drawPanel);
		drawPanel.add(matrix);
		for(int i = 0; i < BLOCK_COUNT; i++)
			matrix.addBlock(new Block(matrix));
	}
	
	public static void main(String[] args){
		SwingUtilities.invokeLater(new Runnable(){
            public void run(){ 
    			new TestWindow();
            }
        });
	}
}

BlocksPane

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class BlocksPane extends JPanel
{
	private static final long serialVersionUID = 1L;
	private Block lastAddedBlock = null;
	private JPanel drawPanel = null;
	private List<Block> children = new LinkedList<Block>();
	private int paneWidth, paneHeight;
	private int bpX = 0, bpY = 0;

	public BlocksPane(JPanel drawPanel){
		this.drawPanel = drawPanel;
		//setLayout(new GridLayout());
		//setLayout(new BorderLayout());
		setLayout(null);
		//setBackground(drawPanel.getBackground());
		setBackground(Color.GREEN);
		setVisible(true);
	}
	
	public void paintBlocksPane(){
		bpX = (getParent().getWidth() - getBlocksPaneWidth()) / 2;
		bpY = (getParent().getHeight() - getBlocksPaneHeight()) / 2;
	    setBounds(bpX, bpY, getBlocksPaneWidth(), getBlocksPaneHeight());
	    validate();
	    repaint();
	    
	    for(Block block : children)
	    	block.paintBlock();
	}
	
	public void addBlock(Block block){
		if(getComponentCount() > 0)
			setBlocksPaneHeight(getHeight() + block.getHeight());
		else{
			setBlocksPaneWidth(block.getWidth() + 1);
			setBlocksPaneHeight(block.getHeight() + 1);
		}
		
		add(block);
		children.add(block);
		
		if(lastAddedBlock != null)
			block.setUpperSibling(lastAddedBlock);
		
		lastAddedBlock = block;
		
		paintBlocksPane();
	}
	
	public void setBlocksPaneLocation(int x, int y){
		bpX = x;
		bpY = y;
	}
	
	public List<Block> getChildrenList(){
		return children;
	}
	
	public int getBlocksPaneWidth(){
		return paneWidth;
	}

	public void setBlocksPaneWidth(int width){
		this.paneWidth = width;
	}
	
	public int getBlocksPaneHeight(){
		return paneHeight;
	}
	
	public void setBlocksPaneHeight(int height){
		this.paneHeight = height;
	}
	
	public void setBlocksPaneSize(int width, int height){
		this.paneWidth = width;
		this.paneHeight = height;
	}
	
	public int getbpX(){
		return bpX;
	}
	
	public void setbpX(int x){
		bpX = x;
	}
	
	public int getbpY(){
		return bpY;
	}
	
	public void setbpY(int y){
		bpY = y;
	}
}

Block

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class TestWindow extends JFrame
{
	private static final long serialVersionUID = 1L;
	private static final int BLOCK_COUNT = 4;
	private JPanel drawPanel = null;
	
	public TestWindow(){
		setTitle("TEST");
		setWindowFullSize();
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBackground(Color.BLACK);
		add( new JScrollPane(getDrawPanel(), JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) );
		setVisible(true);
		
		
		createBlocks();
	}
	
	private void setWindowFullSize(){
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Dimension screenSize = toolkit.getScreenSize();
		
		setBounds(0, 0, screenSize.width, screenSize.height);
		setExtendedState(getExtendedState()|JFrame.MAXIMIZED_BOTH);
	}
	
	public JPanel getDrawPanel(){
		if(drawPanel == null){
			drawPanel = new JPanel(true);
			drawPanel.setBackground(Color.ORANGE);
		}
		return drawPanel;
	}
		
	public void createBlocks(){
		BlocksPane matrix = new BlocksPane(drawPanel);
		drawPanel.add(matrix);
		for(int i = 0; i < BLOCK_COUNT; i++)
			matrix.addBlock(new Block(matrix));
	}
	
	public static void main(String[] args){
		SwingUtilities.invokeLater(new Runnable(){
            public void run(){ 
    			new TestWindow();
            }
        });
	}
}
0

Poprawka - wkleiłem złą klasę Block - poniżej prawidłowa.

import java.awt.Color;
import java.awt.Label;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

public class Block extends JPanel implements MouseListener, MouseMotionListener
{
	private static final long serialVersionUID = 1L;
	private static final int STANDARD_WIDTH = 300, STANDARD_HEIGHT = 70;
	private int blockWidth, blockHeight;
	private Block upperSibling;
	private BlocksPane parent;
	private Label label = null;
	
	public Block(BlocksPane parent){
		this.parent = parent;
		setBackground(Color.BLACK);
		setBlockSize(STANDARD_WIDTH, STANDARD_HEIGHT);
		setSize(getBlockWidth(), getBlockHeight());
		addMouseListener(this);
		addMouseMotionListener(this);
		setMouseOverBorder(false);
		/*label = new Label("Label");
		label.setBackground(Color.RED);
		add(label);*/
	}
	
	public void paintBlock(){
		if(parent.getComponentCount() > 1)
			setBounds( 0, getUpperSibling() == null ? 0 : getUpperSiblingsHeight() - 1, parent.getWidth() - 1, getBlockHeight() );
		else
			setSize(parent.getWidth() - 1, parent.getHeight() - 1);
		
		validate();
	    repaint();
	}
	
	public void setBlockSize(int blockWidth, int blockHeight){
		this.blockWidth = blockWidth;
		this.blockHeight = blockHeight;
	}
	
	public int getBlockWidth(){
		return blockWidth;
	}
	
	public int getBlockHeight(){
		return blockHeight;
	}
	
	public void setUpperSibling(Block upperSibling){
		this.upperSibling = upperSibling;
	}
	
	public Block getUpperSibling(){
		return upperSibling;
	}
	
	public int getUpperSiblingsHeight(){
		int allBlocksHeight = 1;
		
		if(getUpperSibling() != null)
			allBlocksHeight = getUpperSibling().getBlockHeight() + getUpperSibling().getUpperSiblingsHeight();
		
		return allBlocksHeight;
	}
	
	public void setMouseOverBorder(boolean setBorder){
		if(setBorder)
			setBorder(BorderFactory.createLineBorder(Color.RED, 2));
		else
			setBorder(BorderFactory.createLineBorder(Color.WHITE));
	}
	
	public void mouseClicked(MouseEvent e){}
	public void mouseEntered(MouseEvent e){
		setMouseOverBorder(true);
	}
	public void mouseExited(MouseEvent e){
		setMouseOverBorder(false);
	}
	public void mousePressed(MouseEvent e){}
	public void mouseReleased(MouseEvent e){}
	public void mouseDragged(MouseEvent e){}
	public void mouseMoved(MouseEvent e){}
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1