Just came across a slight issue I'm confused by if someone wouldn't mind having a quick look please.
My ComboBoxes are constructed and initialised as shown below, with me thinking it would be a good idea to keep the data as enums, as the options are all fixed:
private JComboBox<JobTitle> comboBox = new JComboBox<>();
but when adding elements to the DefaultComboBoxModel I've realised that I don't want to display the options as a constant (ie I don't want the options to be displayed in capitals).
So I created my enum like this (see below) in order to call jobTitleModel.addElement(JobTitle.Architect.getName())
But obviously that isn't going to work as the ComboBox is of type enum, not string.
public enum JobTitle {
ARCHITECT("Architect"), TOWN_PLANNER("Town Planner"), URBAN_DESIGNER("Urban Designer"), LANDSCAPE_GARDENER("Landscape Gardener");
private final String name;
private JobTitle(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
So I'm not really sure how to approach this? I could change the ComboBox and model to String, but I think I must be missing something obvious here. Thanks
Use a custom renderer for your JComboBox. For example the code below uses a renderer that extends from the default list cell renderer. All it does is to get the value held in each cell of the JComboBox, a JobTitle object, extracts the name from this object, and displays the name:
JComboBox<JobTitle> jobCombo = new JComboBox<>(JobTitle.values());
jobCombo.setRenderer(new DefaultListCellRenderer() {
public Component getListCellRendererComponent(JList<?> list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
value = ((JobTitle) value).getName();
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
});
Why do this? Now if the user selects an item from the combo box, it is a full-fledged JobTitle enum object and not a String.
Regarding your comment:
If I wanted to make a method that does the above for a variety of comboBoxes and enums, is a switch the most elegant way to deal with that cast on line 9?
That's a whole new question and probably should be posted separately, but one possible solution is to give all the enums the same interface, something like:
public enum JobTitle implements Textable {
ARCHITECT("Architect"), TOWN_PLANNER("Town Planner"), URBAN_DESIGNER(
"Urban Designer"), LANDSCAPE_GARDENER("Landscape Gardener");
private final String text;
private JobTitle(String name) {
this.text = name;
}
#Override
public String getText() {
return text;
}
}
public interface Textable {
String getText();
}
And then creating my own renderer class:
public class TextableRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
value = ((Textable) value).getText();
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
}
This will work with any enum (or class) that implements the Textable interface.
And then used:
final JComboBox<JobTitle> jobCombo = new JComboBox<>(JobTitle.values());
jobCombo.setRenderer(new TextableRenderer());
Related
I am having difficulty making a JComboBox that will be used to filter and to select a particular custom data object that is drawn from internal data-structure and the value shown in JComboBox would be just a value of a one field in that custom data object, or even a field of field (that is a custom object itself) of the enclosing custom data object. For example, I have device model registration form.
https://drive.google.com/open?id=1q-_ii_V7SWDBFvUJGw0cd2BEWP3BnM0H
Model has a Name, a Specification, a Device Type and a Manufacturer that define it. This is the actual Model class that I use:
public class Models
{
private DeviceTypes deviceType;
private Manufacturers manufacturer;
private String name;
//getters and setters
}
This is further part of a HashMap that contains all Models with their ID.
public Map<Integer,Models> modelsTable = new HashMap<Integer, Models>();
I want to be able to add and remove items from JComboBox and to select actual data repersesnted with JComboBox items to create new models with those objects.
What is the standard way to do this? I have created a ComboBox renderer:
public class DeviceTypeRenderer extends BasicComboBoxRenderer
{
private static final long serialVersionUID = 3442865560696889757L;
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
if (value instanceof DeviceTypes)
{
value = ((DeviceTypes)value).getName();
}
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
return this;
}
}
public class ManufacturersRenderer extends BasicComboBoxRenderer
{
private static final long serialVersionUID = 3723328665061573656L;
public Component getListCellRendererComponent(JList<?> list, Object value,int index, boolean isSelected, boolean cellHasFocus)
{
if (value instanceof Manufacturers)
{
value = ((Manufacturers)value).getName();
}
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
return this;
}
}
And then I just add or remove them as data objects.
DeviceTypes deviceType = new DeviceTypes(name,description);
comboBoxDeviceType.addItem(deviceType);
And JComboBox displayes deviceType.getName();
What would be the best way to do the reverse. To get a actual data class form JComboBox item selection? I probably did all this the wrong way and am using lot of bad practice. If you see that please inform me to correct myself, and I would appreciate it if you could show me how to implement this correctly. Thank you in advance!
I think I found better and more elegant overall solution for this problem by using ComboBox models.
First of all the data objects are stored in a HashMap with String keys, that are uniqe, and represent actual names od each data object.
private HashMap<String, DataElement> uniqueStringMap = new HashMap<String, DataElement>();
Second ComboBox model is created form those keys by taking the key set of the HashMap.
public static DefaultComboBoxModel<String> getModel(DataType dataType)
{
String[] items = (String[]) DataManager.getDataTable(dataType)
.getUniqueStringMap()
.keySet().toArray();
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<String>(items);
return model;
}
And then in the GUI controller the ComboBox model is set.
deviceGUI.getDeviceRegistrationPanel().setDeviceTypeCmbModel(CmbModelFactory.getModel(DataType.DEVICE_TYPE));
And in the actual Swing class the JComboBox does not know about the actual data objects from the Model, and it has simple design.
private JComboBox<String> cmbDeviceType = new JComboBox<String>();
It's model is set form the controller with a method.
public void setDeviceTypeCmbModel(ComboBoxModel<String> model)
{
cmbDeviceType.setModel(model);
}
The data retrival is done by geting the selected item in the ComboBox.
public String getDeviceType()
{
return (String) cmbDeviceType.getSelectedItem();
}
And then that String value is used to get the actual data object.
public static DeviceType getDeviceType(String name)
{
return (DeviceType) DataManager.getByUniqueString(DataType.DEVICE_TYPE, name);
}
I hope this helps someone.
I'm wanting to create the option for users in a game to select their own piece to play with from a list of piece's that I have made. To do this I have two comboBox's that contain the same items in them but i dont want users to be able to select the same piece.
I have thought about removing the item from comboBox 1 if selected in comboBox2 (and adding it back later) but i am using the index of the selected item later when i assign the image to the 'player' class and so this would get messy there and also the indexing of each list would be different since they dont contain the item that the other has selected (hope that makes sense).
How can i make the item in comboBox 2 hidden or unselectable if it is selected in comboBox 1?
Many thanks
Create POJO which represents the basic properties of the Piece...
public class Piece {
private Image image;
private String name;
public Piece(String name, Image image) {
this.image = image;
this.name = name;
}
public Image getImage() {
return image;
}
public String getName() {
return name;
}
}
Add these to your JComboBox
Piece[] pieces = new Piece[]{
// Create what ever pieces you need...
}
DefaultComboBoxModel modelPlayer1 = new DefaultComboBoxModel(pieces);
DefaultComboBoxModel modelPlayer2 = new DefaultComboBoxModel(pieces);
JComboBox cbPlayer1 = new JComboBox(modelPlayer1);
JComboBox cbPlayer2 = new JComboBox(modelPlayer2);
You will find that you will probably need a ListCellRenderer of some kind in order to display the name of the Piece in the JComboBox, for example...
public class PieceListCellRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof Piece) {
value = ((Piece)value).getName();
}
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
}
Then apply the renderer to the comboboxes
cbPlayer1.setRenderer(new PieceListCellRenderer());
cbPlayer2.setRenderer(new PieceListCellRenderer());
Now, you don't need to care about the indecies, and because you've built the two models from the same array of Pieces you should have no issue simply removing them by reference...
Piece p = (Piece)cbPlayer1.getSelectedItem();
((DefaultComboBoxModel)cbPlayer2.getModel()).removeElement(p);
See How to Use Combo Boxes for more details
I'm creating a JList in a Java GUI program that will be filled with the custom "SteamFriends" class via an ArrayList called friendsList:
friendsListJList.setModel(new javax.swing.AbstractListModel() {
SteamFriend[] friendListArr = friendsList.toArray(new SteamFriend [0]);
public int getSize() { return friendListArr.length; }
public SteamFriend getElementAt(int i) { return friendListArr[i]; }
});
Right now the toString() function returns the friendName of the SteamFriend object, but I'm wondering if it's possible to change it so that I can control what attribute it returns and what the best practice for it would be.
If I want to fill a JList with a list of SteamFriends which is sorted by age (for example), then how would I change the toString() method to display age? Through a static variable (public static int variableToDisplay) in the SteamFriend object, perhaps?
It's better not to use toString() to display data to the user, but rather it is best used as a debugging tool. For JLists, I recommend that you instead use a custom cell renderer, one that you have written to display exactly what information you wish.
e.g. for one I've used in a program,
class LabelTextPairListRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList<?> list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
LabelTextPairList labelTextPairList = (LabelTextPairList) value;
if (labelTextPairList == null) {
value = "";
} else {
value = labelTextPairList.getName();
}
return super.getListCellRendererComponent(list, value, index, isSelected,
cellHasFocus);
}
}
I want to add objects to a JComboBox but show a String on the JComboBox for each object.
For example, in the following html code
<select>
<option value="1">Item 1</option>
<option value="2">Item 2</option>
<option value="3">Item 3</option>
<option value="4">Item 4</option>
</select>
in the first item, the String that is shown is "Item 1", but the value of the item is "1".
Is there a form to do something like that with a JComboBox?
Start by taking a look at How to Use Combo Boxes, in particular Providing a Custom Renderer
Basically, you want to define your object which will be contained within the combo box...
public class MyObject {
private String name;
private int value;
public MyObject(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
Then create a custom ListCellRenderer that knows how to renderer it...
public class MyObjectListCellRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
if (value instanceof MyObject) {
value = ((MyObject)value).getName();
}
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
return this;
}
}
Then populate you combo box and apply the cell renderer...
JComboBox box = new JComboBox();
box.addItem(new MyObject(..., ...));
//...
box.setRenderer(new MyObjectListCellRenderer());
You could, equally, override the toString method of your object, but I tend to like to avoid this for display purposes, as I like the toString method to provide diagnostic information about the object, but that's me
If your combo box model contains objects, their toString() method will be used by default to display them in the combo box. If the toString() method displays what you want, you don't have anything to do.
Otherwise, you just need to set a cell renderer to customize the way each object is displayed (and that doesn't limit you to text: you can also change the font, color, icon, etc.).
This is all described in the Swing tutorial.
For example, in the following html code
For something simple like this, where you have an "ID", "Value" type of data, I do like the approach of a custom Object who's purpose in life is to provide a custom toString() method. See Combo Box With Hidden Data for such an reusable object.
Many people in the forums do recommend a custom renderer. Unfortunately using a custom renderer breaks the default functionality of the comobo box. See Combo Box With Custom Renderer for more information as a solution.
Yes, that can be done using the object type as the parameter for the JComboBox generic, like this:
public class TestFrame extends JFrame {
// This will be the JComboBox's item class
private static class Test {
private Integer value;
private String label;
public Test(Integer value, String label) {
this.setValue(value);
this.setLabel(label);
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
// The "toString" method will be used by the JComboBox to generate the label for the item
#Override
public String toString() {
return getLabel();
}
}
public static void main(String[] args) {
TestFrame frmMain = new TestFrame();
frmMain.setSize(300, 50);
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Here you declare a JComboBox that
// uses the type "Test" for item elements
JComboBox<Test> cmbCombo = new JComboBox<TestFrame.Test>();
for (int i = 0; i < 10; i++) {
// Add some elements for the combo
cmbCombo.addItem(new Test(i, String.format("This is the item %d", i + 1)));
}
// Listen to changes in the selection
cmbCombo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JComboBox<Test> cmbCombo = (JComboBox<Test>) e.getSource();
// The selected element is a "Test" instance, just cast it to the correct type
Test test = (Test) cmbCombo.getSelectedItem();
// Manipulate the selected object at will
System.out.printf("The selected value is '%d'\n", test.getValue());
}
});
frmMain.add(cmbCombo);
frmMain.setVisible(true);
}
}
In Java how do I get a JList with alternating colors? Any sample code?
To customize the look of a JList cells you need to write your own implementation of a ListCellRenderer.
A sample implementation of the class may look like this: (rough sketch, not tested)
public class MyListCellThing extends JLabel implements ListCellRenderer {
public MyListCellThing() {
setOpaque(true);
}
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
// Assumes the stuff in the list has a pretty toString
setText(value.toString());
// based on the index you set the color. This produces the every other effect.
if (index % 2 == 0) setBackground(Color.RED);
else setBackground(Color.BLUE);
return this;
}
}
To use this renderer, in your JList's constructor put this code:
setCellRenderer(new MyListCellThing());
To change the behavior of the cell based on selected and has focus, use the provided boolean values.