 |
Home | Changes | Index | Search | Go
This is the sample code for the example on John O'Conner's blog for a layout manager challenge using the RCLayout layout manager.
RCLayout (row-column) first started as a layout manager called LRLayout (left-right). LRLayout was designed to vertically center labels with a corresponding textfield. From there is was easy to generalize into more than 2 columns, which is when it was renamed to RCLayout. The initial version of RCLayout only allowed a component to occupy a single cell. The newest version of RCLayout allows components to span multiple rows and columns.
Figure 1. Basic layout
Figure 2. Showing that components fit nicely even when frame is smaller.
Figure 3. Showing RIGHT_TO_LEFT layout.
import javax.swing.*;
import java.awt.*;
public class AddressBookPanel extends JPanel {
static String[] names = new String[] {
"Bunny, Bugs", "Cat, Sylvester", "Coyote, Wile E.", "Devil, Tasmanian",
"Duck Daffy", "Fudd, Elmer", "Le Pew, Pepe", "Martian, Marvin"
};
JList nameList = new JList(names);
JScrollPane nameSP = new JScrollPane(nameList);
JLabel lastNameL = new JLabel("Last Name");
JLabel firstNameL = new JLabel("First Name");
JLabel phoneL = new JLabel("Phone");
JLabel emailL = new JLabel("Email");
JLabel addr1L = new JLabel("Address 1");
JLabel addr2L = new JLabel("Address 2");
JLabel cityL = new JLabel("City");
JLabel stateL = new JLabel("State");
JLabel postalCodeL = new JLabel("Postal Code");
JLabel countryL = new JLabel("Country");
JTextField lastNameT = new JTextField(12); // only need to specify length of textfields
JTextField firstNameT = new JTextField(12); // on the top row.
JTextField phoneT = new JTextField();
JTextField emailT = new JTextField();
JTextField addr1T = new JTextField();
JTextField addr2T = new JTextField();
JTextField cityT = new JTextField();
JTextField stateT = new JTextField();
JTextField postalCodeT = new JTextField();
JTextField countryT = new JTextField();
JButton newB = new JButton("New");
JButton deleteB = new JButton("Delete");
JButton editB = new JButton("Edit");
JButton saveB = new JButton("Save");
JButton cancelB = new JButton("Cancel");
private int gap = 5;
JPanel firstNameP = createPanel(firstNameL, firstNameT, gap);
JPanel emailP = createPanel(emailL, emailT, gap);
JPanel postalCodeP = createPanel(postalCodeL, postalCodeT, gap);
JPanel buttonP = createPanel(new JButton[] { newB, deleteB, editB, saveB, cancelB }, 10);
public AddressBookPanel() {
Dimension d = nameSP.getPreferredSize();
nameSP.setPreferredSize(new Dimension(d.width + 50, 0));
nameSP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
add(nameSP);
add(lastNameL);
add(lastNameT);
add(firstNameP);
add(phoneL);
add(phoneT);
add(emailP);
add(addr1L);
add(addr1T);
add(addr2L);
add(addr2T);
add(cityL);
add(cityT);
add(new JPanel()); // filler
add(stateL);
add(stateT);
add(postalCodeP);
add(countryL);
add(countryT);
add(buttonP);
RCLayout rc = new RCLayout(0, 4); // # of columns = 4 (rows are automatically calculated)
rc.setOverallInsets(new Insets(5, 5, 5, 5));
rc.getParam(nameSP).cover = new RCCover(0, 0, 1, 8);
rc.getParam(addr1T).cover = new RCCover(2, 2, 2, 1);
rc.getParam(addr2T).cover = new RCCover(2, 3, 2, 1);
rc.getParam(buttonP).cover = new RCCover(1, 7, 3, 1, new double[] { 0, 0, 0 }, null);
rc.getParam(buttonP).alignX = 0.5;
rc.getParam(buttonP).alignY = 0;
rc.getParamDefault().gap = gap;
rc.getParamCol(0).fillY = 1; // name list expands vertically
rc.getParamCol(0).gap = 20;
rc.getParamCol(1).alignX = 1; // labels are right justified
rc.getParamCol(1).alignY = 0.5; // labels are vertically centered to text fields
rc.getParamCol(2).fillX = 1;
rc.getParamCol(2).expand = 0.5; // columns 2 and 3 expand and shrink as neccessary
rc.getParamCol(2).shrink = 0.5;
rc.getParamCol(3).fillX = 1;
rc.getParamCol(3).expand = 0.5;
rc.getParamCol(3).shrink = 0.5;
rc.getParamRow(6).gap = 20;
rc.getParamRow(7).expand = 1; // all extra vertical space assigned to bottom row
setLayout(rc);
}
private static JPanel createPanel(JLabel l, JTextField t, int gap) {
RCLayout rc = new RCLayout(1, 0);
rc.getParamCol(0).gap = gap;
rc.getParamCol(0).alignY = 0.5;
rc.getParamCol(1).expand = 1;
rc.getParamCol(1).shrink = 1;
rc.getParamCol(1).fillX = 1;
JPanel p = new JPanel(rc);
p.add(l);
p.add(t);
return p;
}
private static JPanel createPanel(JButton[] arr, int gap) {
RCLayout rc = new RCLayout(1, 0);
rc.getParamDefault().gap = gap;
rc.getParamDefault().ignoreMax = new Boolean(true);
JPanel p = new JPanel(rc);
Dimension big = arr[0].getPreferredSize();
for (int i = 0; i < arr.length; i++) {
Dimension d = arr[i].getPreferredSize();
if (d.width > big.width) big = d;
p.add(arr[i]);
}
for (int i = 0; i < arr.length; i++) // make all buttons the same size
arr[i].setPreferredSize(big);
return p;
}
public static void main(String[] args) {
JPanel p = new AddressBookPanel();
JFrame f = new JFrame("Address Book Demo");
f.setContentPane(p);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Nested panels are used for the first name, email, postal code and buttons because these components do not have a one-to-one mapping into the 4 column structure. The button panel could also be created using FlowLayout?, but I don't like the wrapping behavior.
Answers to questions
-
Can it align components across other components? For example, can it both left and right align the 'phone' text field with the 'city' text field on the other side of the two address fields?
Yes.
Does your manager know about platform specific gaps and spacing among components. Just curious.
No. The UI designer is responsible for choosing the gap sizes.
Can the text fields, labels, etc grow and shrink when the text is localized?
Yes. The layout is based on the preferred size, which the components calculate based on the text they contain. The JList preferred width has been explicity set, however it should be the responsibility of the ListCellRenderer? to add extra white space.
Just how much code do I have to write? Java code? Declarative XML?
Java code. It is not just about quantity of code. RCLayout has a balance between understandability and quantity.
Can I nest panels?
Yes. Layout managers that require many nested panels create code that is hard to follow. RCLayout tries to minimize the number of nested panels required by allowing components to span multiple rows and columns.
Can it align components along a baseline?
Has general alignment, but not text baseline alignment.
What's the licensing?
BSD.
Will this work for bi-directional locales? How difficult would it be to mirror the layout for a right-to-left locale?
Yes, L2R? and R2L?. Simply call the applyComponentOrientation on the panel.
What else should we evaluate?
There should be more on the behavior when the window is resized. Can you specify which components grow if the frame is expanded? Can you specify which components shrink if the frame is shrunk? Can the expand and shrink behavior be set independently? Do the components expand and shrink smoothly? Does the layout manager account for pixel round off? Can the layout manager allocate a specific percentation of the window size to a row or column? Does the layout manager adhere to the minimum, maximum and preferred sizes? How many classes are required for layouts? (more classes imply a larger learning curve). What is the lowest version it can be compiled in? What is the total size of the compiled classes? Does the documentation explain the layout process in detail? How many examples does the documentation have?
|