Explorerähnliches Layout

Hiho erstmal

Ich hätte da ein Anliegen an die Layoutexperten unter euch, da ich darin nicht so bewandt bin oder Erfahrungen habe^^

Undzwar hätte ich gerne für ein Programm in dem sich eine gewisse Anzahl von JButtons befinden und diese sollen ähnlich zum Windows Explorer z.B. angeordnet werden.

Ich stell mir das ungefähr so vor:
Die Buttons haben alle eine feste gleiche größe (Quadratisch) und sollen solange Platz da ist in einer Reihe angeordnet werden. Falls das Fenster nicht breit genug ist soll eine neue Zeile angefangen werden undsoweiter. Im Prinzip klappt das auch schon mit nem eigenen Layoutmanager allerdings fänd ichs schön, dass wenn die Buttons außerhalb des sichtbaren bereichs sind Scrollbalken kommen. Hab zwar auch schon mit nem JScrollPane versucht das zu machen allerdings krieg ich da kein Umbruch hin falls der Horizontale Platz zu wenig ist.

Danke schonmal im vorraus

MFG
Clayn

Das wäre mit einem FlowLayout zu machen. Wird standardmäßig im [japi]JPanel[/japi] benutzt.

Okay aber wie gesagt am besten wären dann noch scrollbalken. Wenn das nicht so einfach ist, kann ichs auch nach hinten verschieben erstmal. Das FlowLayout hat mich erstmal gestört da die Größe der Buttons nie so war wie ih sollte aber das hab ich hinzwischen hinbekommen

Edit:
Im Prinzip ist das Problem einen Umbruch im JPanel noch zu kriegen wenns in nem JScrollPane ist. Ich denk mal das geht irgendwie oder?

Standardmäßig hat das FlowLayout IIRC keinen vernünftigen Zeilenumbruch. Da hatte mal jemand, der damals noch “Hobbit_im_Blutrausch” hieß, eine angepasste Implementierung auf java-blog-buch.de gepostet…

Naja also im Prinzip macht das FlowLayout schon das was ich möchte allerdings die Eigenschaften des JScrollPanes wären Ideal. Ginge denn sowas wenn ich die Buttons direkt ins JScrollPane mache?

Richtig. Deine Komponenten brauchen eine eingestellte „bevorzugte Größe“ (PreferredSize). Denn diese zieht das FlowLayout mit in seine Berechnungen ein.

[QUOTE=Clayn;59370]Edit:
Im Prinzip ist das Problem einen Umbruch im JPanel noch zu kriegen wenns in nem JScrollPane ist. Ich denk mal das geht irgendwie oder?[/QUOTE]
Das geht, wenn das zu scrollende Panel auch eine PreferredSize bekommt (die Größer als die Darstellungsfläche der JScrollPane ist).

Schreibe dir ein KSKB und probiere es aus. :wink:

Mäh das ist aber doof mit 2 einbandagierten Fingern^^ Naja werd ich mich aber mal ranhauen bissle zu basteln^^ Aber wenigstens weiß ich jetzt das ich nicht mit irgendwelchen eigenen LayoutManagern rumpfuschen muss danke dafür

Hab’s gerade mal probiert, selbst mit dem ExtendedFlowLayout muss man wohl anscheinend noch ein eigenes JPanel machen, dass Scrollable implementiert, und die Viewport Width Trackt. Lasse mich aber gerne eines besseren belehren.

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Rectangle;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;


public class ButtonListTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
    
    private static class ScrollableJPanel extends JPanel implements Scrollable
    {
        @Override
        public Dimension getPreferredScrollableViewportSize()
        {
            return getPreferredSize();
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect,
            int orientation, int direction)
        {
            return 1;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect,
            int orientation, int direction)
        {
            return 100;
        }

        @Override
        public boolean getScrollableTracksViewportWidth()
        {
            return true;
        }

        @Override
        public boolean getScrollableTracksViewportHeight()
        {
            return false;
        }
        
    }
    
    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JPanel panel = new ScrollableJPanel();
        panel.setLayout(new ExtendedFlowLayout());
        JScrollPane scrollPane = new JScrollPane(panel);
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        f.getContentPane().add(scrollPane);
        
        for (int i=0; i<20; i++)
        {
            JButton b = new JButton(String.valueOf(i));
            b.setPreferredSize(new Dimension(150,150));
            panel.add(b);
        }
        
        f.setSize(600, 600);
        f.setVisible(true);
    }
    
}


