D) Einen Layout-Manager anpassen

Das Layout anpassen

Die Wurzel unseres Problems liegt in dieser Zeile:

dim.height = Math.max(dim.height, d.height);

Dadurch wird der Container immer nur so groß wie seine größte Komponente. Es gilt also die Berechnung der Größe des Containers an unsere Bedürfnisse anzupassen:

public Dimension preferredLayoutSize(Container target) {

  synchronized (target.getTreeLock()) {
    int width = 0;
    int height = 0;
    Dimension dim = new Dimension(0, 0);
    Insets insets = target.getInsets();
    int nmembers = target.getComponentCount();
    boolean firstVisibleComponent = true;
    for (int i = 0; i < nmembers; i++) {
      Component m = target.getComponent(i);
      if (m.isVisible()) {
        Dimension d = m.getPreferredSize();
        if (firstVisibleComponent) {
          firstVisibleComponent = false;
          width = d.width;
          height = d.height;
        }
        else {
          if (width + d.width > target.getWidth() - insets.left - insets.right - getHgap() * 2) {
            dim.height += height + getVgap();
            dim.width = Math.max(dim.width, width);
            width = d.width;
            height = d.height;
          }
          else {
            width += d.width + getHgap();
            height = Math.max(d.height, height);
          }
        }
      }
    }
    dim.height += height + getVgap() * 2 + insets.top + insets.bottom;
    dim.width = Math.max(dim.width, width) + getHgap() * 2 + insets.left + insets.right;
    return dim;
  }
}

Sie sehen, dass die Änderungen nur sehr gering gegenüber den Originalimplementierungen sind. Konkret:

  • In Zeile 4 und 5 legen wir uns Variablen an, die die Größe des Containers speichern
  • In Zeile 7 holen wir uns die Insets des Containers, also die Größe des Rahmens
  • In Zeile 16 und 17 (falls es sich um die erste, sichtbare Komponente handelt) setzen wir die aktuelle Größe auf die bevorzugte Höhe und Breite des Containers
  • In Zeile 20 wird überprüft (falls es sich nicht um die erste, sichtbare Komponente handelt), ob die nächste Komponente in der selben Zeile die Breite des Containers überschreiten würde.
  • In Zeile 21, 22, 23 und 24 wird – falls der vorherige Punkt zutrifft – die Höhe des Containers und ggf. auch die Breite (falls die aktuelle Komponente alleine breiter als der komplette Container ist) angepasst.
  • In Zeile 27 und 28 werden die ganz normalen Schritte der Originalimplementierung ausgeführt – falls Zeile 20 nicht zutrifft
  • In Zeile 33 und 34 wird nun noch die Größe des Containers um die Größe des Rahmens und den Platzhaltern zwischen den Komponenten am Anfang und am Ende erweitert.

Selbiges Verhalten müssen Sie nun noch für unsere andere, überschriebene Methode implementieren:

public Dimension minimumLayoutSize(Container target) {

  synchronized (target.getTreeLock()) {
    int width = 0;
    int height = 0;
    Dimension dim = new Dimension(0, 0);
    Insets insets = target.getInsets();
    int nmembers = target.getComponentCount();
    boolean firstVisibleComponent = true;
    for (int i = 0; i < nmembers; i++) {
      Component m = target.getComponent(i);
      if (m.isVisible()) {
        Dimension d = m.getMinimumSize();
        if (firstVisibleComponent) {
          firstVisibleComponent = false;
          width = d.width;
          height = d.height;
        }
        else {
          if (width + d.width > target.getWidth() - insets.left - insets.right - getHgap() * 2) {
            dim.height += height + getVgap();
            dim.width = Math.max(dim.width, width);
            width = d.width;
            height = d.height;
          }
          else {
            width += d.width + getHgap();
            height = Math.max(d.height, height);
          }
        }
      }
    }
    dim.height += height + getVgap() * 2 + insets.top + insets.bottom;
    dim.width = Math.max(dim.width, width) + getHgap() * 2 + insets.left + insets.right;
    return dim;
  }
}

Beim Betrachten des Quellcodes für die eigentliche Ausrichtung der Komponenten (layoutContainer) stellen Sie fest, dass die Ausrichtung bereits die Höhe und Breite unseres Containers berücksichtigt und somit nicht noch extra angepasst werden muss. Es genügt völlig die Berechnung der Größe des Containers anzupassen.

Beachten Sie noch beim Anpassen eines Layout-Managers, dass Sie nicht auf ggf. vorhandene, private Attribute zugreifen können, sondern stattdessen die Getter- und Setter-Methoden verwenden müssen.

Gegenüberstellung

Um beide Layouts miteinander vergleichen zu können, erweitern wir unser Testprogramm ein wenig:

package de.jbb.tools;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;

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

public class Test {

