Dwa panele w jednym frame

0

W jaki sposób dodać dwa panele do jednego frame'a ?

W poniższy sposób MyPanel3 jest wyświetlany w oknie, ale MyPanel2 już nie - po mimo, że według parametrów setBounds() nie powinny się przesłaniać.

package my;
import javax.swing.*;
import java.awt.*;

public class Main2 {
	public static void main(String args[]) {
		new MyFrame2();
	}

}

class MyFrame2 extends JFrame {

	MyFrame2() {
		setVisible(true);
		setBounds(500, 100, 400, 300);
		setTitle("Lab 1"); 
		
		getContentPane().add(new MyPanel2());
		getContentPane().add(new MyPanel3());
		
	}
}

class MyPanel2 extends JPanel {
	
	MyPanel2() {
		setLayout(null);
	}
	
	public void paintComponent(Graphics gp) {
		super.paintComponent(gp);
		setBounds(250, 10, 100, 100);
		setBackground(Color.darkGray);
		
		repaint();
	}
}

class MyPanel3 extends JPanel {
	
	MyPanel3() {
		setLayout(null);
	}
	
	public void paintComponent(Graphics gp) {
		super.paintComponent(gp);
		setBounds(10, 10, 100, 100);
		setBackground(Color.red);
		
		repaint();
	}
}
0

MyFrame2.setLayout(null)

0

Wtedy nie widać, ani jednego, ani drugiego panelu. Okno jest puste.

0

Teraz nie pojawia się ani jeden ani drugi panel:

package my;
import javax.swing.*;
import java.awt.*;

public class Main2 {
        public static void main(String args[]) {
                new MyFrame2();
        }

}

class MyFrame2 extends JFrame {

        MyFrame2() {
                setLayout(null);
                setVisible(true);
                setBounds(500, 100, 400, 300);
                setTitle("Lab 1"); 
                
                getContentPane().add(new MyPanel2());
                getContentPane().add(new MyPanel3());
                
        }
}

class MyPanel2 extends JPanel {
        
        MyPanel2() {
                setLayout(null);
        }
        
        public void paintComponent(Graphics gp) {
                super.paintComponent(gp);
                setBounds(250, 10, 100, 100);
                setBackground(Color.darkGray);
                
                repaint();
        }
}

class MyPanel3 extends JPanel {
        
        MyPanel3() {
                setLayout(null);
        }
        
        public void paintComponent(Graphics gp) {
                super.paintComponent(gp);
                setBounds(10, 10, 100, 100);
                setBackground(Color.red);
                
                repaint();
        }
}

0
  1. Przenieś setBounds z metody rysującej panel np.: do konstruktora.
  2. Czemu ustawiasz kolor tła w paintComponent ? Metoda setBackground wywoła repaint'a jeżeli kolor tła jest inny niż wcześniej ustawiony dodatkowo ten repaint na końcu (dodaj sobie System.out do paintComponent :p)
  3. Pomyśl nad wykorzystaniem menadżera rozkładu, nawet jeżeli chcesz uzyskać tak statyczny efekt, np:
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;

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

public class Main2 {
        public static void main(String args[]) {
                new MyFrame2();
        }

}

class MyFrame2 extends JFrame {
    
        private JPanel myPanel1;
        private JPanel myPanel2;
        private JPanel myPanel3;
        private JPanel myPanel4;

        MyFrame2() {
                setLayout(new MyLayoutManager().setComponentWidth(150)
                        .setComponentHeight(150).setVerticalGap(10));
                setTitle("Lab 1"); 

                myPanel1 = new JPanel();
                myPanel1.setBackground(Color.RED);
                myPanel2 = new JPanel();
                myPanel2.setBackground(Color.BLACK);
                myPanel3 = new JPanel();
                myPanel3.setBackground(Color.BLUE);
                myPanel4 = new JPanel();
                myPanel4.setBackground(Color.GREEN);
                
                getContentPane().add(myPanel1);
                getContentPane().add(myPanel2);
                getContentPane().add(myPanel3);
                getContentPane().add(myPanel4);
                this.pack();
                setVisible(true);
                this.setLocationRelativeTo(null);
        }
        
      
}

/**
 * Klasa opisująca menadżera mojego rozkładu.
 *
 * @author znikąd
 * @version 0.1.0
 */
class MyLayoutManager implements LayoutManager {
    
    /**
     * Marginesy.
     */
    private Insets margins = new Insets(5, 5, 5, 5);
    
    /**
     * Maksymalna ilość komponentów w jednej lini.
     */
    private int maxInLine = 2;
    
    /**
     * Odległośc między komponentami w poziomie.
     */
    private int horizontalGap = 150;
    
