I have a jcombobox containing item1 and item2, also I have a jtextfield.. when I select item1 on my jcombobox I want 30 to appear on my jtextfield while 40 if Item2 was selected... How do I do that?
问题:
回答1:
this is how you do it with ActionLIstener
import java.awt.FlowLayout;
import java.awt.event.*;
import javax.swing.*;
public class MyWind extends JFrame{
public MyWind() {
initialize();
}
private void initialize() {
setSize(300, 300);
setLayout(new FlowLayout(FlowLayout.LEFT));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField field = new JTextField();
field.setSize(200, 50);
field.setText(" ");
JComboBox comboBox = new JComboBox();
comboBox.setEditable(true);
comboBox.addItem("item1");
comboBox.addItem("item2");
//
// Create an ActionListener for the JComboBox component.
//
comboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
//
// Get the source of the component, which is our combo
// box.
//
JComboBox comboBox = (JComboBox) event.getSource();
Object selected = comboBox.getSelectedItem();
if(selected.toString().equals("item1"))
field.setText("30");
else if(selected.toString().equals("item2"))
field.setText("40");
}
});
getContentPane().add(comboBox);
getContentPane().add(field);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyWind().setVisible(true);
}
});
}
}
回答2:
The simple solution would be to use a ItemListener
. When the state changes, you would simply check the currently selected item and set the text accordingly
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestComboBox06 {
public static void main(String[] args) {
new TestComboBox06();
}
public TestComboBox06() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JComboBox cb;
private JTextField field;
public TestPane() {
cb = new JComboBox(new String[]{"Item 1", "Item 2"});
field = new JTextField(12);
add(cb);
add(field);
cb.setSelectedItem(null);
cb.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
Object item = cb.getSelectedItem();
if ("Item 1".equals(item)) {
field.setText("20");
} else if ("Item 2".equals(item)) {
field.setText("30");
}
}
});
}
}
}
A better solution would be to create a custom object that represents the value to be displayed and the value associated with it...
Updated
Now I no longer have a 10 month chewing on my ankles, I updated the example to use a ListCellRenderer
which is a more correct approach then been lazy and overriding toString
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestComboBox06 {
public static void main(String[] args) {
new TestComboBox06();
}
public TestComboBox06() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JComboBox cb;
private JTextField field;
public TestPane() {
cb = new JComboBox(new Item[]{
new Item("Item 1", "20"),
new Item("Item 2", "30")});
cb.setRenderer(new ItemCelLRenderer());
field = new JTextField(12);
add(cb);
add(field);
cb.setSelectedItem(null);
cb.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
Item item = (Item)cb.getSelectedItem();
field.setText(item.getValue());
}
});
}
}
public class Item {
private String value;
private String text;
public Item(String text, String value) {
this.text = text;
this.value = value;
}
public String getText() {
return text;
}
public String getValue() {
return value;
}
}
public class ItemCelLRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); //To change body of generated methods, choose Tools | Templates.
if (value instanceof Item) {
setText(((Item)value).getText());
}
return this;
}
}
}
回答3:
Not an answer to the original question, but an example to the how-to-make-reusable and working custom renderers without breaking MVC :-)
// WRONG
public class DataWrapper {
final Data data;
final String description;
public DataWrapper(Object data, String description) {
this.data = data;
this.description = description;
}
....
@Override
public String toString() {
return description;
}
}
// usage
myModel.add(new DataWrapper(data1, data1.getName());
It is wrong in a MVC environment, because it is mixing data and view: now the model doesn't contain the data but a wrapper which is introduced for view reasons. That's breaking separation of concerns and encapsulation (every class interacting with the model needs to be aware of the wrapped data).
The driving forces for breaking of rules were:
- keep functionality of the default KeySelectionManager (which is broken by a custom renderer)
- reuse of the wrapper class (can be applied to any data type)
As in Swing a custom renderer is the small coin designed to accomodate for custom visual representation, a default manager which can't cope is ... broken. Tweaking design just to accommodate for such a crappy default is the wrong way round, kind of upside-down. The correct is, to implement a coping manager.
While re-use is fine, doing so at the price of breaking the basic architecture is not a good bargin.
We have a problem in the presentation realm, let's solve it in the presentation realm with the elements designed to solve exactly that problem. As you might have guessed, SwingX already has such a solution :-)
In SwingX, the provider of a string representation is called StringValue, and all default renderers take such a StringValue to configure themselves:
StringValue sv = new StringValue() {
@Override
public String getString(Object value) {
if (value instanceof Data) {
return ((Data) value).getSomeProperty();
}
return TO_STRING.getString(value);
}
};
DefaultListRenderer renderer = new DefaultListRenderer(sv);
As the defaultRenderer is-a StringValue (implemented to delegate to the given), a well-behaved implementation of KeySelectionManager now can delegate to the renderer to find the appropriate item:
public BetterKeySelectionManager implements KeySelectionManager {
@Override
public int selectionForKey(char ch, ComboBoxModel model) {
....
if (getCellRenderer() instance of StringValue) {
String text = ((StringValue) getCellRenderer()).getString(model.getElementAt(row));
....
}
}
}
Outlined the approach because it is easily implementable even without using SwingX, simply define implement something similar and use it:
- some provider of a string representation
- a custom renderer which is configurable by that provider and guarantees to use it in configuring itself
- a well-behaved keySelectionManager with queries the renderer for its string represention
All except the string provider is reusable as-is (that is exactly one implemenation of the custom renderer and the keySelectionManager). There can be general implementations of the string provider, f.i. those formatting value or using bean properties via reflection. And all without breaking basic rules :-)