  public static void main(String[] args) {

    JFrame frame = new JFrame("ExtendedFlowLayout vs. FlowLayout");
    JPanel normalFlow = new JPanel();
    JPanel extendedFlow = new JPanel();
    JLabel n1 = new JLabel("Das ist ein Label mit viel Text (FL)");
    JLabel n2 = new JLabel("Das ist noch ein Label mit viel Text (FL)");
    JLabel n3 = new JLabel("Und das ist ein drittes Label mit viel Text (FL)");
    JLabel e1 = new JLabel("Das ist ein Label mit viel Text (EFL)");
    JLabel e2 = new JLabel("Das ist noch ein Label mit viel Text (EFL)");
    JLabel e3 = new JLabel("Und das ist ein drittes Label mit viel Text (EFL)");

    n1.setBackground(Color.YELLOW);
    e1.setBackground(Color.YELLOW);
    n2.setBackground(Color.RED);
    e2.setBackground(Color.RED);
    n3.setBackground(Color.GREEN);
    e3.setBackground(Color.GREEN);

    n1.setOpaque(true);
    n2.setOpaque(true);
    n3.setOpaque(true);
    e1.setOpaque(true);
    e2.setOpaque(true);
    e3.setOpaque(true);

    frame.setLayout(new BorderLayout());
    normalFlow.setLayout(new FlowLayout(FlowLayout.LEFT));
    extendedFlow.setLayout(new ExtendedFlowLayout(FlowLayout.LEFT));

    frame.add(normalFlow, BorderLayout.NORTH);
    frame.add(extendedFlow, BorderLayout.SOUTH);
    frame.add(new JLabel("Platzhalter"), BorderLayout.CENTER);

    normalFlow.add(n1);
    normalFlow.add(n2);
    normalFlow.add(n3);

    extendedFlow.add(e1);
    extendedFlow.add(e2);
    extendedFlow.add(e3);

    frame.setSize(450, 400);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  }
}

Wir erhalten folgendes Ergebnis:

Ergebnis

Lesen Sie auf der nächsten Seite das Fazit und eine Zusammenfassung.

Ähnliche Beiträge

  • D) Mehrsprachigkeit mit Bundles in Java

    Bestimmt kennen Sie das: Sie haben ein großes Programm mit vielen GUI-Beschriftungen. Nun kommt ein User auf die Idee, dass es das Programm ja auch in anderen Sprachen geben könnte. Also suchen Sie alle setText()-Methoden aus Ihrem Code und erstellen zum Beispiel noch eine englische Version. Sehr aufwendig. Vor allem, weil es einen vorgefertigten Standard…

  • D) Plugin-Entwicklung in Java

    Zusammenfassung PluginLoader package de.jbb.plug.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import de.jbb.plug.interf.Pluggable; public class PluginLoader { public static List<Pluggable> loadPlugins(File plugDir) throws IOException { File[] plugJars = plugDir.listFiles(new JARFileFilter()); ClassLoader cl = new URLClassLoader(PluginLoader.fileArrayToURLArray(plugJars)); List<Class<Pluggable>> plugClasses = PluginLoader.extractClassesFromJARs(plugJars, cl); return PluginLoader.createPluggableObjects(plugClasses);…

  • D) Dependency Injection mit Spring

    Scopes Ein Objektnetz zeichnet sich neben dem Typ seiner Komponenten auch durch die Kardinalität der enthaltenen Objekte aus. Auf gut Deutsch heißt das, dass Spring eine Möglichkeit braucht, herauszufinden, ob immer das selbe Objekt benutzt werden soll oder doch lieber immer ein Neues. Die Standardlösung für dieses Problem ist es, immer dasselbe Objekt zu benutzen….

  • D) Formatierte Texteingabe

    Wenn Sie editierbaren Text in Java darstellen möchten, haben Sie mehrere Möglichkeiten. Sie können bspw. eine JTextArea oder ein JTextField verwenden und simple Formatierungen daran vornehmen, die das komplette Textfeld betreffen (bspw. die Hintergrundfarbe oder Schrift verändern). Sollen sich bestimmte Textabschnitte voneinander unterscheiden, haben Sie zuerst einmal ein Problem, weil das mit einer JTextArea oder…

  • D) Text in JTextField vorblenden

    Sie kennen diese Funktionalität aus vielen Bereichen: Sie tippen etwas in ein Eingabefeld und das Programm schlägt Ihnen automatisch einen Text vor. Bspw. bei der Autovervollständigung in der Browser-Adressleiste oder in vielen Textverarbeitungsprogrammen bei der Eingabe des aktuellen Datums. Leider gibt es eine solche Eingabemöglichkeit nicht im Standard-Java. Sie müssen selbst Hand anlegen.

  • D) Code beim Beenden ausführen

    Oft kommt es vor, dass etwas beim Beenden des eigenen Programms geschehen soll – z. B. um Einstellungen zu speichern oder eine Datenbankverbindung sauber zu trennen. Dieses Kapitel zeigt Ihnen, wie Sie etwas beim Beenden Ihres Programms automatisiert ausführen lassen können – ganz gleich wie Ihr Programm beendet wird. Hierzu verwenden wir die addShutdownHook Objektmethode…