Größe der Parent-Component dynamisch an children anpassen

blz

Bekanntes Mitglied
Hallo,
ich möchte gerne folgendes programmieren:

ein blaues JPanel soll untereinander zwei JTextAreas enthalten, in der oberen steht default-mäßig Hallo.
Die JTextAreas sollen linksbündig im Panel so positioniert sein, dass drumherum ein Rand von 10px ist.
Wenn man Text eingibt, sei es in die Breite, oder auch durch Enter eine weitere Zeile erzeugt, soll sich die Größe des beinhaltenden Panels automatisch anpassen.
(Das Panel kommt dann wiederum in einen JFrame.)

asdf.png

Ich bin relativ unerfahren in der Grafikprogrammierung.
Ich bin mir nicht sicher, welchen LayoutManager ich für das Panel benutzen sollte. GridLayout wäre plausibel, fällt aber doch dadurch raus, dass ich die 2 Textfelder nicht einfach oben positioniere sondern den Rand brauche.

Und wie teile ich der Parentcomponent mit, dass sich was getan hat? Ist es sinnvoll, an jede Textarea einen KeyListener zu heften und wenn sich was tut, dann die Größe des Textfelds abzufragen?
Was mich zur Frage führt, wie man das zu Beginn löst, denn da ist z. B. die Breite immer 0.

Vielen Dank für jede Hilfe!
 
Das, was Du vorhast, muss erst genauer spezifiziert werden. Hintergrund ist, dass das Fenster letztlich die maximale Größe vorgibt. D. h. die Fenstergröße definiert den zur Verfügung stehenden Platz und ausgehend davon werden die Komponenten im Fenster verteilt. Insofern stellt sich die Frage, welcher Platz denn genutzt werden soll, um die JTextArea zu vergrößern?
 
OK, der frame hat eine fixe Größe und das panel darin wird nicht so groß werden wie der frame, soll heißen, sooo viel Text wird da nicht eingegeben werden.
Ich fände es sogar recht schön, wenn man nicht 'aus dem frame rausschreiben' könnte.

Im Fenster soll später allerdings noch eine Menuleiste sein und auch noch links eine menubar, auf der man dann ZB das Element mit den zwei textareas auswählen und um Fenster platzieren kann.
 
OK der frame hat ne fixe Größe.
In das beschriebene Element wird wohl nie so viel Text eingegeben, dass das über den frame hinausgehen würde. Falls doch, fände ich eine automatische Begrenzung durch die frame Größe schön.
In den frame sollen später noch eine Menuleiste oben und eine menubar links hinkommen. Auf letzterer kann man dann ZB das beschriebene Element auswählen und mit der Maus im frame platzieren.
 
Java:
import java.awt.BorderLayout;
import javax.swing.*;

public class Test {

    public void run() {
        JTextArea area = new JTextArea();
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(area, BorderLayout.NORTH);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
 
Hallo, vielen Dank, aber das sieht bei mir so aus:
1585996171583.png
Die Area geht über die ganze Breite.
Sie sollte ja nur so breit sein wie ein Beispielwort, das drinsteht, zB Hallo.
Außerdem sollte sie ja noch auf einem farbigen Panel liegen, das man darunter sieht.
Kann man das noch einbauen?
 
Ich bin nun so weit, dass das Prinzip anschaulich klar wird, aber es sind noch etliche Probleme drin:

Java:
import java.awt.Color;

import javax.swing.JPanel;

public class BackgroundPanel extends JPanel {
    
    public BackgroundPanel() {
        super();       
        setBackground(Color.BLUE);
    }
}

Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.geom.Point2D;

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

public class MyEditablePanel extends JPanel {
    
    private static final long serialVersionUID = 1L;

    JTextArea jta1;
    JTextArea jta2;
      
    public MyEditablePanel() {
        super();
    }
    
    public MyEditablePanel(Color c) {
        super();
        // The GridLayout class is a layout manager that lays out a container's components in a rectangular grid. (Raster)
        // The container is divided into equal-sized rectangles, and one component is placed in each rectangle.
        setLayout(new GridLayout(2, 1, 2, 2));
        setBackground(c);
              
        jta1 = new JTextArea(1, 0);
        jta1.setLocation(2, 2);
        jta1.setAlignmentX(0.5f);       
        add(jta1);             
        
        jta2 = new JTextArea("...");       
        add(jta2);
    }
}

Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.Point2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;


public class MyEditableFrame extends JFrame {
    
    private JPanel contentPane;

    public MyEditableFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(0, 0, 500, 500);
        setResizable(false);
        setLayout(null);
        
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout());
        setContentPane(contentPane);
        
        MyEditablePanel panel0 = new MyEditablePanel(Color.PINK);
       
        BackgroundPanel bp = new BackgroundPanel();
        bp.setPreferredSize(new Dimension(50, 50));
        bp.add(panel0);
        panel0.setLocation(5, 5);
        
        contentPane.add(bp, BorderLayout.CENTER);
        
        setVisible(true);       
    }
    
