Swing, dimensioni componenti

di il
3 risposte

Swing, dimensioni componenti

Salve,
sto provando a creare una gui, applicando vari layout, quali BoxLayout, GridLayout e FlowLayout, a vari JPanel annidati tra loro.
Volevo chiedervi il perchè le dimensioni dei componenti mi variano di volta in volta.
Il mio codice è il seguente:

package gui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class Main {

	public static void main(String[] args) {
		JFrame myjframe = new JFrame("PROVA MENU");
		myjframe.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		myjframe.setSize(800, 600);
		
		Container mycontainer = myjframe.getContentPane();
		mycontainer.setLayout(new BorderLayout());
		
		JPanel panel_1 = new JPanel();
		panel_1.setLayout(new BoxLayout(panel_1, BoxLayout.Y_AXIS));
		
		JPanel panel_2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
		JPanel panel_3 = new JPanel(new GridLayout(3,1));
		JPanel panel_4 = new JPanel(new GridLayout(3,2));
		JPanel panel_5 = new JPanel(new GridLayout(1, 2));

		JLabel empty = new JLabel();
		JLabel label_1 = new JLabel("Valore campo JTextField 1: ");
		JLabel label_2 = new JLabel("Valore campo JTextField 2: ");
		JLabel label_3 = new JLabel("Valore campo JTextArea 3: ");
		JTextField text_1 = new JTextField();
		JTextField text_2 = new JTextField();
		JTextArea text_3 = new JTextArea(7,21);
		JButton bottone_1 = new JButton("Indietro");
		JButton bottone_2 = new JButton("Avanti");
		
		panel_4.add(label_1);
		panel_4.add(text_1);
		panel_4.add(label_2);
		panel_4.add(text_2);
		panel_4.add(label_3);
		panel_4.add(empty);
		panel_5.add(bottone_1);
		panel_5.add(bottone_2);
		panel_3.add(panel_4);
		panel_3.add(text_3);
		panel_3.add(panel_5);
		panel_2.add(panel_3);
		panel_1.add(panel_2);

		mycontainer.add(panel_1, BorderLayout.NORTH);

		myjframe.setVisible(true);
	}
}

