I've got a ListView and I listen to the selectedItemProperty for when the user changes the selection.
In this listener I add an event to my UndoManager. When I try to undo the selection, the selectedItemProperty fires the ChangeListener and it will add an other event to the UndoManger and creating an infinit loop because it will add a ListViewSelectionChange to the UndoManger when it undoes something.
public class DeviceConfigurationController {
#FXML private ListView<DeviceConfiguration> device_list;
#FXML
private void initialize() {
device_list.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
UndoManager.add(new ListViewSelectionChange<>(oldValue, device_list));
});
}
//redo/undo stuff
#FXML
private void undo() {
UndoManager.undo(); //calls the last Change
}
}
public class ListViewSelectionChange<T> implements Change {
privateT lastValue;
private T redoValue;
ListView<T> listView;
public ListViewSelectionChange(T lastValue, ListView<T> listView) {
this.lastValue = lastValue;
this.listView = listView;
}
//gets called from the undomanager
#Override
public void undo() {
redoValue = listView.getSelectionModel().getSelectedItem();
listView.getSelectionModel().select(lastValue); //fires the selection listener again, thus adding a ListViewSelection to the UndoManager
}
}
Does someone has any idea how to stop the listview from calling the listener?
Sebastian
You could add a simple flag to indicate if the listener should be fired:
public class DeviceConfigurationController {
#FXML private ListView<DeviceConfiguration> device_list;
private boolean pauseListener;
#FXML
private void initialize() {
device_list.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if(!pauseListener)
UndoManager.add(new ListViewSelectionChange<>(oldValue, device_list));
}
});
}
#FXML
private void undo() {
pauseListener = true;
UndoManager.undo();
pauseListener = false;
}
}
Related
I have JFXTreeTableView which consist of 5 columnsx In that first 2 columns have Delete & Edit Buttons for each cell. After populating table
I want first columns should disable on save Button click.
If above case is not possible then delete Buttons inside first column's cells should be disabled on Save button click.
I did like this but dont know how to disable column or buttons inside cells.
Controller Class
public class FinanceActionsController implements Initializable {
#FXML
private JFXTreeTableView<InvoiceItems> tblInvoiceItemsView;
private JFXButton btnSave;
#FXML
private HBox hbBottonBtnBar;
ObservableList<InvoiceItems> invoiceItems = FXCollections.observableArrayList();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
tableStructure();
btnSave.setOnAction((ActionEvent event) -> {
if (invoiceItems.isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Please add Atleast one Invoice Item");
alert.showAndWait();
} else {
onClickBtnSaveInvoice();
disableAndAddControlsOnSave();
//tblInvoiceItemsView.setDisable(true);
}
});
}
private void tableStructure() {
JFXTreeTableColumn<InvoiceItems, Boolean> delItem = new JFXTreeTableColumn<>("Delete");
JFXTreeTableColumn<InvoiceItems, String> editItem = new JFXTreeTableColumn<>("Edit");
JFXTreeTableColumn<InvoiceItems, String> billItem = new JFXTreeTableColumn<>("Billable Head");
delItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, Boolean> param) -> param.getValue().getValue().getBtnFlag());
delItem.setCellFactory(new Callback<TreeTableColumn<InvoiceItems, Boolean>, TreeTableCell<InvoiceItems, Boolean>>() {
#Override
public TreeTableCell<InvoiceItems, Boolean> call(TreeTableColumn<InvoiceItems, Boolean> param) {
final TreeTableCell<InvoiceItems, Boolean> cell = new TreeTableCell<InvoiceItems, Boolean>() {
MaterialIconView del = new MaterialIconView(MaterialIcon.DELETE_FOREVER, "1.5em");
final JFXButton btnDel = new JFXButton("", del);
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
btnDel.disableProperty().bind(txtN.disableProperty());
del.setFill(Color.RED);
btnDel.setButtonType(JFXButton.ButtonType.RAISED);
btnDel.setOnAction(event -> {
});
setGraphic(btnDel);
setText(null);
}
}
};
return cell;
}
});
billItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, String> param) -> param.getValue().getValue().getBillItemDesc());
final TreeItem<InvoiceItems> root = new RecursiveTreeItem<>(invoiceItems, RecursiveTreeObject::getChildren);
tblInvoiceItemsView.getColumns().setAll(delItem, editItem, billItem);
tblInvoiceItemsView.setRoot(root);
tblInvoiceItemsView.setShowRoot(false);
}
Class InvoiceItems -
class InvoiceItems extends RecursiveTreeObject<InvoiceItems> {
StringProperty billItemDesc;
BooleanProperty btnFlag;
public InvoiceItems(String billItemDesc) {
this.billItemDesc = new SimpleStringProperty(billItemDesc);
}
public StringProperty getBillItemDesc() {
return billItemDesc;
}
public BooleanProperty getBtnFlag() {
return btnFlag;
}
public void setBtnFlag(Boolean btnFlag) {
this.btnFlag = new SimpleBooleanProperty(btnFlag);
}
}
I have tried to pass InvoiceItems setBtnFlag as True in Observable list to work in setCellFactory's updateItem method but not working. Please help any help will be appreciable, Thank You.
I'm designing a custom JavaFX node using FXML. This node fires a custom event.
And I would like to know how to add an event handler to the custom event in the FXML of the parent of this node.
I created my handler, passed it to the child as an object property and hooked it into the event system via the setEventHandler method. But it throws me an error when the event is fired.
Custom event code :
public class ValueUpdatedEvent extends Event {
public static final EventType<ActionEvent> VALUE =
new EventType<>(Event.ANY, "VALUE_UPDATED");
private float value;
public ValueUpdatedEvent() {
super(VALUE);
}
public float getValue() {
return value;
}
public void setValue(float value) {
this.value = value;
}
}
Child component controller :
public class CharacteristicBar extends Component {
#FXML
private JFXTextField field;
#FXML
private JFXProgressBar bar;
#FXML
private JFXButton plus;
#FXML
JFXButton minus;
private ObjectProperty<EventHandler<ValueUpdatedEvent>> onValueUpdated = new ObjectPropertyBase<EventHandler<ValueUpdatedEvent>>() {
#Override
public Object getBean() {
return CharacteristicBar.this;
}
#Override protected void invalidated() {
setEventHandler(new EventType<>(Event.ANY, "onValueUpdated"), get());
}
#Override
public String getName() {
return "onValueUpdated";
}
};
private SimpleFloatProperty value = new SimpleFloatProperty();
private boolean readonly = false;
public CharacteristicBar() {
super("CharacteristicBar.fxml");
value.addListener(
newValue -> {
ValueUpdatedEvent event = new ValueUpdatedEvent();
event.setValue(value.get());
fireEvent(event);
}
);
bar.progressProperty().bind(this.value);
if (this.readonly) {
this.field.setEditable(false);
this.minus.setVisible(false);
this.plus.setVisible(false);
}
}
#FXML
private void handleInput(KeyEvent event) {
try {
value.set(Float.parseFloat(field.getText()) / 20f);
} catch (NumberFormatException exception) {
field.setText("");
}
}
public float getValue() {
return value.get() * 20f;
}
#FXML
public void handleClickPlus(ActionEvent event) {
this.value.set((this.value.get() * 20f + 1f) / 20f);
this.field.setText(String.valueOf(this.value.get() * 20));
}
#FXML
public void handleClickMinus(ActionEvent event) {
this.value.set((this.value.get() * 20f - 1f) / 20f);
this.field.setText(String.valueOf(this.value.get() * 20));
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
this.field.setEditable(!readonly);
this.minus.setVisible(!readonly);
this.plus.setVisible(!readonly);
}
public EventHandler<ValueUpdatedEvent> getOnValueUpdated() {
return onValueUpdated.get();
}
public ObjectProperty<EventHandler<ValueUpdatedEvent>> onValueUpdatedProperty() {
return onValueUpdated;
}
public void setOnValueUpdated(EventHandler<ValueUpdatedEvent> onValueUpdated) {
this.onValueUpdated.set(onValueUpdated);
}
}
Parent's FXML :
<CharacteristicBar fx:id="courageBar" onBarValueChanged="#handleChangeCou"
GridPane.columnIndex="1" GridPane.rowIndex="7"/>
Handler in parent's controller:
#FXML
public void handleChangeCou(ValueUpdatedEvent event){
System.out.println(event.getValue());
}
Still, my event handler isn't called.
Do you guys have any clue on how to hook my handler with the event system ?
Thanks in advance
I could not get the custom event to work but instead I used properties to achieve my goal. Maybe it's intendend this way in JavaFX
I'm implementing a desktop chat application using JavaFX.
I'm using a listview to show contacts.
I customized the list cell by following this link JavaFX custom cell factory with custom Objects.
When a contact becomes online/offline, the server notifies me appropriately, so I need to change the color of online icon accordingly.
Below is my code...
File: MainController.java
public class MainController implements Initializable {
#FXML
private ListView<ContactInfo> contactListView;
private ObservableList<ContactInfo> contactList = FXCollections.observableArrayList();
this.contactListView.setCellFactory( listView -> {
return new ContactCell();
}
}
File: ContactCell.java
public class ContactCell extends ListCell<ContactInfo> {
private final ContactItemController controller = new ContactItemController();
public ContactCell() {
}
#Override
protected void updateItem(ContactInfo item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
controller.setItem(item);
setGraphic(controller.getView());
}
}
}
File: ContactItemController.java
public class ContactItemController {
#FXML
private Pane pane;
#FXML
private ImageView contactImage;
#FXML
private Label contactName;
#FXML
private FontAwesomeIconView onlineIcon;
public ContactItemController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/snippets/ContactItem.fxml"));
fxmlLoader.setController(this);
try {
pane = fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setItem(ContactInfo item) {
this.contactName.setText(item.getName());
if(item.getOnline().getOnline())
this.onlineIcon.setFill(Color.LIGHTGREEN);
else
this.onlineIcon.setFill(Color.web("#838383"));
}
public Pane getView() {
return this.pane;
}
}
File: ContactInfo.java
public class ContactInfo {
private String name;
private BooleanProperty online;
// Getters and setters
..............
..............
}
I tried to add change listener to each item's boolean property inside setItem() method of the ContactItemController class.
But the listener is getting added more than once....
Is this the right way to do this?
I've been trying to create a music player and part of that requires listening to a time slider. So I've added to the time slide and this is the error I get:
I've been trying to get my head around how you fix this error and the whole business of overriding.
Can anyone point me in the right direction on how to fix this error?
My Code:
public class graphicalController implements Initializable
{
//GUI Decleration
#FXML
public Button centreButton;
#FXML
public Button backButton;
#FXML
public Button forwardButton;
#FXML
public ToggleButton muteToggle;
#FXML
public MenuItem loadFolder;
#FXML
public Text nameText;
#FXML
public Text albumText;
#FXML
public Text timeText;
#FXML
public Text artistText;
#FXML
public Slider timeSlider;
#FXML
public Slider volumeSlider;
//Controller Decleration
String absolutePath;
SongQueue q = new SongQueue();
MediaPlayer player;
Status status;
boolean isPlaying = false;
boolean isMuted = false;
boolean isPaused = false;
private Duration duration;
/**
* The constructor. The constructor is called before the initialize()
* method.
*
* Anything in regards to CSS styling with FXML MUST be done within the initialize method.
*/
public graphicalController() {
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
#FXML
public void initialize(URL location, ResourceBundle resources)
{
centreButton.setStyle("-fx-background-image: url('/Resources/Play_Button.png')");
centreButton.setText("");
backButton.setStyle("-fx-background-image: url('/Resources/Back_Button.png')");
backButton.setText("");
forwardButton.setStyle("-fx-background-image: url('/Resources/Forward_Button.png')");
forwardButton.setText("");
muteToggle.setStyle("-fx-background-image: url('/Resources/ToggleSound_Button.png')");
muteToggle.setText("");
nameText.setText("");
albumText.setText("");
artistText.setText("");
volumeSlider.valueProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
double sliderValue = newValue.intValue();
handleVolumeSlider(sliderValue);
}
});
timeSlider.valueProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
//outputTextArea.appendText("Slider Value Changed (newValue: " + newValue.intValue() + ")\n");
}
});
timeSlider.valueProperty().addListener(new InvalidationListener() {
public void invalidated(Observable ov) {
if (timeSlider.isValueChanging()) {
// multiply duration by percentage calculated by slider position
if(duration!=null) {
player.seek(duration.multiply(timeSlider.getValue() / 100.0));
}
updateValues();
}
}
});
}
public void setSongText() {
String file = q.peek().fileName;
String songName = q.peek().songName;
String albumName = q.peek().albumName;
String artistName = q.peek().artistName;
if (songName == "") {
songName = "Song name not specified in metadata.";
}
else if (albumName == "")
{
albumName = " Album name not specified in metadata.";
}
else if (artistName == "")
{
artistName = "Artist name not specified in metadata.";
}
nameText.setText(songName);
albumText.setText(albumName);
artistText.setText(artistName);
}
}
You will find my problem in the initialize method.
Are you using the correct Observable type? It should be of type javafx.beans.Observable.
I want to get the event of two buttons in my custom component.
the component is a imageview with two buttons to move between images, but I need to get the position of the image that is currently displayed, Im storing the key of the image, but I need to know when a button have been pressed outside the custom component, so I can change a Label outside the custom component.
public class TransitionSlider extends AnchorPane {
#FXML
private AnchorPane transitionSliderPane;
#FXML
private ImageView transitionSliderImageView;
#FXML
private Button prevButton;
#FXML
private Button nextButton;
private Map<Integer,Image> imageMap;
private Image currentImage;
private DropShadow imageViewDropShadow;
private int currentKey = 1;
private Image[] images;
public TransitionSlider() {
FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setController(this);
loader.setLocation(this.getClass().getResource("TransitionSlider.fxml"));
loader.setClassLoader(this.getClass().getClassLoader());
try {
loader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
prevButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= 1){
currentKey = currentKey + 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
nextButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= imageMap.size()){
currentKey = currentKey - 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
}
// more code here...
}
I want a way to capture the event and get variables inside the component and change a label outside the custom component...
for example:
public class Gallery extends Application {
#FXML
TransitionSlider ts;
Label label;
#Override
public void start(Stage stage) throws Exception {
label = new Label();
TransitionSlider ts = new TransitionSlider();
ts.captureButtonEvent(){ // need a way to capture this
label.setText(ts.getCurrentKey());
}
// more code here....
}
If I understood your question correctly, you want a binding.. Follow these steps:
1) Put bindable field and its getter/setter into TransitionSlider:
private IntegerProperty currentKey = new SimpleIntegerProperty(1);
public int getCurrentKey() {
return currentKey.get();
}
public void setCurrentKey(int val) {
return currentKey.set(val);
}
public IntegerProperty currentKeyProperty() {
return currentKey;
}
2) Bind this property to label's text in Gallery:
label = new Label();
TransitionSlider ts = new TransitionSlider();
label.textProperty.bind(ts.currentKeyProperty().asString());
Alternatively, if you want to do stuff more than just changing label's text, you can add a change listener to currentKeyProperty:
ts.currentKeyProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
label.setText(newValue);
// do other stuff according to "oldValue" and "newValue".
}
});