I'm trying to get the position (as a int) of a JComboBox object, The ComboBox is generated and haves an action listener like this
for (int d=0; d<i; d++)
{
titulos.addItem(listaPraBox[d]);
}
ActionListener comboListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
ItemSelectable is =(ItemSelectable)actionEvent.getSource();
objectoseleccionado = selectedString(is);
DeskMetodos.listaTexto(objectoseleccionado);
}
};
titulos.addActionListener(comboListener);
The executes
static private String selectedString(ItemSelectable is) {
Object selected[] = is.getSelectedObjects();
return ((selected.length == 0) ? "null" : (String)selected[0]);
}
But I wanted the position of the selected object to get a string from another array by that int.
Is this even possible? By the search I've made there isn't even reference to this.
JComboBox defines getSelectedIndex(). The implementation is just to loop over the data model checking equality with getSelectedItem().
That doesn't make it up into ItemSelectable, but neither does the data model itself, so you may need to use the concrete class.
Instead of storing items in a ComboBox and having to use the index to reference another array of values. Just store an object in the ComboBox that has a toString() output that matches your current displayed value and a direct reference to the object in the array. That way any object pulling the selected item or dealing with comobo box can just pull the value they need and not have to also "know" about this other array.
Related
How do I have a JComboBox with a label viewable to the end user that applies a different value/data type behind the scenes?
I've built a basic calculator for my workplace. It calculates finished roll size and diameter based on length, material thickness, and core size. If I use all text fields and stick to int/double data types it's easy and works well. However dealing with sales people etc., that do not know material thicknesses, I want to switch to a combo box for those entries.
For example I want the first combo box item to say "Thermal transfer Permanent" but I want the thickness value of .005 to be entered into my math behind the scenes, and my second item would be "Thermal Direct Permanent" but I want .006 to be entered into my math. There are many more materials and thicknesses that will be added.
JComboBox can display list of any objects. The text it displays is (usually) text returned by the toString() method of said objects. All you need to do is to create your own data object that holds the label and the value.
Like this:
class CoBoItem {
private final String display;
private final float value;
// constructor to create your data objects
CoBoItem(String display, float value) {
this.display = display;
this.value = value;
}
// methods to get the values
String getDisplay() {
return display;
}
float getValue() {
return value;
}
// this will be displayed in the JComboBox
#Override
public String toString() {
return display;
}
}
Then initialize your JComboBox with this data class as type parameter, like this.
JComboBox<CoBoItem> cb = new JComboBox<>();
cb.addItem(new CoBoItem("Thermal transfer Permanent", 0.005f));
cb.addItem(new CoBoItem("Thermal Direct Permanent", 0.006f));
You can access the selected item with
cb.getSelectedItem();
it will return an Object so you will have to cast it into your CoBoItem.
I'm trying to figure out how to get a undo function for a small maze game. First I worked out a way to do this by checking what the last direction was and just going back in the opposite direction. But this code was getting way too long since I also had to track back possible item pickups or hidden walls etc.
Background info on the code: I use a String[][] to store the maze as this was the easiest. I use an Arraylist<String[][]> to store all the strings.
After each step the player takes I save the String[][] array to the arraylist. When the player say undo I look at the second last String[][] in the arraylist and want to set the String[][] back to this. But the currentPos never seems to get updated. I'm not sure where the problem lies.
if (direction.equals("north")) {
if (currentPos[i - 1][j].equals("---")) {
continue;
} else {
currentPos[i][j] = " ";
currentPos[i - 2][j] = "P";
break;
}
}
if (direction.equals("undo")) {
currentPos = history.get(history.size()-2);
history.remove(history.size()-1);
break;
}
Without understanding the way you are setting history, I've made the assumption from your question that you are simply adding the current map to the history list. If you aren't careful, you will be simply adding the same Object, populating the history with multiply Object references to the current map state. This would have the effect you are observing with the state not changing, because you the history only contains a reference to the most recent map (Not storing any actual history).
To obtain the value from an Object, you typically need to clone the object (invoking the clone() method). However, cloning a 2-dimensional array is somewhat problematic. Invoking the clone() method on a 2-dimensional array "shallow" clones the object, essentially only cloning the first dimension while leaving the second as a reference to the same object (The reason for this is that the first 1-dimension of the array holds a reference to the second 1-dimension). Changing the value on a shallow copied object will change the value of the original and vice-versa, not what you want if you want to keep the Objects distinct.
To create two distinct objects, you will need to perform a "deep" clone, which can be easily implemented in a helper method. The below code illustrates the importance of ensuring you fully clone the object before storing it in the history list.
public static void main (String args[]) throws Exception {
ArrayList<String[][]> list = new ArrayList<>();
String[][] shallowClonedMap = new String[1][1];
String[][] deepClonedMap = new String[1][1];
shallowClonedMap[0][0] = "Old";
deepClonedMap[0][0] = "Old";
list.add(shallowClonedMap.clone());
list.add(deepClone(deepClonedMap));
shallowClonedMap[0][0] = "New";
deepClonedMap[0][0] = "New";
list.add(shallowClonedMap.clone());
list.add(deepClone(deepClonedMap));
for (String[][] item : list) {
System.out.print(item[0][0]);
}
}
public static String[][] deepClone(String[][] arry) {
if (arry == null) {
return null;
}
String[][] clone = new String[arry.length][];
for (int i = 0; i < arry.length; i++) {
clone[i] = arry[i].clone();
}
return clone;
}
The output for executing this code is : NewOldNewNew whereas the "intended" output is "OldOldNewNew". From this you can see the shallowClonedMap was updated to "New" even after being cloned and added to the list.
I have created buttons inside a panel using Java Swing. I have a button "Produce" that when clicked, I need it to create an object from type Produce (which is defined in another class and has items such as vendor, weight, and price).
When the button is clicked, the Produce object should be created using information that a user has typed in to the text fields in the same panel. So if a user typed in the vendor, weight, and price for an item into the textfields, the Produce object needs to be created with those values.
So far I have:
public void createButtons() {
JButton produceBtn = new JButton("Produce");
JButton prepMealBtn = new JButton("Prepared Meal");
infoPanel.add(produceBtn, BorderLayout.SOUTH);
infoPanel.add(prepMealBtn, BorderLayout.SOUTH);
produceBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
Object source = e.getSource();
if(source == produceBtn){
Produce myProduce = new Produce();
}
}
}
}
But I can't figure out how to do the mentioned part above.
the Produce object should be created using information that a user has
typed in to the text fields in the same panel. So if a user typed in
the vendor, weight, and price for an item into the textfields, the
Produce object needs to be created with those values.
First, you'll need to create some sort of list/array to hold the created objects.
List<Produce> produce = new ArrayList<>(); // make this global within the class
then, you have two options, either overload the Produce constructor and create another constructor to take 3 params (vendor, weight, and price):
Example constructor:
public Produce(type param1, type param2, type param3){ //Constructor to take 3 params
// assign the values appropriately
}
or create setter methods:
public void setVender(type vender){
// assign the values appropriately
}
public void setWeight(type weigth){
// assign the values appropriately
}
public void setPrice(type price){
// assign the values appropriately
}
then you can do this:
if(source == produceBtn){
String vender = someTextField.getText();
String weight = someTextField.getText();
String price = someTextField.getText();
//perform any conversion from string to numbers if needed.
/*
*/
//then create the object
Produce myProduce = new Produce(vender,weight,price);
// make sure the order in which you input the data into the arguments of the constructor above is the same as the order in which the constructor definition of the Produce class is.
produce.add(myProduce);
}
or you can use the setter methods.
Lastly but not least, you seem to have a typo with addActionListener, you're missing the closing ).
Also, you can simplify your code by using lambda expression:
produceBtn.addActionListener(e -> {
Object source = e.getSource();
if(source == produceBtn){
// do something
}
});
I have a JComboBox that I made this way, using an enum for its values:
JComboBox<StudyGrade> maxLevelOfStudiesCombo = new JComboBox<StudyGrade>(StudyGrade.values());
The enum looks like this:
public enum StudyGrade {
ELEMENTARY ("Primaria"),
MIDDLE ("Secundaria"),
MIDDLE_HIGH ("Preparatoria"),
HIGH ("Universidad"),
MASTERS ("MaestrÃa / Posgrado"),
DOCTORATE ("Doctorado"),
POST_DOCTORATE ("Post Doctorado");
private String studies;
private StudyGrade(String studies) {
this.studies = studies;
}
public String getStudies() {
return studies;
}
public void setStudies(String studies) {
this.studies = studies;
}
#Override
public String toString() {
return studies;
}
}
As you can see I'm overriding the toString() method, so I can have the studies values shown instead of the enum ones...
However I want to show the studies values only in the JComboBox not everytime I use the StudyGrade enum.
How would I change the code, so whenever I use something like:
System.out.println(StudyGrade.HIGH);
I get printed HIGH instead of Universidad, but not for the JComboBox?
I'm overriding the toString() method, so I can have the studies values shown instead of the enum ones...
I've never used a enum before but I assume you can use it like any custom object added to the combo box so you should be able to use a custom renderer so you can control which data is displayed by the combo box.
Check out Combo Box With Custom Renderer for more information and a helper class.
You're looking to extend an enum, but that's impossible. It means that something is wrong with your requirement.
Rendering is done in the UI component, and it's not enum's business to deal with presentation of data. You should make you UI component render enum the way you'd like instead of trying to make enum understand where it's being used. Since you're a Swing fanatic you should know how to do that, something like this:
maxLevelOfStudiesCombo.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList<?> jList, Object o, int i, boolean b, boolean b1) {
Component rendererComponent = super.getListCellRendererComponent(jList, o, i, b, b1);
setText(o instanceof StudyGrade ? ((StudyGrade) o).getStudies() : o.toString());
return rendererComponent;
}
});
That's going to do that.
You could just remove the toString override as the default toString for an enum is to return the name of the enum element.
And you could just have a simple for loop that would iterate through the values in your enums and add it to a string array. Then, you would need to pass that array as the argument for your JComboBox and it should be gold.
The code for it should look a bit like that:
//get all study grades
StudyGrade[] temp = StudyGrade.values();
//create a string array of same length as the array
String[] str = new String[temp.length];
//append all the studies value to the string array
for(int i = 0; i< temp.length; i++){
str[i] = temp[i].getStudies();
System.out.println(temp[i]);//debug
}
System.out.println("---------------------");//debug
for(String s : str){//debug
System.out.println(s);//debug
}//debug
//pass it
JComboBox<StudyGrade> maxLevelOfStudiesCombo = new JComboBox<StudyGrade>(StudyGrade.values());
Here is an example I made on repl.it
https://repl.it/GH28/1
1) In the following method (actionListener) a user select a grade (e.g. A-F) from a JComboBox.
2) There are multiple JComboBoxes, and each selection made gets stored into a single String[] array.
PROBLEM:
Here is the dilemma, if a user goes back and changes a selection made from a random JComboBox the previous grade selection does not get replaced in the array, however the new selection made gets stored at the next array index.
How can I make the program replace the previous grade selection and not just add the new selection?
relevant variables:
int counter;
private JComboBox[] gradeField;
//grade.userGrades[] is array of grades taken from selected combo boxes
Action Listener anonymous class:
gradeField[counter].addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e) {
Object holder = e.getSource();
JComboBox tempGradeBox = (JComboBox)holder;
String theGrade = (String)tempGradeBox.getSelectedItem();
grade.userGrades[grade.getNext()] = theGrade;
grade.updateNext();
}
});
Thanks in advance for any help.
I save the grade in an array and increment the index,
Well you should not be incrementing the index. This assumes that the user selects the grades from the combo box in a sequential order. As you have discovered users can often work randomly.
Instead you need to know which combo box has been changed and then update the appropriate entry in your array.
Or a different solution might be to update your array at the end. So maybe you have a "Process Results" button. Then you can sequentually loop through all the combo boxes to get the selected value.
Update the user grade being at the same index as the combo box:
final int index = counter;
gradeField[counter].addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e) {
Object holder = e.getSource();
JComboBox tempGradeBox = (JComboBox)holder;
String theGrade = (String)tempGradeBox.getSelectedItem();
grade.userGrades[index] = theGrade;
}
});
Here's another variation of JB Nizet's answer:
class OuterClass
{
...
gradeField[counter].addActionListener( new GradeSettingActionListener( counter ) );
...
class GradeSettingActionListener implements ActionListener
{
// -- Doesn't have to be final here (it does in JB's answer), but I like to be restrictive.
private final int index;
public GradeSettingActionListener( int index )
{
this.index = index;
}
#Override
public void actionPerformed( ActionEvent e )
{
Object holder = e.getSource();
JComboBox tempGradeBox = (JComboBox) holder;
String theGrade = (String) tempGradeBox.getSelectedItem();
grade.userGrades[index] = theGrade;
}
}
}
This approach removes the anonymous class by adding an inner class. The inner class will still have access to grade. You don't gain much here unless there's a chance you'll be splitting out the inner class later.
Of course, camickr's suggestion to process all the grades at once may also be valid, depending on other requirements (i.e., whether additional processing is done after the grades are stored in the array, which seems likely).