    public static void main(String[] args) {
        MyEditableFrame mef = new MyEditableFrame();
    }
}

Probleme:
  1. Das GridLayout scheint keine gute Wahl zu sein: Denn wenn ich in der ersten Area Enter drücke, wird die zweite Zelle und damit auch die zweite Textarea genauso groß.
  2. Wie bekomme ich es hin, dass der Layoutmanager die Größe meiner Komponenten (hier des blauen Panels) nicht selbstständig entscheidet, sondern dass er es nur so groß macht, wie angegeben?
  3. Wie bekomme ich die Textareas auf dem blauen Panel so positioniert, dass sie links oben mit zwei Pixeln Abstand vom Rand starten?
 
Das ist das, was ich ursprünglich meinte: die Frage ist, wie der freie Platz verteilt werden soll.

Jede Komponente besitzt eine Mindest- und Maximalgröße, außerdem eine bevorzugte Größe. Der LayoutManager verteilt den zur Verfügung stehenden Platz (= freier Platz) im Fenster bzw. Container i. d. R. unter Berücksichtigung der bevorzugten Größe, ggf. auch unter Berücksichtigung der anderen Größen.

Java:
import java.awt.*;
import javax.swing.*;

public class Test {

    public void run() {
        JTextArea area = new JTextArea();
        area.setText("AA");

        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints cc = new GridBagConstraints();
        JPanel panel = new JPanel(layout);
        panel.setBackground(Color.YELLOW);

        cc.anchor = GridBagConstraints.FIRST_LINE_START;
        cc.weightx = 1;
        cc.weighty = 1;
        panel.add(area, cc);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(panel);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
 
Hallo,
ich möchte gerne folgendes programmieren:

ein blaues JPanel soll untereinander zwei JTextAreas enthalten, in der oberen steht default-mäßig Hallo.
Ich habe einmal ein relatives Layout benötigt. Damit ist gemeint, dass die Position und die Dimension der Komponenten in Prozenten relativ zur Dimension des Containers angegeben werden. Bei einem Resize des Containers kann dann automatisch angepasst werden. Es kommt dabei ein null Layout zum Einsatz damit jede Komponente punktgenau platziert werden kann. Vielleicht hilft dir das bei Deinem Problem weiter.
Java:
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class start {
    public static void main(String[] args) {
        final int WIDTH = 500;
        final int HEIGHT = 400;
        JFrame frame = new JFrame("Relatives Layout Resizeable");
        RelativeLayoutPanel layout = createLayout(WIDTH, HEIGHT);
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                layout.resize(frame.getWidth(), frame.getHeight());
            }
        });
        layout.addToContainer(frame.getContentPane());
        frame.pack();
        frame.setVisible(true);
    }

    private static RelativeLayoutPanel createLayout(int width, int height) {
        RelativeLayoutPanel panel = new RelativeLayoutPanel(500, 400);
        panel.setBackGround(Color.BLUE);
        LocationInfo[] info = { new LocationInfo(.02f, .02f, .451f, .05f), new LocationInfo(.5f, .02f, .451f, .05f),
                new LocationInfo(.02f, .09f, .451f, .7f), new LocationInfo(.5f, .09f, .451f, .3f) };
        Component[] component = { new MyLabel("Left Text"), new MyLabel("Right Text"), new JScrollPane(new JTextArea()),
                new JScrollPane(new JTextArea()) };
        panel.setBorder(BorderFactory.createLineBorder(Color.WHITE));

        for (int i = 0; i < info.length; i++)
            panel.add(new FlexibleLayoutComponent(component[i], info[i]));
        return panel;
    }
}
Java:
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.util.Vector;
import javax.swing.JPanel;
import javax.swing.border.Border;

/**
 * Allows to position Components using relative position and size.
 *
 * @see LocationInfo
 *
 */
