Rather annoying issue with a Swing combo box - java

https://youtu.be/8djixdHNoEQ - this video shows what I am facing, in that the JComboBox seems to be display multiple times/leaving an after-image.
Here is how I have setup the combo box:
private String[] list = { "Inches/Centimeters", "Miles/Kilometres", "Pounds/Kilograms", "Gallons/Litres", "Feet/Metres", "Celcius/Kelvin", "Acres/Hectare" }; //the String array the ComboBox uses
private JComboBox<String> conversionCombo; //defining the JComboBox.
conversionCombo = new JComboBox<String>(list); // creating JComboBox
Other than defining the list it uses, the comb box itself and creating the combo box, there is no other code which interacts with this component. (other than the code which sets the Combo box up, and adds it to the screen etc.)
Example program below.
thing1 (driver class, sets up JFrame):
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class thing1 {
public static void main(String[] args) throws IOException {
JFrame frame = new JFrame("Thing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
thing2 thing2 = new thing2();
frame.getContentPane().add(thing2);
frame.pack();
frame.setVisible(true);
}
}
thing2 (sets up UI element - in this case just the JComboBox):
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComboBox;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class thing2 extends JPanel {
/**
* Declaring all variables and components to be used within the GUI
*/
private String[] list = { "Inches/Centimeters", "Miles/Kilometres", "Pounds/Kilograms", "Gallons/Litres", "Feet/Metres", "Celcius/Kelvin", "Acres/Hectare" };
thing2() {
JComboBox<String> conversionCombo = new JComboBox<String>(list); // creating JComboBox
add(conversionCombo);
setPreferredSize(new Dimension(800, 80));
setBackground(Color.WHITE);
}
}
Should be able to compile this and see the issue. In this case if you are to select anything from the dropdown and move your mouse to the right of the combobox whilst your mouse is 'within' it (ex: https://i.imgur.com/Z2Slrl3.gifv), "after-images" will appear.

Related

Swing - Remove selected border and change arrow color of JComboBox

I'm trying to remove the selected border of a JComboBox (top arrow) and change the arrow color (bottom arrow)
If possible, how do I remove the outer border? (the darker gray one)
How do I go about doing this?
So you can do this by implementing a ComboBoxUI, or, actually, subclassing BasicComboBoxUI for example. The second option is better, because you only need to tweak some code in some places and you are ready (instead of implementing your own ComboBoxUI from scratch). So follows the code which does what you requested (hopefully):
import java.awt.Color;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.plaf.basic.BasicComboBoxUI;
public class MainComboBoxUI {
public static class MyComboBoxUI extends BasicComboBoxUI {
#Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.uninstallBorder(comboBox); //Uninstalls the LAF border for both button and label of combo box.
}
#Override
protected JButton createArrowButton() {
//Feel free to play with the colors:
final Color background = Color.CYAN.darker(); //Default is UIManager.getColor("ComboBox.buttonBackground").
final Color pressedButtonBorderColor = Color.RED; //Default is UIManager.getColor("ComboBox.buttonShadow"). The color of the border of the button, while it is pressed.
final Color triangle = Color.BLACK; //Default is UIManager.getColor("ComboBox.buttonDarkShadow"). The color of the triangle.
final Color highlight = background; //Default is UIManager.getColor("ComboBox.buttonHighlight"). Another color to show the button as highlighted.
final JButton button = new BasicArrowButton(BasicArrowButton.SOUTH, background, pressedButtonBorderColor, triangle, highlight);
button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
return button;
}
}
public static void main(final String[] args) {
final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
combo.setUI(new MyComboBoxUI());
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(combo);
panel.setBackground(Color.RED.darker());
final JFrame frame = new JFrame("MainComboBoxUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
First of, we remove the border from the combo box inside installDefaults() method. That will remove the border from both the button and the label. Then we mimic the createArrowButton() method of BasicComboBoxUI to create our own custom button. The default implementation of BasicComboBoxUI.createArrowButton() creates a BasicArrowButton with some construction-time colors. Those colors change the button's triangle's color for example and the background color.
That is for creating a BasicArrowButton. Although, if you want better control over the button (for example instead of a triangle you need an icon and/or some text on the button) then you can simply create a plain JButton inside the createArrowButton() instead of a BasicArrowButton. Then you can initialize it inside the same method, or, preferably modify it inside the configureArrowButton(). Like the following code does:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.basic.BasicComboBoxUI;
public class MainComboBoxUI2 {
public static class MyComboBoxUI extends BasicComboBoxUI {
#Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.uninstallBorder(comboBox);
}
#Override
protected JButton createArrowButton() {
final JButton button = new JButton("V");
button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
return button;
}
#Override
public void configureArrowButton() {
super.configureArrowButton(); //Do not forget this!
arrowButton.setBackground(Color.CYAN.darker());
arrowButton.setForeground(Color.BLUE);
}
}
public static void main(final String[] args) {
final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
combo.setUI(new MyComboBoxUI());
combo.setPreferredSize(new Dimension(150, 45)); //Needed to be able to show the button's text.
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(combo);
panel.setBackground(Color.RED.darker());
final JFrame frame = new JFrame("MainComboBoxUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
The only problem with the custom plain JButton approach (ie the second approach) is that the LayoutManager of the JComboBox will not take into account the preferred size of the arrow button if a protected flag inside the UI (named squareButton) is true. If that flag is true, then the button's width is set equal to the button's height, which in turn is set equal to the combo box height. That's why in the second approach, I set the preferred size of the combo box manually to give it a height of 45 pixels which is enough to be able to display the text inside the button (in that specific case, where the text is equal to "V").
You can set the value of this flag for a single UI instance manually by overriding installDefaults() method in the BasicComboBoxUI and setting it to false for example. Otherwise, you can set its value for all combo boxes via calling UIManager.put("ComboBox.squareButton", Boolean.FALSE); in your code early enough (ie before the instantiation of each combo box). But as it turns out, because I tested it, when setting this value to false the size of the button overlaps the display area's size (the display area is where the selected value of the combo box is visible).
After some more digging, I found out that the cause of this problem is that the UI's getMinimumSize(...) method does not take into account the button's insets... That means the default border of the JButton around the text/icon is not accounted for. So after setting the squareButton flag to false, you will also need to override this method.
Putting them all together, you get the following final approach/code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicComboBoxUI;
public class MainComboBoxUI3 {
public static class MyComboBoxUI extends BasicComboBoxUI {
#Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.uninstallBorder(comboBox);
}
#Override
protected JButton createArrowButton() {
final JButton button = new JButton("Drop");
button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
return button;
}
#Override
public void configureArrowButton() {
super.configureArrowButton(); //Do not forget this!
arrowButton.setBackground(Color.CYAN.darker());
arrowButton.setForeground(Color.BLUE);
//arrowButton.setBorder(javax.swing.BorderFactory.createEmptyBorder());
}
//Overrided getMinimumSize to take into account the button's insets for both width and height:
#Override
public Dimension getMinimumSize(final JComponent c) {
final Dimension mindim = super.getMinimumSize(c);
final Insets buttonInsets = arrowButton.getInsets();
return new Dimension(mindim.width + buttonInsets.left + buttonInsets.right, mindim.height + buttonInsets.top + buttonInsets.bottom);
}
}
public static void main(final String[] args) {
UIManager.put("ComboBox.squareButton", Boolean.FALSE); //Set all the combo boxes' button to non-square...
final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
combo.setUI(new MyComboBoxUI());
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(combo);
panel.setBackground(Color.RED.darker());
final JFrame frame = new JFrame("MainComboBoxUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
And the result looks like this:
Finally, if you want smaller border for the drop down button (ie the one with text "Drop" in the above picture), you can set the border of the arrowButton to (for example) javax.swing.BorderFactory.createEmptyBorder() inside the configureArrowButton() method...

Adding New Item to JComboBox Does Not Display

I have a JComboBox that is editable. When the user enters a new item, I want that added to the list and display it as the selected item. I am able to add it to the list but I cannot seem to make it display as the selected item. By default I display an empty string ("") which is what the user would edit to add the new item.
public class EventComboBoxListener implements ActionListener {
private JComboBox<String> eventBox=null;
public EventComboBoxListener(JComboBox<String> event_) {
eventBox=event_;
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Selected: " + eventBox.getSelectedItem());
System.out.println(", Position: " + eventBox.getSelectedIndex());
if (eventBox.getSelectedIndex() < 0) {
eventBox.addItem(eventBox.getSelectedItem().toString());
eventBox.setSelectedItem(eventBox.getSelectedItem().toString());
}
}
}
It doesn't make sense to me that I have to use setSelectedItem with the getSelectedItem. That it does not work is no surprise but I don't know what else to do. The newly added item shows up in the list as it should but how do I make it the selected item in the display at the same time? I can select it after but that should not be necessary.
Added MVCE:
Main
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
public class Test {
public static void main(String[] args) {
String[] list= {"","A","B","C"};
TestTableModel model=new TestTableModel(null,new String[] {"col1","col2"});
JTable table=new JTable(model);
JDialog dialog=new JDialog();
JScrollPane scroller=new JScrollPane(table);
JComboBox<String> box=new JComboBox<String>(list);
box.setEditable(true);
box.setSelectedIndex(0);
box.addActionListener(new EventComboBoxListener(box));
JTextField field=new JTextField();
field.setPreferredSize(new Dimension(75,30));
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setLayout(new FlowLayout());
dialog.setSize(new Dimension(400,100));
dialog.add(scroller);
dialog.pack();
dialog.setVisible(true);
table.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(box));
table.getColumnModel().getColumn(1).setCellEditor(new DefaultCellEditor(field));
model.insertRow(0,new Object[] {"","placeholder"});
}
}
TestTableModel class
import javax.swing.table.DefaultTableModel;
public class TestTableModel extends DefaultTableModel {
/**
*
*/
private static final long serialVersionUID = 1L;
public TestTableModel(Object[][] data_,String[] columnNames_) {
super(data_,columnNames_);
}
}
First of all some comments about the MCVE (since you will be including one with every question in the future).
We expect the code so be in a single source file so we can easily copy/paste compile and test. We don't want 3 files lying around on our machine that we need clean up after testing.
Only relevant code directly related to the problem should be included. Why do you have the TestTableModel class. Are the "column names" relevant to the problem? The point is always test your MCVE using standard JDK classes when possible.
Regarding the EventComboListener class. Again, this can be added to the combo box by using and annoymouse inner class or a lambda. This keeps the code in a single class.
The newly added item shows up in the list as it should but how do I make it the selected item in the display at the same time?
I found that playing with your MCVE the ActionListener of the combo box is invoked at different times.
So my suggestion is to add the ActionListener to the editor of the combo box. Then we know for sure the ActionListener is only invoked when you press the Enter key. Once you press the Enter key the editor is stopped and the value is saved to the model.
So the logic would be something like:
//box.addActionListener(new EventComboBoxListener(box));
ComboBoxEditor editor = box.getEditor();
JTextField textField = (JTextField)editor.getEditorComponent();
textField.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
String item = textField.getText();
DefaultComboBoxModel model = (DefaultComboBoxModel)box.getModel();
if (model.getIndexOf(item) == -1)
{
box.addItem(item);
box.setSelectedIndex( box.getItemCount() - 1 );
}
}
});
So the trick is to set the select index (not the selected item). But first the logic checks to make sure the item has not already been added to the combo box.

Using JTabbedPane with JPanels that need to share data

In my application, I have several custom JPanels added to a JTabbedPane. A single JPanel offers a piece of functionality, I've added them to JTabbedPane so that the user can switch between modules easily.
All of those custom JPanels operate on the same set of data, meaning if one of the modules has to change something in the data (for instance, a List), all other panels should be aware of that change and react accordingly.
However, with JTabbedPane, you first need to instantiate those JPanels to add them to JTabbedPane - and you do it once.
I have one problem - suppose user adds something to the collection in panel A (which is shared by all those panels) and switches to panel B. What should happen in this case? How is B supposed to know that something has been added to that collection?
My idea was to simply detect a tab switch event, and call the method of B to take the new data into account. But I feel this is not how it should be done.
What could you suggest?
In the example below, each panel in the tabbed pane has its own JComboBox that listens to a common ComboBoxModel. When the common model is updated, by clicking Update, each listening JComboBox sees the change.
I never need any two combo boxes to share a model. My panels contain completely different JComponent instances, but the look and data those components display depend on several collections common to all of those panels.
You may be able to leverage the observer pattern, examined here. The exact details depend on your use case, but the PropertyChangeListener examples are worth studying.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.Date;
import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
/**
* #see https://stackoverflow.com/a/37514928/230513
* #see https://stackoverflow.com/questions/8752037
* #see https://stackoverflow.com/a/37222598/230513
*/
public class TabTest {
private static final int N = 5;
private final JTabbedPane pane = new JTabbedPane();
private final DefaultComboBoxModel model = new DefaultComboBoxModel(
new String[]{"Alpher", "Bethe", "Gamow", "Dirac", "Einstein"});
public TabTest() {
for (int i = 0; i < N; i++) {
Color color = Color.getHSBColor((float) i / N, 1, 1);
pane.add("Tab " + String.valueOf(i), new TabContent(i, color));
}
}
private class TabContent extends JPanel {
private TabContent(int i, Color color) {
setOpaque(true);
setBackground(color);
add(new JComboBox(model));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
}
private void display() {
JFrame f = new JFrame("TabColors");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(pane);
JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p.add(new JButton(new AbstractAction("Update") {
#Override
public void actionPerformed(ActionEvent e) {
model.addElement(new Date());
}
}));
f.add(p, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new TabTest().display();
}
});
}
}

findComponentAt returns self for JList

Ok, so I think my problem here stems from a fundamental lack of understanding as to what a JList is actually doing.
I have a simple example class MyList.
package test;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
public class MyList extends JList<String> {
public MyList(Vector<String> myStrings){
super(myStrings);
this.setCellRenderer(new ListCellRenderer<String>(){
#Override
public Component getListCellRendererComponent(
JList<? extends String> list, String value, int index,
boolean isSelected, boolean cellHasFocus) {
String myString = value.toString();
return new JLabel(myString+" (idx "+index+")");
}
});
this.addMouseListener(new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e) {
MyList p = (MyList) e.getSource();
Component c = p.findComponentAt(e.getX(), e.getY());
System.out.println("am clicking on "+c.getClass()+" in "+p.getClass());
}
});
}
public static void main(String[] args){
Vector<String> myStrings = new Vector<String>();
myStrings.addAll(Arrays.asList("str 1","str 2","str 3"));
MyList mine = new MyList(myStrings);
JFrame myFrame = new JFrame();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(500, 500);
myFrame.add(mine);
myFrame.setVisible(true);
}
}
The output of clicking on any JLabel inside the list is the same:
am clicking on class test.MyList in class test.MyList
I expected it to be:
am clicking on class javax.swing.JLabel in class test.MyList
I think this as something to do with how the ListCellRenderer works, but I am not sure. Why isn't the component I get back from p.findComponentAt(e.getX(), e.getY()); a JLabel? What is going on here?
Why isn't the component I get back from p.findComponentAt(e.getX(), e.getY()); a JLabel? What is going on here?
JLists and JTables put in extra effort to efficiently display a lot of information while minimizing overhead doing so. One way they do this is by displaying information via renderers. What you are seeing in a JList are not true JLabels but images of JLabels (or whatever components are used as the renderer). The JList uses the same renderer to create images for each element in the column. So say you have a JList with 1000 items, rather than create 1000 JLabels, the JList creates just one, uses it to create JLabel images, and then displays the images. So what you are clicking on is truly a JList and not a JLabel.
To test this further, try using a JTextField as a renderer, and you'll quickly find that the cell does not behave like a JTextField and that you can't edit the information that the it displays.

Understanding layout managers -- I'm not, please enlighten me

I'm just not understanding why things are being resized when I call the validate() and repaint() methods. I'm struggling to understand this. Essentially, my program is meant to display like this. I have a main frame into which I plug the various JPanels that I'm extending for the various functions of my photo album. The class below is the NewAlbum class that is supposed to allow the user to select files and make a new album out of them.
The code for choosing files works nicely. Once the files are selected, the change to the NewAlbum panel should be the select files button is replaced by a done button. Under the done button is a JSplitPane with the horizontal splitter just off center with the right side being larger than the left. The left side will eventually have a thumbnail of each photo as metadata about the photo is entered into the right side.
The right side pane is to be a JScrollPane with a single JPanel which has, in a grid form, the 4 entries that the user is asked for data about. After adding everything, the dimensions are where I want them to be, but when I call the validate/repaint combination the dimensions become "messed up." I'm pretty sure it's because I'm not understanding how the default layout managers for the various classes I'm using, or extending. Please help me understand. Also, tell me if the GridBagLayout is what I want, or if a different one is what I'm looking for.
The NewAlbum code is below.
I apologize for the uncompilable code. I figured that you'd be able to just look at the class and tell me, "Oh, yeah, this is the problem." Below is compilable and does demonstrate the problem. Once the files are selected, the split pane window is too thin and too long. I want it to fit inside the frame. Actually, it should fit inside the JPanel which is inside the JFrame.
Thanks,
Andy
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
class Main extends JFrame {
static JPanel transientPanel = null;
public Main() {
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(640, 480);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Example");
JMenuItem albumMenu = new JMenuItem("New Album");
albumMenu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
transientPanel = new NewAlbum();
add(transientPanel);
validate();
repaint();
}
});
menu.add(albumMenu);
menuBar.add(menu);
setJMenuBar(menuBar);
validate();
}
public static void main(String[] args) {
final Main m = new Main();
m.setVisible(true);
}
}
/**
* #description NewAlbum is the window that is presented to the user
* to select new photographs for the album. Once selected, the user
* will be presented a form, of sorts, to complete the metadata for this
* album.
* #author Andy
*/
class NewAlbum extends JPanel {
JButton selectFiles;
JButton done;
JButton nextButton = new JButton("Next Image");
ArrayList<File> filesArray;
JSplitPane splitWindow = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
JScrollPane scrollWindow;
JPanel rightSidePanel = new JPanel();
JPanel leftSidePanel = new JPanel();
JLabel subjectLabel = new JLabel("Image subject:");
JLabel locationLabel = new JLabel("Image location:");
JLabel commentLabel = new JLabel("Comments:");
JLabel dateLabel = new JLabel("Date (mm/dd/yyyy):");
JTextField subjectText = new JTextField(25);
JTextField locationText = new JTextField(25);
JTextArea commentText = new JTextArea(4, 25);
JTextField dateText = new JTextField(10);
public NewAlbum() {
super();
selectFiles = new JButton("Select Photos");
selectFiles.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
selectFilesForAlbum();
}
});
add(selectFiles);
}
private void configureRightPanel() {
int jPanelX = getParent().getWidth();
int jPanelY = getParent().getHeight() - 30; // this should account for buttons
// now, resize this panel so that it will be the right size for the split pane
jPanelX = jPanelX - (int)(jPanelX * .31);
rightSidePanel.setSize(jPanelX, jPanelY);
rightSidePanel.add(subjectLabel);
rightSidePanel.add(subjectText);
rightSidePanel.add(locationLabel);
rightSidePanel.add(locationText);
rightSidePanel.add(commentLabel);
rightSidePanel.add(commentText);
rightSidePanel.add(dateLabel);
rightSidePanel.add(dateText);
rightSidePanel.add(nextButton);
// iterate over the photos selected, make bogus info for now
}
private ArrayList<File> makeFileIntoArrayList(File[] f) {
ArrayList<File> a = new ArrayList<File>();
a.addAll(Arrays.asList(f));
return filesArray = a;
}
/**
* selectFilesForAlbum
* This method is private to the NewAlbum class. It is the handler for
* when the user clicks on the "select photos" button. When the function
* executes, it displays the JFileChooser so that the user may select
* the desired photos. The files selected are assigned to a class variable
* of type File[] which is used by the enterPhotoInfo method.
*
* #return void
*/
private void selectFilesForAlbum() {
JFileChooser jfc = new JFileChooser();
jfc.setMultiSelectionEnabled(true);
jfc.showOpenDialog(this);
makeFileIntoArrayList(jfc.getSelectedFiles());
changeButtonToDone();
enterPhotoInfo();
// TODO write the photo album to the disk
}
private void changeButtonToDone() {
remove(selectFiles);
done = new JButton("Done");
add(done);
// by the time this gets called, we'll have a parent container
getParent().validate();
getParent().repaint();
}
private void enterPhotoInfo() {
splitWindow.setSize(this.getWidth(), this.getHeight() - 30);
// remove when the left side panel actually has something
Dimension iewDims = splitWindow.getSize();
int leftX = iewDims.width - (int)(iewDims.width * .69);
int leftY = iewDims.height;
leftSidePanel.setSize(leftX, leftY);
configureRightPanel();
scrollWindow = new JScrollPane(rightSidePanel);
scrollWindow.setSize(rightSidePanel.getSize());
splitWindow.setRightComponent(scrollWindow);
splitWindow.setLeftComponent(leftSidePanel);
splitWindow.setDividerLocation(.31);
System.out.println("Printing dimensions of before validate/repaint: this, splitWindow, scrollWindow, LSP, RSP");
debugPrintDimensions(this);
debugPrintDimensions(splitWindow);
debugPrintDimensions(scrollWindow);
debugPrintDimensions(leftSidePanel);
debugPrintDimensions(rightSidePanel);
//infoEntryWindow.add(infoScroller);
this.add(splitWindow);
this.validate();
this.repaint();
System.out.println("Printing dimensions of: this, splitWindow, scrollWindow, LSP, RSP");
debugPrintDimensions(this);
debugPrintDimensions(splitWindow);
debugPrintDimensions(scrollWindow);
debugPrintDimensions(leftSidePanel);
debugPrintDimensions(rightSidePanel);
}
private void debugPrintDimensions(Container c) {
System.out.println("DEBUG: Containers (x,y): (" +
String.valueOf(c.getWidth()) +
"," +
String.valueOf(c.getHeight()) +
")");
}
}
Also, tell me if the GridBagLayout is what I want, or if a different one is what I'm looking for.
You use the appropriate layout manager for the job. This can also mean using different layout managers on different panels.
splitWindow.setSize(this.getWidth(), this.getHeight() - 30);
You should NEVER use setSize(). That is the job of the layout manager, to determine the size of the component based on the rules of the layout manager.
All components have a preferred size which is used by the layout manager. At times you can use the setPreferredSize() to change the default.
By selecting a LayoutManager, you are handing over control of the layout to that layout manager. You can give the LayoutManager hints via layout constraints and restrictions by setting the preferred dimensions on the components you are arranging, but essentially, the layout manager will call the shots.
With GridBagLayoutManager you can achieve almost anything with constraints and component dimension settings, but it can still be tricky to get right. Try setting the preferred size on your components.
I used to use GridBagLayoutManager for everything, but then I came across MigLayout which really is a huge step forward in terms of easy configuration and layout consistency. I recommend you give it a look.

Categories

Resources