3 Risposte

  • Re: Swing, dimensioni componenti

    Ho provato ad eseguire il programma qualche volta ma non ottengo risultati diversi, riesci a spiegare come cambiano le dimensioni o meglio a fornire due screenshot di esempio?

    Tieni comunque presente che il modo corretto per costruire e rendere visibile il frame è eseguire il codice relativo all'interno del contesto dell'EDT (Event Dispatch Thread), tipicamente questo si fa inserendo il codice dentro al metodo invokeLater della classe SwingUtilities, trovi tonnellate di esempi in internet (o sui libri), a partire dal tutorial ufficiale.

    Prendo in prestito dal link di cui sopra questo frammento di codice:
    
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });
    
    Questo è appunto il tipico modo di avviare l'applicazione, spesso quel blocco è l'unica istruzione all'interno del metodo main, e la creazione della GUI avviene in un metodo separato (quel createAndShowGUI ()) o direttamente nel costruttore della classe che estende JFrame se ne prevedi una (in questo caso dentro al run () avresti solo un'istruzione del tipo new MyFrame ().setVisible (true);, e faresti appunto tutta l'inizializzazione dei componenti a partire dal costruttore del frame).

    Se non crei/modifichi l'interfaccia all'interno dell'EDT, puoi incorrere in diversi tipi di errori, anche se in questo caso io non ottengo risultati diversi.

    Infine, abituati a preferire il metodo pack () di JFrame rispetto al setSize (), che permette al frame di calcolare in modo autonomo la dimensione "corretta" dei propri componenti, senza rischiare di lasciare qualcosa fuori.

    Se vuoi inserire dello spazio tra/fuori i componenti e stai usando setSize per non avere la GUI troppo "affollata" ci sono vari modi per fare questo: ad esempio utilizzando i bordi (EmptyBorder su tutti) per il contenitore esterno o i sottopannelli, o aggiungendo lo spazio direttamente per i layout manager che lo prevedono (ad esempio BorderLayout, GridLayout e FlowLayout accettano nel costruttore parametri come vgap e hgap, per GridBagLayout si utilizzano invece gli Insets, nel BoxLayout si utilizza una rigid area, o metodi come create[Horizontal/Vertical]Glue/Strut).

    Ma non utilizzare appunto setSize sul frame.
  • Re: Swing, dimensioni componenti

    Ansharja ha scritto:


    Ho provato ad eseguire il programma qualche volta ma non ottengo risultati diversi, riesci a spiegare come cambiano le dimensioni o meglio a fornire due screenshot di esempio?
    Sono dei JPanel annidati,
    panel_1 allinea tutti i componenti in colonna tramite BoxLayout.Y_AXIS, e contiene Panel_2.
    panel_2 allinea tutti i componenti a sinistra tramite FlowLayout(FlowLayout.LEFT), e contiene Panel_3.
    panel_3 allinea tutti i componenti in tre righe ed una colonna tramite GridLayout(3,1), ed aggiunge:
    in riga 1, panel_4 che, allinea tutti i componenti in tre righe e due colonne tramite GridLayout(3,2), ed aggiunge 4 JLabel e due JTextField.
    Il risultato a video è il seguente:


    A seguire, riga 2 di panel_3 aggiunge una JTextArea, ed i componenti iniziano ad avere dimensioni spropositate:


    Infine, riga 3 di panel_3, aggiunge panel_5 che allinea tutti i componenti in una riga e due colonne, aggiungendo due JButton, che si ingrandiscono spropositamente anch'essi:


    Perchè le dimensioni dei componenti mi variano, invece di avere una loro dimensione fissa? mi si ingrandiscono pari al componente più grande presente in un JPanel, in questo caso, si sono ingranditi in propozione alle dimensioni della JTextArea.

    Infine, abituati a preferire il metodo pack () di JFrame rispetto al setSize ()
    Ci ho provato, ma la finestra asssume queste dimensioni:

    Tieni comunque presente che il modo corretto per costruire e rendere visibile il frame è eseguire il codice relativo all'interno del contesto dell'EDT (Event Dispatch Thread), tipicamente questo si fa inserendo il codice dentro al metodo invokeLater della classe SwingUtilities, trovi tonnellate di esempi in internet (o sui libri), a partire dal tutorial ufficiale.
    Perfetto, non lo sapevo, ora il codice è eseguito nell'event dispatching thread
    
    package gui;
    
    import java.awt.BorderLayout;
    import java.awt.Container;
    import java.awt.FlowLayout;
    import java.awt.GridLayout;
    
    import javax.swing.BoxLayout;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    public class Main {
    	private static void	createAndShowGUI() {
    		JFrame myjframe = new JFrame("PROVA MENU");
    		myjframe.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    		// myjframe.pack();
    		myjframe.setSize(800, 600);
    		
    		Container mycontainer = myjframe.getContentPane();
    		mycontainer.setLayout(new BorderLayout());
    		
    		JPanel panel_1 = new JPanel();
    		panel_1.setLayout(new BoxLayout(panel_1, BoxLayout.Y_AXIS));
    		
    		JPanel panel_2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
    		JPanel panel_3 = new JPanel(new GridLayout(3,1));
    		JPanel panel_4 = new JPanel(new GridLayout(3,2));
    		JPanel panel_5 = new JPanel(new GridLayout(1, 2));
    
    		JLabel empty = new JLabel();
    		JLabel label_1 = new JLabel("Valore campo JTextField 1: ");
    		JLabel label_2 = new JLabel("Valore campo JTextField 2: ");
    		JLabel label_3 = new JLabel("Valore campo JTextArea 1: ");
    		JTextField text_1 = new JTextField();
    		JTextField text_2 = new JTextField();
    		JTextArea text_3 = new JTextArea(7,21);
    		JButton bottone_1 = new JButton("Indietro");
    		JButton bottone_2 = new JButton("Avanti");
    		
    		panel_4.add(label_1);
    		panel_4.add(text_1);
    		panel_4.add(label_2);
    		panel_4.add(text_2);
    		panel_4.add(label_3);
    		panel_4.add(empty);
    		panel_5.add(bottone_1);
    		panel_5.add(bottone_2);
    		panel_3.add(panel_4);
    		panel_3.add(text_3);
    		panel_3.add(panel_5);
    		panel_2.add(panel_3);
    		panel_1.add(panel_2);
    
    		mycontainer.add(panel_1, BorderLayout.NORTH);
    
    		myjframe.setVisible(true);
    	}
    	
    	public static void main(String[] args) {
    		SwingUtilities.invokeLater(new Runnable() {
    		    public void run() {
    		        createAndShowGUI();
    		    }
    		});
    	}
    }
    
  • Re: Swing, dimensioni componenti

    Allora, l'immagine del tuo primo screenshot ha i componenti delle dimensioni "normali" perché non c'è la textarea fondamentalmente.

    Non ho capito se quell'immagine l'hai ottenuta lanciando il codice senza l'aggiunta della textarea, o se il codice è sempre lo stesso ma per qualche motivo non viene mostrata a video (penso più la prima, comunque se segui quelle due indicazioni che ti dicevo non dovrebbe più ricapitare, leggi il P.S per pack ()).

    Perché quando aggiungi la textarea i componenti assumono dimensioni spropositate? Perché stai usando un GridLayout.

    Nel GridLayout ogni cella ha la stessa dimensione, è una caratteristica del layout che a volte torna utilissima, in altri casi è un problema.
    La dimensione preferita di ogni cella viene calcolata singolarmente (in base ai componenti che contiene), ma poi ogni cella viene "tirata" per far coincidere la larghezza con la larghezza della cella più larga e l'altezza con l'altezza della cella più alta. Questo ovviamente per non rischiare di "nascondere" un componente.

    Nel tuo caso panel_3 ha un GridLayout, la cella di altezza maggiore è appunto quella contente la textarea, che si dimensione autonomamente grazie ai parametri che hai fornito al suo costruttore (7 come numero di righe e 21 come numero di colonne). Prende cioè le dimensioni necessarie a mostrare 7 righe e 21 colonne di testo, in base al Font corrente. Se provi a scrivere del testo nella text area vedrai che ci stanno appunto 7 righe, ma più colonne.

    Le altre celle, quindi, saranno allungate in altezza fino a raggiungere quella della textarea, quindi panel_4 (contentente label e textfield) e panel_5 (contenente i bottoni) saranno più alti di quanto non sarebbero di default.

    Ma perché nella textarea ci stanno più di 21 colonne di testo (ce ne starebbero comunque un po' di più di quelle specificate, ma in questo sono molte più delle necessarie)? Perché la cella più "larga" del layout è quella contenente le label e i field (panel_4), che ha larghezza doppia della label più larga. Questo perché al proprio interno panel_4 ha un altra griglia di celle, e il textfield sarà a sua volta allargato per raggiungere la dimensione massima, che in larghezza è quella della label (prova a passare 100 come parametro del costruttore di uno dei JTextField, e vedrai che la larghezza esplode, perché anche la label viene "tirata").

    Quindi la textarea e panel_5 verranno tirati in orizzontale fino a raggiungere la larghezza di panel_4.

    Poi al proprio interno i pannelli hanno ancora dei GridLayout, quindi i bottoni ad esempio si divideranno a metà la larghezza degli altri pannelli. Se tu provassi a usare un BorderLayout per panel_5, e inserire i bottoni in un panel_6 (con quel gridlayout che ora stai usando in panel_5), e aggiungere panel_6 in BorderLayout.WEST di panel_5, vedresti che il pannello viene sì allargato, ma tutto lo spazio in eccesso finisce in BorderLayout.CENTER (che è vuoto), mentre i bottoni sono dimensionati solamente alla dimensione massima dei due bottoni, ma non vengono più influenzati dal resto dei componenti.

    Questo dipende solo da come funzionano i layout manager, ognuno ha un proprio comportamento da scoprire

    PS: il pack () fa assumere quella dimensione al frame perché lo richiami quando il tuo container non ha alcun componente all'interno, quindi fondamentalmente in quel momento il frame si dimensiona ad una dimensione nulla.
    Devi invece richiamarlo dopo che hai aggiunto panel_1 al container, in questo modo le dimensioni saranno controllate correttamente, e poi potrai rendere visibile il frame.
Devi accedere o registrarti per scrivere nel forum
3 risposte