public class RelativeLayoutPanel {
    private JPanel panel = new JPanel();
    private Vector<FlexibleLayoutComponent> components = new Vector<FlexibleLayoutComponent>();

    public RelativeLayoutPanel(int width, int height) {
        panel.setLayout(null);
        resize(width, height);
    }
    public Component addToContainer(Container container) {
        return container.add(panel);
    }
    public Component add(FlexibleLayoutComponent c) {
        if (components.contains(c))
            return null;
        components.add(c);
        Component tmp = c.addToContainer(panel);
        resize(panel.getPreferredSize());
        return tmp;
    }
    public void repaint() {
        panel.repaint();
    }
    public void remove(FlexibleLayoutComponent c) {
        if (!components.contains(c))
            return;
        components.remove(c);
        c.removeFromContainer(panel);
    }
    public void removeAll() {
        components.removeAllElements();
        panel.removeAll();
    }
    public void removeFromContainer(Container container) {
        container.remove(panel);
    }
    public void resize(Dimension dim) {
        resize(dim.width, dim.height);
    }
    public void resize(int width, int height) {
        panel.setPreferredSize(new Dimension(width, height));
        for (FlexibleLayoutComponent c : components)
            c.resize(width, height);
    }
    public void setBackGround(Color bg) {
        panel.setBackground(bg);
    }
    public void setBorder(Border b) {
        panel.setBorder(b);
    }
}
Java:
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;

public class FlexibleLayoutComponent {
    private LocationInfo info;
    private Component component;

    public FlexibleLayoutComponent(Component component, LocationInfo info) {
        this.component = component;
        this.info = info;
    }
    public Component addToContainer(Container contenpane) {
        return contenpane.add(component);
    }
    public void removeFromContainer(Container contenpane) {
        contenpane.remove(component);
    }
    /**
     * Call {@link #resize(int, int)} after setting new LocatinInfo.
     *
     * @param info
     */
    public void setLocationInfo(LocationInfo info) {
        this.info = info;
    }
    /**
     * Updates component's bounds relative to given dimension.
     *
     * @param dim
     *            Container's dimension.
     */
    public void resize(Dimension dim) {
        resize(dim.width, dim.height);
    }
    /**
     * @see #resize(Dimension)
     * @param width
     *            Container's width.
     * @param height
     *            Container's height.
     */
    public void resize(int width, int height) {
        component.setBounds(info.getX(width), info.getY(height), info.getWidth(width), info.getHeight(height));
    }
}
Java:
/**
 * Manages position and dimension info used by {@link RelativeLayoutPanel}
 */
public class LocationInfo {
    final public float percentageX;
    final public float percentageY;
    final public float percentageWidth;
    final public float percentageHeight;
    /**
     *
     * @param x
     *            Relative x-coordinate.
     * @param y
     *            Relative y-coordinate.
     * @param percentageWidth
     *            Relative width.
     * @param percentageHeight
     *            Relative height.
     * @see RelativeLayoutPanel
     */
    public LocationInfo(float percentageX, float percentageY, float percentageWidth, float percentageHeight)
            throws IllegalArgumentException {
        if (!isInRange(percentageX) || !isInRange(percentageY) || !isInRange(percentageWidth)
                || !isInRange(percentageHeight))
            throw new IllegalArgumentException("LocationInfo parmeters have to be within range [0-1]!");
        this.percentageX = percentageX;
        this.percentageY = percentageY;
        this.percentageWidth = percentageWidth;
        this.percentageHeight = percentageHeight;
    }
    private boolean isInRange(float value) {
        return value >= 0 && value <= 1;
    }
    public int getHeight(int height) {
        return (int) (height * percentageHeight);
    }
    public int getWidth(int width) {
        return (int) (width * percentageWidth);
    }
    public int getX(int width) {
        return (int) (width * percentageX);
    }
    public int getY(int height) {
        return (int) (height * percentageY);
    }
}

Java:
import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;

@SuppressWarnings("serial")
public class MyLabel extends JLabel {
    public MyLabel(String text) {
        super(text, SwingConstants.CENTER);
        setVerticalAlignment(JLabel.BOTTOM);
        setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
        setBackground(Color.DARK_GRAY);
        setForeground(Color.WHITE);
        setOpaque(true);
    }

    /**
     * Override to adapt font's size to new height.
     *
     * @param x
     * @param y
     * @param width
     * @param height
     */
    @Override
    public void setBounds(int x, int y, int width, int height) {
        super.setBounds(x, y, width, height);
        setFont(new Font(getName(), Font.BOLD, (int) (height * .8)));
    }
}

