Reputation:
I have combobox with page numbers (1,2,3,etc). When user select the certain page (by mouse or by keyboard(as combobox editable)).I want to show and total pages. That's why on combobox action I also do:
combobox.setValue(currentPage+"/"+totalPages)
so the final code looks like
@FXML
private void onPageComboBoxAction(ActionEvent event){
....
combobox.setValue(currentPage+"/"+totalPages)
}
However this code fires one more ActionEvent and code cycles. So I see two possible ways:
Can anyone help me to solve this problem?
Upvotes: 4
Views: 4467
Reputation: 222
First remove all the listeners from the value property if any and add action event filter, e.g. by adding a method:
public class MyComboBox extends ComboBox<String> {
public void setValueSilently(String value) {
EventHandler<ActionEvent> filter = e -> e.consume();
valueProperty().removeListener(myValueListener); // a custom listener
addEventFilter(ActionEvent.ACTION, filter);
setValue(value);
removeEventFilter(ActionEvent.ACTION, filter);
valueProperty().addListener(myValueListener);
}
}
Upvotes: 2
Reputation: 209330
setValue(...)
changes the value that is held by the combo box (i.e. it changes the data, or the state of the underlying model). It seems to me that what you really want here is just to change the way the data is displayed. You change the display of the selected value by setting the button cell.
Here is an example in which the button cell keeps track of both the selected item and the number of pages:
import java.util.Random;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.collections.ListChangeListener.Change;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class PageNumberCombo extends Application {
private static final Random RNG = new Random();
@Override
public void start(Stage primaryStage) {
ComboBox<Integer> combo = new ComboBox<>();
combo.setButtonCell(new ListCell<Integer>() {
{
itemProperty().addListener((obs, oldValue, newValue) -> update());
emptyProperty().addListener((obs, oldValue, newValue) -> update());
combo.getItems().addListener((Change<? extends Integer> c) -> update());
}
private void update() {
if (isEmpty() || getItem() == null) {
setText(null);
} else {
setText(String.format("%d / %d", getItem().intValue(), combo.getItems().size()));
}
}
});
Button reloadButton = new Button("Reload");
reloadButton.setOnAction(e -> reload(combo));
reload(combo);
HBox root = new HBox(10, combo, reloadButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(24));
primaryStage.setScene(new Scene(root, 240, 60));
primaryStage.show();
}
private void reload(ComboBox<Integer> combo) {
int numPages = RNG.nextInt(10) + 11 ;
combo.getItems().clear();
IntStream.rangeClosed(1, numPages).forEach(combo.getItems()::add);
}
public static void main(String[] args) {
launch(args);
}
}
If your ComboBox
is editable, then the button cell is by default a TextField
, and the combo box's converter
is used to convert the string value in the text field to the model value, and vice-versa. So in this case you just need to install a converter that converts between the integer x
and the string "x / total"
.
Note that by default this converter is also used to display the text in the cells in the drop down, so if you want these to appear just as the integer values, you need to install a cellFactory
to explicitly create those cells.
import java.util.Random;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class PageNumberCombo extends Application {
private static final Random RNG = new Random();
@Override
public void start(Stage primaryStage) {
ComboBox<Integer> combo = new ComboBox<>();
combo.setEditable(true);
combo.setConverter(new StringConverter<Integer>() {
@Override
public String toString(Integer object) {
return object + " / " + combo.getItems().size();
}
@Override
public Integer fromString(String string) {
int index = string.indexOf('/');
if (index < 0) {
index = string.length();
}
String text = string.substring(0, index).trim();
try {
return Integer.parseInt(text);
} catch (Exception exc) {
return 0 ;
}
}
});
combo.setCellFactory(lv -> new ListCell<Integer>() {
@Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null) ;
} else {
setText(item.toString());
}
}
});
Button reloadButton = new Button("Reload");
reloadButton.setOnAction(e -> reload(combo));
reload(combo);
HBox root = new HBox(10, combo, reloadButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(24));
primaryStage.setScene(new Scene(root, 240, 60));
primaryStage.show();
}
private void reload(ComboBox<Integer> combo) {
int numPages = RNG.nextInt(10) + 11 ;
combo.getItems().clear();
IntStream.rangeClosed(1, numPages).forEach(combo.getItems()::add);
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 1
Reputation: 36179
try to use prompt text instead setting value.
combobox.setPromptText(currentPage+"/"+totalPages);
but I don't know if it work once value is selected by user. Maybe you will have to setValue(null)
just to see prompt text again. But then you would have to keep somewhere last selected value.
Upvotes: 0