//From http://www.java-blog-buch.de/dflowlayout-mit-zeilenumbruch/
class ExtendedFlowLayout extends FlowLayout {

 private static final long serialVersionUID = 1L;

 public ExtendedFlowLayout() {
     super();
 }

 public ExtendedFlowLayout(int arg0, int arg1, int arg2) {
     super(arg0, arg1, arg2);
 }

 public ExtendedFlowLayout(int arg0) {
     super(arg0);
 }

 @Override
 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;
     }
 }

 @Override
 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;
     }
 }
}

Danke Marco das ist doch ziemhlich genau das was ich mir gewünscht habe. nun muss ich schasuen wie ichs am besten bei mir einbaue und dann kanns weitergehen^^ Dann steh ich ja kurz vor ner funktionierenden Demo.
Ich mag Layouts und Guis einfach nicht machen^^

Sehr schönes Layout!

Ich hab mal eine kommentierte Version davon für mich erstellt (sie hat auch sinnvolle Namen in den Parametern der Konstruktoren, ich finde sowas hilfreich), vielleicht ist sie für jemanden nützlich:

Lies mehr…

[spoiler]```package basics.gui.ui.layout;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;

/**

  • An adapted FlowLayout. It prevents elements from being hided at the right
  • end of the containing component.
  • @author Error | OPNsense
  •     Comments and changed names of variables by Christian Dühl.
    
  •     Blog from 20.12.2008. Changes from 04.09.2013.
    

*/

public class ExtendedFlowLayout extends FlowLayout {

private static final long serialVersionUID = -4842928220870781073L;

/**
 * Constructs a new extended flow layout manager with a centered alignment
 * and a default 5-unit horizontal and vertical gap.
 */
public ExtendedFlowLayout() {
    super();
}

/**
 * Creates a new extended flow layout manager with the indicated alignment
 * and the indicated horizontal and vertical gaps.                           <br /><br />
 *
 * The value of the alignment argument must be one of
 * ExtendedFlowLayout.LEFT, ExtendedFlowLayout.RIGHT,
 * ExtendedFlowLayout.CENTER, ExtendedFlowLayout.LEADING, or
 * ExtendedFlowLayout.TRAILING.
 *
 * @param align
 *            the alignment value
 * @param hgap
 *            the horizontal gap between components and between the
 *            components and the borders of the Container
 * @param vgap
 *            the vertical gap between components and between the components
 *            and the borders of the Container
 */
public ExtendedFlowLayout(int align, int hgap, int vgap) {
    super(align, hgap, vgap);
}

/**
 * Constructs a new extended flow layout manager with the specified
 * alignment and a default 5-unit horizontal and vertical gap. The value of
 * the alignment argument must be one of ExtendedFlowLayout.LEFT,
 * ExtendedFlowLayout.RIGHT, ExtendedFlowLayout.CENTER,
 * ExtendedFlowLayout.LEADING, or ExtendedFlowLayout.TRAILING.
 *
 * @param align
 *            the alignment value
 */
public ExtendedFlowLayout(int align) {
    super(align);
}

/**
 * Returns the preferred dimensions for this layout given the <i>visible</i>
 * components in the specified target container.
 *
 * @param target
 *            the container that needs to be laid out
 * @return the preferred dimensions to lay out the subcomponents of the
 *         specified container
 * @see Container
 * @see #minimumLayoutSize
 * @see java.awt.Container#getPreferredSize
 */
@Override
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;
    }
}

/**
 * Returns the minimum dimensions needed to layout the <i>visible</i>
 * components contained in the specified target container.
 *
 * @param target
 *            the container that needs to be laid out
 * @return the minimum dimensions to lay out the subcomponents of the
 *         specified container
 * @see #preferredLayoutSize
 * @see java.awt.Container
 * @see java.awt.Container#doLayout
 */
@Override
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;
    }
}

}```

Testprogramm:


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

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

/**
 * A test program for the adapted FlowLayout.
 *
 * @author http://www.java-blog-buch.de/dflowlayout-mit-zeilenumbruch/
 *         Blog from 20.12.2008.
 */


public class ExtendedFlowLayoutTest {

    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(ExtendedFlowLayout.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);
    }

}```[/spoiler]