Für dich interessant ist die Klasse start und MyLabel. Die anderen kannst Du einfach mit Copy Paste verwenden.
 
Hallo,
vielen Dank für die Antworten. Ich bin mir nicht sicher, ob das, was ich möchte, richtig verstanden wurde:
Wenn ich in die zwei untereinanderliegenden Textareas tippe, verändert sich deren Breite und evtl. auch Höhe.
Ich möchte dann, dass der Container, das Panel, das die beiden Areas enthält, auch entsprechend mitwächst bzw. kleiner wird.
Ich habe mal auf Basis von mihes Klasse etwas programmiert, was das Ganze glaube ich ganz gut illustriert.

Ein großes Problem dabei ist momentan, dass ich das Panel zwischendurch immer erst auf eine große Größe setzen muss, damit man abfragen kann, wie groß das Textfeld denn nach Eingabe eines Zeichens nun ist. Erst dann kann man ausrechnen, wie groß das Panel eigentlich sein müsste und seine Größe wieder korrekt setzen.

Beim Enter-Drücken klappt es auch erst, wenn man danach ein Zeichen eingegeben hat ...

Man sieht aber, was ich will: Der gelbe Rand soll fix so breit wie vorgegeben die beiden Areas umschließen, diese sollen nur gerade so groß sein wie der enthaltene Text.

Java:
package package3;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;

public class Test {

    int actMaxWidth;
    int actSumOfHeights;
    
    
    int paddLeftRight;
    int paddCell1Top;    
    int paddCell1Bottom;    
    int paddCell2Top;    
    int paddCell2Bottom;
    
    JTextArea area1;
    JTextArea area2;
    
    JPanel panel;
    
    public void run() {
        
        paddLeftRight = 15;            
        paddCell1Top = 15;        
        paddCell1Bottom = 5;        
        paddCell2Top = 5;        
        paddCell2Bottom = 15;        
        
        area1 = new JTextArea();
        area1.setText("Hallo!");
        area1.addKeyListener(new KeyAdapter() {                
            @Override
            public void keyTyped(KeyEvent e) {
                panelSizeAdaption();
                System.out.println("Key pressed");
            }        
        });

        area2 = new JTextArea();
        area2.setText("B");
        
        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints cc = new GridBagConstraints();
        panel = new JPanel(layout);
        panel.setBackground(Color.YELLOW);
        panel.setSize(new Dimension(250, 250));

        // Place the component in the corner of its display area where the first line of text on a page would normally begin for the current ComponentOrientation.
        cc.anchor = GridBagConstraints.FIRST_LINE_START;
        cc.gridx = 0;
        cc.gridy = 0;
        cc.weightx = 1;
        cc.weighty = 1;
        cc.fill = GridBagConstraints.NONE;
        // padding (Abstand zw. Zellrand und Element): top, left, bottom, right
        cc.insets = new Insets(paddCell1Top, paddLeftRight, paddCell1Bottom, paddLeftRight);

        panel.add(area1, cc);
        System.out.println(area1.getHeight());

        cc.insets.set(paddCell2Top, paddLeftRight, paddCell2Bottom, paddLeftRight);
        cc.gridy = 1;
        panel.add(area2, cc);        
        
        JFrame frame = new JFrame();
        frame.setLayout(null);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(panel);
        frame.setSize(800, 600);
        System.out.println(area1.getHeight());
        System.out.println(area1.getHeight());

        frame.setVisible(true);
        

        panelSizeAdaption();
    }
    
    private void panelSizeAdaption() {
         panel.setSize(new Dimension(250, 250));
        
        int width = getMaxWidth(); 
        boolean change = false;
            
        if (actMaxWidth != width) {
            actMaxWidth = width;
            change = true;
        }
            
        int heights = getSumOfHeights();
        
        if (actSumOfHeights != heights) {            
            actSumOfHeights = heights;
            change = true;
        }
        
        if (change) {                      
            panel.setSize(2 * paddLeftRight + actMaxWidth, paddCell1Top + paddCell1Bottom + paddCell2Top + paddCell2Bottom + actSumOfHeights);
            panel.repaint();
            change = false;
        }                
    }
    
