I have a table. It has a list of voice channels. Each row has a toggle button that can listen or mute the channel. I have two listeners, One for clicking on a row and another for selecting the toggle button. How do I change the button state if I click on a row? (For example, I click on "Voice Channel 1" and the button in that row changes to "Mute", in other words, if I select a channel, I automatically start listening to it).
public class VoiceChannel {
private final StringProperty voiceChannelName = new SimpleStringProperty();
public VoiceChannel(String userName) {
setVoiceChannelName(userName);
}
public final StringProperty voiceChannelNameProperty() {
return this.voiceChannelName;
}
public final String getVoiceChannelName() {
return this.voiceChannelNameProperty().get();
}
public final void setVoiceChannelName(String voiceChannelName) {
this.voiceChannelNameProperty().set(voiceChannelName);
}
}
public class Main extends Application {
@Override
public void start(Stage stage) {
TableView<VoiceChannel> voiceChannelTable = new TableView<>();
TableColumn<VoiceChannel, String> voiceChannelNameColumn = new TableColumn<>("Channel Name");
voiceChannelNameColumn.setCellValueFactory(cellData -> cellData.getValue().voiceChannelNameProperty());
TableColumn<VoiceChannel, VoiceChannel> listenMuteColumn = new TableColumn<>("Listen/Mute");
listenMuteColumn.setCellValueFactory(cellData -> new ReadOnlyObjectWrapper<>(cellData.getValue()));
ObservableSet<VoiceChannel> channels = FXCollections.observableSet();
channels.add(new VoiceChannel("Voice Channel 1"));
channels.add(new VoiceChannel("Voice Channel 2"));
channels.add(new VoiceChannel("Voice Channel 3"));
channels.add(new VoiceChannel("Voice Channel 4"));
channels.add(new VoiceChannel("Voice Channel 5"));
voiceChannelTable.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {
if(newValue != null) {
System.out.println(newValue.getVoiceChannelName() + " selected");
}
});
listenMuteColumn.setCellFactory(c -> new TableCell<>() {
private final ToggleButton button = new ToggleButton();
{
button.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
System.out.println("Now listening to " + getItem().getVoiceChannelName());
} else {
System.out.println(getItem().getVoiceChannelName() + " muted");
}
});
button.textProperty().bind(Bindings.when(button.selectedProperty()).then("Mute").otherwise("Listen"));
setAlignment(Pos.CENTER);
}
@Override
public void updateItem(VoiceChannel item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(button);
}
}
});
voiceChannelTable.getColumns().add(voiceChannelNameColumn);
voiceChannelTable.getColumns().add(listenMuteColumn);
voiceChannelTable.getItems().addAll(channels);
Scene scene = new Scene(new BorderPane(voiceChannelTable), 400, 300);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I can't seem to get the listeners to talk to each other. I saw an example online using Callbacks but I am not familiar with this process.
Here's a little application that has the core of what you're looking for, so that you can see how it would work:
and the TableColumn/Cell:
This is Kotlin, but still JavaFX, you should be able to figure out what it's doing.
In this example
channelPlayingandchannelListconstitute the Presentation Model. All of the logic works off this. In the InvalidationListener for each item inchannelListit updates thechannelPlayingvalue and flips all of the other channels off. Where it sets the value ofchannelPlayingis where you would trigger the channel to start playing.The TableView is just used to present the data to the user. The
selectionModel.selectedItemProperty()has a listener on it to flip theisPlayingproperty in thatselectedItemtotrue. Then the Listener on that property will kick in, play the channel and turn off the others.Just to keep it clean, I created a separate class for the
TableColumnand it pulls the values and creates the cells.The thing to note here is that the value being passed to the Cell is the BooleanProperty itself. So the
itemPropertyof the cell contains aBooleanProperty. This means that the value factory has to wrap theBooleanPropertyin aReadOnlyObjectWrapperso it will stay aBooleanPropertyand not aBoolean.In the Cell, there's just a ToggleButton. It's
textProperty()is bound to whether it's selected or not.In
PlayMuteCell.updateItem()is the only tricky part. The first step is to unbind theToggleButton selectedproperty if the cell was previous populated. Then it's bidirectionally bound to the incomingBooleanProperty.The result is that when you click on the ToggleButton it also flips the
BooleanPropertyin theChannelDataitem which triggers the Listener on it which would turn on/off the playback and do the other stuff.