Swing layout manager/solution для замены динамически создаваемых и вложенных разделенных панелей?

Я работаю над текстовым редактором, в котором пользователь может разделить окно редактора по вертикали или по горизонтали любое количество раз (т. е. на любое количество панелей). Одно окно может быть разделено как по вертикали, так и по горизонтали (например, на 2 строки, та, которая содержит 3 столбца и т. д.). Каждая панель содержит JTextArea внутри JScrollPane и строку состояния.

До сих пор мой подход заключался в использовании вложенных JSplitPanes. Я изо всех сил пытался расположить разделители разделенных панелей так, чтобы пространство в окне было разделено поровну между всеми вертикально или горизонтально разделенными панелями. Я подошел довольно близко к тому, чтобы сделать это правильно, но мне пришлось прибегнуть к использованию setPreferredSize() в ряде мест (Должен ли я избегать использования методов set[Preferred|Maximum|Minimum]Size в Java Swing?).

Мне интересно, было бы проще/лучше полностью использовать другой подход. MultiSplitPane выглядит заманчиво. ..

Какой будет лучший макет/подход для моей ситуации?


person the-konapie    schedule 11.10.2012    source источник


Ответы (2)


Теперь я делаю некоторые предположения о том, что вы хотите. Я думаю, вы спрашиваете, есть ли простой способ динамически добавлять текстовые панели одинаковой ширины/высоты (разделяя их по вертикали или по горизонтали, но не смешивая их).

Пример горизонтального разделения

Снимок экрана с горизонтальным разделением

Пример вертикального разделения

Снимок экрана с вертикальным разделением

Если это так, я бы рекомендовал использовать BoxLayout - он делает это практически без какой-либо настройки.

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

public class SplitablePanel extends Box{

    Box container;
    Dimension minSize = new Dimension(400, 300);

    public SplitablePanel(int axis){
        super(BoxLayout.Y_AXIS);

        //Container that holds all the text areas
        container = new Box(axis);
        container.setAlignmentX(Box.LEFT_ALIGNMENT);
        add(container);

        JTextArea text = new JTextArea();
        container.add(new JScrollPane(text));

        //Button to add another pane
        JButton split = new JButton("Split");
        split.setAlignmentX(Box.LEFT_ALIGNMENT);
        split.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent e) {
                JTextArea text = new JTextArea();
                container.add(new JScrollPane(text));
                revalidate();
            }});
        add(split);

        //Button To switch Axis - more for demo purposes
        JButton axisChanger = new JButton("Change Axis");
        axisChanger.setAlignmentX(Box.LEFT_ALIGNMENT);
        axisChanger.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Box newContainer;
                if(((BoxLayout)container.getLayout()).getAxis() == BoxLayout.X_AXIS){
                    newContainer = Box.createVerticalBox();
                } else{
                    newContainer = Box.createHorizontalBox();
                }

                for(Component c : container.getComponents()){
                    container.remove(c);
                    newContainer.add(c);
                }
                remove(container);
                add(newContainer, 0);
                container = newContainer;
                container.setAlignmentX(Box.LEFT_ALIGNMENT);
                revalidate();
            }
        });
        add(axisChanger);

    }

    @Override
    public Dimension getPreferredSize() {
        Dimension result = super.getPreferredSize();
        result.width = result.width > minSize.width ? result.width : minSize.width;
        result.height = result.height > minSize.height ? result.height : minSize.height;
        return result;
    }

    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SplitablePanel(BoxLayout.X_AXIS));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

}
person Nick Rippe    schedule 12.10.2012
comment
Ник, спасибо за ответ. К сожалению, хотелось бы, чтобы одно окно можно было разделить как по вертикали, так и по горизонтали. Я извиняюсь, что мой вопрос не был ясен по этому поводу; Я отредактировал его для уточнения. Но все равно спасибо, что обратили мое внимание на макет Box. Прочитав код вашего примера, я также решил провести дополнительные исследования по переопределению getPreferredSize. Это то, чего мне не хватало. - person the-konapie; 14.10.2012

Я решил потратить некоторое время на изучение макета MultiSplitPane. Это похоже на хорошее решение.

Вот код, который я написал в качестве теста. По сути, это имитация макета, который динамически меняется, когда пользователь разбивает окно различными способами. Это немного длинно, но, возможно, будет полезно для тех, кто пытается изучить MultiSplitPane.