    private int getSumOfHeights() {
        return area1.getHeight() + area2.getHeight();        
    }
    
    
    private int getMaxWidth() {
        int width = area1.getWidth();
        if (area2.getWidth() > width)
            width = area2.getWidth();
        return width;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
 
Ich möchte dann, dass der Container, das Panel, das die beiden Areas enthält, auch entsprechend mitwächst bzw. kleiner wird.
Java:
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;

public class Test {

    int actMaxWidth;
    int actSumOfHeights;
    
    
    int paddLeftRight;
    int paddCell1Top;    
    int paddCell1Bottom;    
    int paddCell2Top;    
    int paddCell2Bottom;
    
    JTextArea area1;
    JTextArea area2;
    
    JPanel panel;
    
    public void run() {
        
        paddLeftRight = 15;            
        paddCell1Top = 15;        
        paddCell1Bottom = 5;        
        paddCell2Top = 5;        
        paddCell2Bottom = 15;        
        
        area1 = new JTextArea();
        area1.setText("Hallo!");
        area2 = new JTextArea();
        area2.setText("B");
        
        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints cc = new GridBagConstraints();
        cc.fill = GridBagConstraints.NONE;       
        panel = new JPanel(layout);
        panel.setBackground(Color.YELLOW);

        cc.anchor = GridBagConstraints.FIRST_LINE_START;
        cc.gridx = 0;
        cc.gridy = 0;
        cc.weightx = 1;
        cc.weighty = 0;

        cc.insets = new Insets(paddCell1Top, paddLeftRight, paddCell1Bottom, paddLeftRight);
        panel.add(area1, cc);

        cc.insets.set(paddCell2Top, paddLeftRight, paddCell2Bottom, paddLeftRight);
        cc.gridy = 1;
        cc.weightx = 0;
        cc.weighty = 0;
        panel.add(area2, cc);       

        JPanel content = new JPanel(new GridBagLayout());
        cc = new GridBagConstraints();
        cc.anchor = GridBagConstraints.FIRST_LINE_START;
        cc.gridx = 0; 
        cc.gridy = 0;
        cc.weightx = 1;
        cc.weighty = 1;
        content.add(panel, cc);
            
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(content);
        frame.setSize(800, 600);

        frame.setVisible(true);
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
 
Hi,
vielen Dank, das funktioniert ja wunderbar 🙂

Was ich nicht ganz verstehe, ist die Funktion von weight:

Soweit ich das recherchiert habe, weist man Komponenten prozentual ein weight zu. Wenn sich dann die Größe der Parent-Komponente verändert, wird der zusätzliche/wenigere Platz entsprechend der angegebenen Prozentsätze auf die beinhalteten Komponenten verteilt.

Beobachtungen/Erste Frage:

  1. Für die Funktionalität meiner Anforderung scheine ich die weights nicht zu brauchen, wenn ich sie auskommentiere, funktioniert es genauso.
  2. Wenn ich die weights des Panels content auf 1 habe, ist das Panel links oben in der Ecke, bei den Werten Null ist es zentriert in der Frame-Mitte. Warum?
  3. Wenn ich die weights der Textareas modifiziere, kann ich keine Veränderungen beobachten.

Zweite Frage:
Warum brauche ich das zweite Panel? Wo ist der Unterschied, wenn ich dem Frame direkt das gelbe Panel hinzufüge?

Dritte Frage:
Obwohl der anchor vom Panel content auf "FIRST_LINE_START" gesetzt wurde, erscheint das Panel zentriert, wenn ich die weights auskommentiere.
Warum?
 
Soweit ich das recherchiert habe, weist man Komponenten prozentual ein weight zu. Wenn sich dann die Größe der Parent-Komponente verändert, wird der zusätzliche/wenigere Platz entsprechend der angegebenen Prozentsätze auf die beinhalteten Komponenten verteilt.
Fast. Der Platz wird anteilig auf die Spalten und Zeilen des Gitters verteilt. Gilt für alle Komponenten weightx == 0, dann kann zusätzlicher Platz keiner Spalte zugewiesen werden. Stattdessen wir der Platz zwischen dem Gitter und der Kante des Containers eingefügt, der die Komponenten enthält - und zwar auf beiden Seiten gleichmäßig. D. h. das Gitter wird horizontal zentriert. Analog gilt dies für weighty. Damit sind dürfte sowohl die erste als auch die dritte Frage beantwortet sein.

Auch die zweite Frage hängt damit zusammen. Das content-Panel sorgt einfach für Platz, in dem sich das gelbe panel "ausbreiten" kann. Würdest Du panel direkt zum JFrame hinzufügen, würde dessen BorderLayout dafür sorgen, dass das panel das gesamte Fenster ausfüllt. Es wäre also von vornherein so groß wie das Fenster (alles gelb). Um das zu verhindern, gibt es content.
 
ok, danke für diese wirklich sehr schönen Erklärungen!
Fürs Verständnis (nur weightx-bezogen):
1586160874652.png
0. Mit "dann kann zusätzlicher Platz keiner Spalte zugewiesen werden" meinst du, "keiner Komponente"? Denn einer Spalte wird er ja letztlich schon zugewiesen?!
1. Ich gebe allen drei Buttons weightx = 0; dann darf keine Komponente breiter gemacht werden. Also bleibt nichts anderes übrig, als die blau markierten Abstände zu vergrößern?
2. Gäbe es nur die zweite Zeile des Layouts, dann läge der linke Button "eng" in der linken Spalte und es würde nur die rechte Spalte größer werden?
 
Zuletzt bearbeitet:
Und:
Bzgl. Frage 2: Ich habe ausprobiert, dem frame ein GridBagLayout zu geben und dann das gelbe Panel direkt dem frame hinzuzufügen. Dann funktionierts auch. Kann mans so genauso machen oder hat es noch irgendwelche anderen Vorteile, das Panel zwischenzuschalten?
 
0. Richtig, es geht zunächst nicht um die Komponente, sondern um das Grid. Die Komponente betrifft dann die fill-Eigenschaft.
1. Nein, Du müsstest außenrum noch den Container zeichnen und dann wäre der Abstand des Containers zum Grid gemeint.
2. Nein, dann gäbe es keine zwei Spalten 🙂

Ich habe ausprobiert, dem frame ein GridBagLayout zu geben und dann das gelbe Panel direkt dem frame hinzuzufügen. Dann funktionierts auch. Kann mans so genauso machen oder hat es noch irgendwelche anderen Vorteile, das Panel zwischenzuschalten?
Das funktioniert natürlich, da das "content pane" des JFrame ja auch nur ein Container ist.
 
Achso, da ist mein Denkfehler (oder zumindest einer davon) die ganze Zeit:
Das Grid, das Raster, also "die Tabelle", füllt nicht automatisch den ganzen Container aus?!!
Also hellblau ist der Container, gelb ist das Grid, das Raster und in grün der Abstand, der übrig bleibt:
1586176022977.png
Und man müsste es so darstellen, dass links und rechts der Buttons quasi kein Abstand innerhalb der einzelnen Zellen ist, wenn man davon aus geht, dass weightx == 0 gilt.
(So wie es jetzt abgebildet ist, wäre weightx beider Spalten vielleicht jeweils grob == 0,25, d. h. die Hälfte des gesamten Platzes wird innerhalb der "Tabelle" verteilt, die andere Hälfte außerhalb.)
 
Zuletzt bearbeitet:
Das Grid, das Raster, also "die Tabelle", füllt nicht automatisch den ganzen Container aus?!!
Richtig.
Und man müsste es so darstellen, dass links und rechts der Buttons quasi kein Abstand innerhalb der einzelnen Zellen ist, wenn man davon aus geht, dass weightx == 0 gilt.
Richtig.
So wie es jetzt abgebildet ist, wäre weightx beider Spalten vielleicht jeweils grob == 0,25, d. h. die Hälfte des gesamten Platzes wird innerhalb der "Tabelle" verteilt, die andere Hälfte außerhalb
Falsch 🙂 Sobald irgendein weightx > 0 ist, füllt das Grid den Container in der Horizontalen aus. Welche Zahlen Du für weightx verwendest, spielt keine Rolle - es geht um Verhältnismäßigkeiten. Sprich: weightx einer Spalte / Summe der weightx aller Spalten, falls es ein weightx > 0 gibt.
 
1. Ah ok, logisch. Das bedeutet, man kann nur dann ein Grid haben, das den Container nicht ausfüllt, wenn wirklich sämtliche weightx == 0 sind.
2. Dh der letzte abgebildete Fall ließe sich mit einem GridBagLayout so nicht realisieren. Man könnte höchstens ein vierspaltiges Grid nehmen, in die linke und rechte Spalte nichts reintun und diese dann mit einem passenden weightx (dann wohl jeweils 0,25 etwa) entsprechend breit machen.
3. Jetzt hab ich aber noch ein Problem: Es funktioniert eigentlich sehr gut, aber immer wenn ich Text eingebe, nachdem ich das Panel verschoben habe, springt es immer wieder zur ursprünglichen Position zurück.
 
Und, Nachtrag: Ist auf dem äußeren Panel überhaupt auch zwingend das recht komplizierte GridBagLayout notwendig, oder ginge es auch mit einem anderen Layout (einfacher)?
 
1. genau
2. ja, wobei die Zahl, wie geschrieben, völlig egal ist. Du kannst auch 0, 1, 1 und 0 verwenden. Dann bleiben die beiden äußeren Spalten in ihrer Größe fix und die inneren bekommen zusätzlichen Platz zu gleichen Teilen.
3. Panel verschoben?!?
 
zu 2: Was heißt, die anderen [äußeren] beiden bleiben in ihrer Größe fix. Wenn die ganze Spalte keinerlei Komponente enthalten, haben sie doch auch keine Breite, oder nicht?

zu 3: Hatte übersehen, dass ich das nicht eingangs geschrieben hatte. Ich möchte gerne mehrere dieser gelben Panels erzeugen und im Fenster mit der Maus herumschieben können. Mit bisher einem funktioniert das auch wunderbar, außer, dass, wenn ich es verschiebe und dann Text eingebe, es immer wieder zur Anfangsposition zurückspringt ...
edit: ich habe dem Panel einen MouseListener hinzugefügt und setze die Positon des Panels in MouseDragged(...) mittels panel.setLocation(...)
 
Zuletzt bearbeitet:
Was soll das heißen? Dass man ein null-Layout verwenden sollte?
Ja, wenn Du ein Panel frei verschieben willst, stört ein LayoutManager, dessen Aufgabe es ja ist, Position und Größe der Komponenten festzulegen.

zu 2: Was heißt, die anderen [äußeren] beiden bleiben in ihrer Größe fix. Wenn die ganze Spalte keinerlei Komponente enthalten, haben sie doch auch keine Breite, oder nicht?
Ich meinte, wenn Du in den äußeren Spalten z. B. leere Panels (mit gegebener, bevorzugter Größe) einfügst.

Und, Nachtrag: Ist auf dem äußeren Panel überhaupt auch zwingend das recht komplizierte GridBagLayout notwendig, oder ginge es auch mit einem anderen Layout (einfacher)?
Das musst Du probieren, es könnte z. B. auch ein FlowLayout funktionieren.

zu 3: Hatte übersehen, dass ich das nicht eingangs geschrieben hatte. Ich möchte gerne mehrere dieser gelben Panels erzeugen und im Fenster mit der Maus herumschieben können.
Immer diese Nebensächlichkeiten... s. oben.
 
Dann habe ich jetzt glaube ich größere Schwierigkeiten:

Momentan habe ich ein gelbes Panel mit GridBagLayout, das zwei Textareas enthält.
GridbagLayout deshalb, damit ich via Insets die Randbreite um die Textfelder herum setzen kann und ich die beiden Textareas schön untereinander anordnen kann.

Dann habe ich das blaue JPanel, auch mit GridBagLayout, damit das gelbe Panel darauf nicht maximal groß wird (wie es z. B. bei einem BorderLayout des blauen Panels der Fall wäre), sondern eben nur so groß wie nötig (durch weightx = 0).

Das blaue Panel setze ich schließlich in meinen JFrame.

1586301444529.png

Nun habe ich dem gelben Panel einen MouseListener hinzugefügt, damit ich es mit der Maus greifen und verschieben kann. Mit setLocation wird die neue Position gesetzt. Klappt so lange, bis sich die Größe des gelben Panels verändert. Dann springt es wieder an die ursprüngliche Position zurück.

Oben wurde geraten, man dürfe auf dem blauen Panel keinen LayoutManager verwenden, was einleuchtend klang.
Gleichzeitig ist der LayoutManager aber dafür verantwortlich, dass das gelbe Panel nur so groß wie nötig ist und nicht beliebig groß wird. Eine feste Größe kann ich auch nicht angeben, da die ja von den Textareas abhängt.

Wie könnte ich das lösen??
 
In dem Fall lohnt es sich, über einen eigenen LayoutManager nachzudenken:
Java:
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;

public class Test {

    int actMaxWidth;
    int actSumOfHeights;
    
    
    int paddLeftRight;
    int paddCell1Top;    
    int paddCell1Bottom;    
    int paddCell2Top;    
    int paddCell2Bottom;
    
    JTextArea area1;
    JTextArea area2;
    
    JPanel panel;

    LayoutManager freeLayout = new LayoutManager() {
        public void addLayoutComponent(String name, Component comp) {
        }

        public void removeLayoutComponent(Component comp) {
        }

        public Dimension minimumLayoutSize(Container parent)  {
            return null;
        }

        public Dimension preferredLayoutSize(Container parent)  {
            return null;
        }

        public void layoutContainer(Container parent) {
           for (Component c : parent.getComponents()) {
                 Rectangle bounds = c.getBounds();
                 Dimension pref = c.getPreferredSize();
                 c.setBounds(bounds.x, bounds.y, pref.width, pref.height);
            }
        }
    };
    
    public void run() {
        
        paddLeftRight = 15;            
        paddCell1Top = 15;        
        paddCell1Bottom = 5;        
        paddCell2Top = 5;        
        paddCell2Bottom = 15;        
        
        area1 = new JTextArea();
        area1.setText("Hallo!");
        area2 = new JTextArea();
        area2.setText("B");
        
        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints cc = new GridBagConstraints();
        cc.fill = GridBagConstraints.NONE;       
        panel = new JPanel(layout);
        panel.setBackground(Color.YELLOW);

        cc.anchor = GridBagConstraints.FIRST_LINE_START;
        cc.gridx = 0;
        cc.gridy = 0;
        cc.weightx = 1;
        cc.weighty = 0;

        cc.insets = new Insets(paddCell1Top, paddLeftRight, paddCell1Bottom, paddLeftRight);
        panel.add(area1, cc);

        cc.insets.set(paddCell2Top, paddLeftRight, paddCell2Bottom, paddLeftRight);
        cc.gridy = 1;
        cc.weightx = 0;
        cc.weighty = 0;
        panel.add(area2, cc);       

        JPanel content = new JPanel(freeLayout);
        content.add(panel);
        panel.setBounds(50, 50, panel.getPreferredSize().width,
                          panel.getPreferredSize().height);
           
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(content);
        frame.setSize(800, 600);

        frame.setVisible(true);
     }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
 
ok ... saucool!
Ich muss das erste einmal verstehen/nachvollziehen, was der eigene LayoutManager da macht. Letztlich geht es ja "nur" um die Methode
layoutContainer: Für alle Elemente im ParentPanel werden x und y der linken oberen Ecke und die PreferredSize abgefragt und dann werden die x und y-Werte unverändert wieder gesetzt und als Breite und Höhe werden die Preferred-Werte gesetzt...
Also letztlich geht es wohl darum, als tatsächliche Größe die PreferredSize zu setzen.
Wieso löst das mein bisheriges Problem?

Eine Sache klappt noch nicht ganz: wenn ich zwei der yellowPanels erzeuge und sie im Fenster herumschiebe, dann bleibt manchmal eines vor/hinter dem anderen hängen, wenn man sie übereinander schiebt; manchmal klappts aber auch problemlos.
Könnte das etwas hiermit zu tun haben:
"Swing's painting architecture assumes the children of a JComponent do not overlap. If a JComponent's LayoutManager allows children to overlap, the JComponent must override isOptimizedDrawingEnabled to return false."
https://docs.oracle.com/javase/7/docs/api/java/awt/LayoutManager.html
 
Könnte das etwas hiermit zu tun haben:
Wäre denkbar. In jedem Fall sollte man sich daran halten.

die x und y-Werte unverändert wieder gesetzt und als Breite und Höhe werden die Preferred-Werte gesetzt...
Also letztlich geht es wohl darum, als tatsächliche Größe die PreferredSize zu setzen.
Wieso löst das mein bisheriges Problem?
Das Zurückspringen wird verhindert, gerade weil x- und y-Werte unverändert gesetzt werden, und dass sich das Panel in der Größe verändert wird erreicht, indem die preferred size gesetzt wird.

In der Regel funktioniert das Layout etwas anders: zu Beginn berechnet der LayoutManager die preferred size des Containers. Durch die Größe des Fensters (oder anderen Dingen) steht dann meist mehr oder weniger Platz zur Verfügung und der LayoutManager passt die Verteilung bzw. Größen der Komponenten entsprechend an.
 
Hat sich erledigt: Das Layout passt perfekt, ich hatte einen Fehler in meinen MouseAdapter-Methoden.
Super!
Herzlichen Dank für die vielen Erklärungen, das hat mich alles ein großes Stück weiter gebracht!
 

Zurück
Oben