2

I am looking to use a 'value' for my combo box lists similar to how you can do in HTML where you can have the label (whatever is shown in the combo box) and the value (being the returned value) like so:

List 1:

label="9am - 12pm", value="Morning"  
label="12pm - 3pm", value="Afternoon"  
label="3pm - 6pm", value="Evening"

So, the combo box will display the "9am - 12pm", etc. but the value returned will be "Morning".

Maybe I've been spending too much time on my web assignments that I'm going about this in a stupid way but any help would be appreciated.

Deano3607
  • 21
  • 2
  • It might be a bit messy but why don't you just put those entries into a map? – ParkerHalo Apr 24 '17 at 10:43
  • 1
    I am no expert in JavaFX but with Swing, a Combobox is generic, you can pass any instance, it will use `toString()` to get the `value` and you acn get the instance with the selectedItem method. If this is the same, this is how you could do it. Something like [Javafx combobox with custom object ...](http://stackoverflow.com/q/20283940/4391450) – AxelH Apr 24 '17 at 10:50
  • This is by no means a stupid way, I have the same problem and found multiple posts about it so it seems to be a common need. – Konrad Höffner Aug 09 '19 at 09:16

3 Answers3

5

Create a class to encapsulate the entity that you want to be displayed in the combo box:

import java.time.LocalTime ;

// maybe an enum would be good here too
public class TimeOfDay {

    private final LocalTime startTime ;
    private final LocalTime endTime ;

    private final String shortDescription ;

    public TimeOfDay(LocalTime startTime, LocalTime endTime, String description) {
        this.startTime = startTime ;
        this.endTime = endTime ;
        this.shortDescription = description ;
    }

    public LocalTime getStartTime() {
        return startTime ;
    }

    public LocalTime getEndTime() {
        return endTime ;
    }

    public String getShortDescription() {
        return shortDescription ;
    }
}

Now you can make a ComboBox that displays these:

ComboBox<TimeOfDay> timeOfDayCombo = new ComboBox<>();

timeOfDayCombo.getItems().addAll(
    new TimeOfDay(LocalTime.of(9,0), LocalTime.of(12,0), "Morning"),
    new TimeOfDay(LocalTime.of(12,0), LocalTime.of(15,0), "Afternoon"),
    new TimeOfDay(LocalTime.of(15,0), LocalTime.of(18,0), "Evening"));

You can customize the display by defining a list cell:

import java.time.LocalTime ;
import java.time.format.DateTimeFormatter ;

public class TimeOfDayListCell extends ListCell<TimeOfDay> {

    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ha");

    @Override
    protected void updateItem(TimeOfDay timeOfDay, boolean empty) {
        super.updateItem(timeOfDay, empty) ;
        if (empty) {
            setText(null);
        } else {
            setText(String.format("%s - %s",
                formatter.format(timeOfDay.getStartTime()),
                formatter.format(timeOfDay.getEndTime())));
        }
    }
}

and then

timeOfDayCombo.setCellFactory(lv -> new TimeOfDayListCell());
timeOfDayCombo.setButtonCell(new TimeOfDayListCell());

Now calling timeOfDayCombo.getValue() will return the TimeOfDay instance, from which you can call any method you need (such as getShortDescription()).

James_D
  • 201,275
  • 16
  • 291
  • 322
  • You seems more confortable than me with JavaFx, if you can, could you tell me if my solution is valid or not. PS : you have a type in the `TimeOfDay`, you use `description` insteand of `shortdescription` in the constructor. – AxelH Apr 24 '17 at 11:11
  • @AxelH Thanks for pointing out the typo (which is now fixed). – James_D Apr 24 '17 at 11:12
  • Nice complete answer! – GhostCat Apr 24 '17 at 11:12
1

What you can do is use a custome class that will hold those value. Use that class to create an ObservableList to create this Combobox.

public class MyHolder{
    private int id;
    private String label;

    public MyHolder(int id, String label){ 
        //...
    }

    //GETTER (AND SETTER IF NEEDED)

}

Then, using a StringConverter using this same class as type, you define the method toString to return the String you want.

public class MyConverterHolder extends StringConverter<MyHolder>{

    public String toString(MyHolder holder){
        return holder.getName();
    }
    //...
}

You set the converter to the combobox, and you are set.

Then to get the selectedValue, get the selectionModel, and get the item with getModelItem. Here you will get an instance of the specified type, just get the value you want.

This is a solution from documentation only, I didn't write an example to test it but this seems to be correct from what I have read. If this is not clear or not working, I will write an example (I could use a working example to get this started ;) ).

AxelH
  • 14,325
  • 2
  • 25
  • 55
  • This might work... according to the API the converter is used the other way around - i.e. to convert a `String` to the entity contained in the combo box - when the user edits via, e.g. a text field. The default cell used by a combo box might also use the converter to generate the text; I would have to test that. – James_D Apr 24 '17 at 11:16
  • @James_D, thanks for the review. If this might work, I will keep it like this, I should be able to test this tomorrow. – AxelH Apr 24 '17 at 11:18
-1

The combo box uses toString() so you just need to output there what you want to have displayed:

class TimeSpan
{
    String label,value;

    public TimeSpan(String label, String value)
    {
        this.label = label;
        this.value = value;
    }

    @Override public String toString() {return label;}
}

If for some reason you cannot do that, e.g. it's not your own class or you need toString() to work differently for some other case, you can encapsulate it as follows, assuming the class above as your base class:

class TimeSpanFormatter
{
    TimeSpan timeSpan;

    public TimeSpanFormatter(TimeSpan timeSpan)
    {
        this.timeSpan = timeSpan;   
    }

    @Override public String toString() {return "Whatever you want to output with "+timeSpan.label;}
}

P.S.: This is the same as AxelH's approach, but I think you don't need the StringConverter.

Konrad Höffner
  • 11,100
  • 16
  • 60
  • 118
  • The approach using the `StringConverter` is preferrable to your second approach though: It requires the creation of a single object; your solution requires the creation of one object per item and changes the item type of the `ComboBox` to something that makes the relevant data more complicated to access (`combo.getValue().getTimeSpan().getLabel()`)... – fabian Aug 09 '19 at 09:41
  • no, never-ever override toString for application reasons (what would you do if they differ at different places?) Instead, use a custom cell that takes over the visualization – kleopatra Aug 09 '19 at 14:44
  • @Kleopatra: Never say never! If this class is only ever used in this context and toString is not used at any other place, why not? – Konrad Höffner Aug 09 '19 at 14:49
  • because it's the wrong approach: fx has dedicated support to customize the visuals, use it instead of breaking rules and polluting your code base .. it _will_ come back to you (or the maintainer of the code, poor fellow ;) – kleopatra Aug 09 '19 at 14:52