Конечный результат выглядит так:

введите здесь описание изображения

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.util.LinkedList;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import org.jdesktop.swingx.MultiSplitPane;
import org.jdesktop.swingx.MultiSplitLayout.*;

@SuppressWarnings("serial")
class MultiSplitPaneTest extends JFrame {
    private final static String sampleText;

    static {
        String text = "I'm working on a text editor in which the user is free to divide the editor window vertically or horizontally any number of times (ie, into any number of panes).\n";
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            sb.append(text);
        }
        sampleText = sb.toString();
    }

    private class MyScrollPane extends JScrollPane {
        public MyScrollPane(final Component view) {
            super(view);
        }
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(1440, 900);
        }
    }

    public MultiSplitPaneTest() {
        // The application opens with a window containing a single pane (a single text area).

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container cp = getContentPane();
        cp.setLayout(new BorderLayout());

        JTextArea ta1 = new JTextArea();
        ta1.setText("TEXT AREA 1\n" + sampleText);

        MyScrollPane sp1 = new MyScrollPane(ta1);
        sp1.setViewportView(ta1);

        cp.add(sp1, BorderLayout.CENTER);

        pack();
        setLocationRelativeTo(null);
        setVisible(true);

        // -------------------------------------------------

        // Let's say the user splits the window horizontally, creating a second pane.
        // We'll simulate that with the following code.

        JTextArea ta2 = new JTextArea();
        ta2.setText("TEXT AREA 2\n" + sampleText);

        MyScrollPane sp2 = new MyScrollPane(ta2);
        sp2.setViewportView(ta2);

        Leaf leaf1 = new Leaf("1");
        Leaf leaf2 = new Leaf("2");

        LinkedList<Node> rootChildren = new LinkedList<>();
        rootChildren.add(leaf1);
        rootChildren.add(new Divider());
        rootChildren.add(leaf2);

        Split root = new Split();
        root.setRowLayout(true);
        root.setChildren(rootChildren);

        MultiSplitPane multiSplitPane = new MultiSplitPane();
        multiSplitPane.getMultiSplitLayout().setModel(root);

        multiSplitPane.add(sp1, "1");
        multiSplitPane.add(sp2, "2");

        cp.remove(sp1);
        cp.add(multiSplitPane, BorderLayout.CENTER);

        // --------------------------------------------------

        // Let's say the user splits the window horizontally again, creating a new pane on the very left.

        JTextArea ta3 = new JTextArea();
        ta3.setText("TEXT AREA 3\n" + sampleText);

        MyScrollPane sp3 = new MyScrollPane(ta3);
        sp3.setViewportView(ta3);

        Leaf leaf3 = new Leaf("3");

        rootChildren.add(0, leaf3);
        rootChildren.add(1, new Divider());

        root.setChildren(rootChildren);

        multiSplitPane.add(sp3, "3");

        multiSplitPane.revalidate();

        // --------------------------------------------------

        // Let's say the user decides to remove the center pane (that is, the first pane that we started with).

        rootChildren.remove(2); // Remove leaf1.
        rootChildren.remove(2); // Remove the divider following leaf1.

        root.setChildren(rootChildren);

        multiSplitPane.remove(sp1);

        multiSplitPane.revalidate();

        // --------------------------------------------------

        // Let's say the user creates another pane, this time splitting the pane on the right vertically.

        rootChildren.remove(leaf2);

        JTextArea ta4 = new JTextArea();
        ta4.setText("TEXT AREA 4\n" + sampleText);

        MyScrollPane sp4 = new MyScrollPane(ta4);
        sp4.setViewportView(ta4);

        Leaf leaf4 = new Leaf("4");

        LinkedList<Node> branchChildren = new LinkedList<>();
        branchChildren.add(leaf2);
        branchChildren.add(new Divider());
        branchChildren.add(leaf4);

        Split branch = new Split();
        branch.setRowLayout(false);
        branch.setChildren(branchChildren);

        rootChildren.add(branch);

        root.setChildren(rootChildren);

        multiSplitPane.add(sp4, "4");

        multiSplitPane.revalidate();
    }
}
person the-konapie    schedule 14.10.2012