    /**
     * Odległość między komponentami w pionie.
     */
    private int verticalGap = 5;
    
    /**
     * Szerokość komponentu.
     */
    private int width = 100;
    
    /**
     * Wyskokośc komponentu.
     */
    private int height = 100;
    
    /**
     * Rozmiar.
     */
    private final Dimension size = new Dimension(0, 0);
    
    /**
     * Konstruktor.
     */
    public MyLayoutManager() {
    }
    
    /**
     * Konstruktor.
     *
     * @param w szerokość komponentu
     * @param h wysokość komponentu
     */
    public MyLayoutManager(final int w, final int h) {
        this(2, 150, 5, w, h);
    }
    
    /**
     * Konstruktor.
     *
     * @param max maksymalna ilość komponentów w jednej lini
     * @param hGap odległośc miedzy koponentami w poziomie
     * @param vGap odległość między komponetami w pionie
     * @param w szerokość komponentu
     * @param h wysokość komponentu
     */
    public MyLayoutManager(final int max, final int hGap, final int vGap, 
            final int w, final int h) {
        this.maxInLine = max;
        this.horizontalGap = hGap;
        this.verticalGap = vGap;
        this.width = w;
        this.height = h;
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void addLayoutComponent(final String name, 
            final Component comp) {
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void layoutContainer(final Container parent) {
        final Component[] components = parent.getComponents();
        int x = this.margins.left;
        int y = this.margins.top;
        int count = 0;
        for (final Component c : components) {
            c.setBounds(x, y, this.width, this.height);
            // dodaje szerokość i odstep w poziomie
            x += this.width + this.horizontalGap;
            count ++;
            if (count == this.maxInLine) {
                count = 0;
                x = this.margins.left;
                y += this.height + this.verticalGap;
            }
        }
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public Dimension minimumLayoutSize(final Container parent) {
        this.calculateSize(parent);
        return this.size;
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public Dimension preferredLayoutSize(final Container parent) {
        this.calculateSize(parent);
        return this.size;
    }
    
    private final void calculateSize(final Container parent) {
        // wyciągam komponenty
        final Component[] components = parent.getComponents();
        
        int layoutWidth = this.margins.left + this.margins.right;
        int layoutHeight = this.margins.top + this.margins.bottom;
        // obliczam szerokość
        if (components.length >= this.maxInLine && this.maxInLine > 0) {
            layoutWidth += (this.maxInLine * this.width) + 
                ((this.maxInLine - 1) * this.horizontalGap);
        } else if (components.length > 0 && this.maxInLine > 0) {
            layoutWidth += (components.length * this.width) + 
            ((components.length - 1) * this.horizontalGap);
        }
        // obliczam wysokość
        int rows = components.length / this.maxInLine;
        if (rows > 0) {
            layoutHeight += (rows * this.height) + ((rows -1) * this.verticalGap);
        }
        this.size.setSize(layoutWidth, layoutHeight);
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void removeLayoutComponent(final Component comp) {
    }

    /**
     * Zwraca odstep między komponentami w poziomie.
     *
     * @return odstep między komponentami w poziomie
     */
    public final int getHorizontalGap() {
        return this.horizontalGap;
    }

    /**
     * Ustawia odstep między komponentami w poziomie.
     *
     * @param hGap odstep między komponentami w poziomie
     * @return instancja klasy
     */
    public final MyLayoutManager setHorizontalGap(final int hGap) {
        this.horizontalGap = hGap;
        return this;
    }

    /**
     * Zwraca odstęp między komponentami w pionie.
     *
     * @return odstęp między komponentami w pionie
     */
    public final int getVerticalGap() {
        return this.verticalGap;
    }

    /**
     * Ustawia odstęp między komponentami w pionie.
     *
     * @param vGap odstęp między komponentami w pionie
     * @return instancja klasy
     */
    public final MyLayoutManager setVerticalGap(final int vGap) {
        this.verticalGap = vGap;
        return this;
    }

    /**
     * Zwraca szerokośc komponentu.
     *
     * @return szerokośc komponentu
     */
    public final int getComponentWidth() {
        return this.width;
    }

    /**
     * Ustawia szerokośc komponentu.
     *
     * @param w szerokośc komponentu
     * @return instancja klasy
     */
    public final MyLayoutManager setComponentWidth(final int w) {
        this.width = w;
        return this;
    }

    /**
     * Zwraca wysokość komponentu.
     *
     * @return wysokość komponentu
     */
    public final int getComponentHeight() {
        return this.height;
    }

    /**
     * Ustawia wysokość komponentu.
     *
     * @param h wysokość komponentu
     * @return instancja klasy
     */
    public final MyLayoutManager setComponentHeight(final int h) {
        this.height = h;
        return this;
    }

    /**
     * Zwraca marginesy.
     *
     * @return marginesy
     */
    public final Insets getMargins() {
        return this.margins;
    }
}
0

Użyj LayoutManagera, np. BorderLayout

class MyFrame2 extends JFrame {

        MyFrame2() {
                setLayout(new BorderLayout()); //BorderLayout
              
                setBounds(500, 100, 400, 300);
                setTitle("Lab 1");
               
                getContentPane().add(new MyPanel2(), BorderLayout.EAST); // PRAWO
                getContentPane().add(new MyPanel3(), BorderLayout.WEST); // LEWO
                
                setVisible(true); //to powinno być na końcu    
        }
}
0

Dzięki za rady powyżej, były bardzo pomocne ;-) Ale ...

znikąd napisał(a)

Czemu ustawiasz kolor tła w paintComponent ?

Czyli ustawianie tła w paintComponent jest mało eleganckie ?

Tak więc dlaczego, gdy zdefiniuje metodę paintComponent(), to metoda setBacground() w konstruktorze w ogóle nie działa ? Za to wystarczy, że usunę paintComponent i faktycznie kolor zielony się pojawia ..

	class Panel extends JPanel {
		
		Panel() {
			setBackground(Color.green);			
		}
		
		public void paintComponent(Graphics g) {
		            ...			
		} 
	}
0
puertainsua napisał(a)

Tak więc dlaczego, gdy zdefiniuje metodę paintComponent(), to metoda setBacground() w konstruktorze w ogóle nie działa ? Za to wystarczy, że usunę paintComponent i faktycznie kolor zielony się pojawia ..

Nie dziala (w zasadzie to dziala, tyle ze jej zadaniem jest jedynie zapisanie w obiekcie informacji o kolorze tla, a nie jak zakladales, nalozenie koloru na panel), gdyz wlasnie przesloniles metode klasy bazowej JPanel, ktora byla odpowiedzialna m.in. za wypelnienie kolorem tla komponentu.
Od tej chwili jest to Twoje zadanie do zrealizowania.
Mozesz albo na wlasna reke zrealizowac wszystkie/wybrane zadania, ktore realizowala przeslaniana metoda, albo wywolac w niej metode klasy bazowej:

super.paintComponent(g);

i dodatkowo zrealizowac to co chciales zrobic przeslaniajac te metode

0

Już rozumiem, dzięki za wyjaśnienie.

Jeszcze jedna kwestia. Chcę, żeby po kliknięciu na JButton, w innym panelu wykonała się jakaś akcja, niech będzie to na przykład narysowanie prostej przy użycie drawLine(). No właśnie ... tylko jak to zaplanować efektywnie ?

Moje rozwiązanie problemu jest takie:

	class PanelPrawy extends JPanel {
		Graphics2D g2d;
                        boolean czyNarysowac;
		
		PanelPrawy() {
			...
		}
		
		public void paintComponent(Graphics g) {
			
			...
                        if (czyNarysowac) {     
			g2d.drawLine(0, 0, pPrawy.getWidth(), pPrawy.getHeight());
                        }
		} 
	}

No i metoda, którą dołączam do przycisku:

			public void actionPerformed(ActionEvent e) {
				
				czyNarysowac = true; repaint();
		    }

Czyli streszczając, po kliknięciu na przycisk zmienna czyNarysowac jest ustawiana na true, a wtedy prosta jest rysowana. Niby działa, ale to chyba kiepska strategia ? Jak to powinno wyglądać porządnie ?

0

Moim zdaniem to jest prawidłowa strategia. GUI powinno wpływać na stan zmiennych programu, na podstawie których program działa (również coś wyrysowuje itp.). Błędem byłoby właśnie coś odwrotnego czyli wykonywanie bezpośrednio jakichś akcji z odbiorców zdarzeń takich jak actionPerformed(). A już tym bardziej akcji długotrwałych, co spowodowałoby zawieszenie GUI. Do wykonania czasochłonnych zadań powinieneś użyć innych wątków niż EventDispatchThread, który "porusza" kodem wszystkich kontrolek oraz odbiorców zdarzeń - np. SwingWorkera. Ale nawet wtedy działania w innym wątku powinny zmieniać jedynie stan zmiennych programu (tu uwaga na wielowątkowy dostęp do tych danych), na podstawie których metody paint i paintComponent wyrysowują coś na ekranie.

U Ciebie zmienna "czyNarysować" jest stanem programu, który zmieniasz bez użycia wątków ponieważ zmiana ta nie wymaga obliczeń więc jest błyskawiczna. Rysowanie (i reszta GUI) korzysta m.in. z tej danej. Jest OK.

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