问题
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). A single window can be divided both vertically and horizontally (for example, 2 rows, one which contains 3 columns, etc). Each pane contains a JTextArea inside a JScrollPane and a status bar.
My approach thus far has been to use nested JSplitPanes. I've struggled to arrange the split pane dividers so that the space in the window is divided equally among all the vertically- or horizontally-split panes. I've come quite close to getting it right, but I've had to resort to using setPreferredSize() in a number of places (Should I avoid the use of set[Preferred|Maximum|Minimum]Size methods in Java Swing?).
I am wondering if it would be easier/better to take a different approach entirely. MultiSplitPane looks tempting...
What would be the best layout/approach for my situation?
回答1:
Now I'm making some assumptions about what you want. I thinking your asking if there's an easy way to dynamically add text panes all the same width/height (split either vertically or horizontally, but not mixing the two).
Example of Horizontal Split
Example of Vertical Split
If that's the case, I'd recommend using the BoxLayout - it does this without hardly any configuration at all.
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);
}
}
回答2:
I decided to invest some time in learning the MultiSplitPane layout. It looks like a good solution.
Here is the code I wrote as a test. It's essentially a simulation of a layout that changes dynamically as the user splits the window in various ways. It's a bit lengthy, but maybe it will be helpful to someone who is trying to learn MultiSplitPane.
The end result looks like this:
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();
}
}
来源:https://stackoverflow.com/questions/12848566/swing-layout-manager-solution-to-replace-dynamically-created-and-nested-split-pa