我正在研究客户端 - 服务器解决方案,我有一个与我连接的客户端的ListView。用户应该能够通过编辑ListView中的名称来远程重命名客户端。我已经阅读了很多关于编辑ListView单元格的内容,但是我还没有找到任何可以改变我的类的成员属性的好例子。大多数示例都带有字符串列表,在我看来,如果ListView中的项不仅仅是字符串,那么这不是现代软件开发。
我想要做的是更改客户端内的name
属性。
class Client {
private String name;
public String getName(){
return name;
}
public String setName(String val){
name = val;
}
}
我不在乎,如果我必须将成员name
实现为JavaFX属性(实际上我已经这样做了,但为了便于阅读和简单而将其留下来)。 / em>的
如果您感兴趣,我尝试使用TextFieldListCell单元工厂:
this.listViewClients.setCellFactory(TextFieldListCell
.forListView(new NetworkClientStringConverter(this.clientController)));
但是我发现了一些棘手的事情:
fromString
)。
StringConverter
来解决这个问题。 (这不太好,我更愿意只访问我正在更改的客户端。)答案 0 :(得分:7)
使用标准TextFieldListCell
这有点棘手,但以下似乎有效。
在整个问题中假设有一个Client
模型类:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Client {
private final StringProperty name = new SimpleStringProperty();
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
(fwiw我认为如果你使用标准的Java Bean属性而不是JavaFX属性,那么一切仍然适用于此。)
创建一个引用单元格的转换器类:
public class ClientConverter extends StringConverter<Client> {
private final ListCell<Client> cell;
public ClientConverter(ListCell<Client> cell) {
this.cell = cell ;
}
@Override
public String toString(Client client) {
return client.getName();
}
@Override
public Client fromString(String string) {
Client client = cell.getItem();
client.setName(string);
return client ;
}
}
然后你可以做
listViewClients.setCellFactory(lv -> {
TextFieldListCell<Client> cell = new TextFieldListCell<>();
cell.setConverter(new ClientConverter(cell));
return cell ;
});
SSCCE:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class EditableListView extends Application {
@Override
public void start(Stage primaryStage) {
ListView<Client> listViewClients = new ListView<>();
for (int i= 1 ; i <= 20; i++) {
Client c = new Client();
c.setName("Client "+i);
listViewClients.getItems().add(c);
}
listViewClients.setEditable(true);
listViewClients.setCellFactory(lv -> {
TextFieldListCell<Client> cell = new TextFieldListCell<>();
cell.setConverter(new ClientConverter(cell));
return cell ;
});
// debug:
Button debug = new Button("Show clients");
debug.setOnAction(e -> listViewClients.getItems().stream().map(Client::getName).forEach(System.out::println));
BorderPane root = new BorderPane(listViewClients, null, null, debug, null);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static class ClientConverter extends StringConverter<Client> {
private final ListCell<Client> cell;
public ClientConverter(ListCell<Client> cell) {
this.cell = cell ;
}
@Override
public String toString(Client client) {
return client.getName();
}
@Override
public Client fromString(String string) {
Client client = cell.getItem();
client.setName(string);
return client ;
}
}
public static void main(String[] args) {
launch(args);
}
}
这感觉有点像黑客,因为更新模型(StringConverter
)类中的数据并不是Client
的真正工作。我可能会赞成在这里从头开始创建单元格实现。
这是一个更多的代码,但这感觉更安全:
public class ClientListCell extends ListCell<Client> {
private final TextField textField = new TextField();
public ClientListCell() {
textField.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
textField.setOnAction(e -> {
getItem().setName(textField.getText());
setText(textField.getText());
setContentDisplay(ContentDisplay.TEXT_ONLY);
});
setGraphic(textField);
}
@Override
protected void updateItem(Client client, boolean empty) {
super.updateItem(client, empty);
if (isEditing()) {
textField.setText(client.getName());
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setContentDisplay(ContentDisplay.TEXT_ONLY);
if (empty) {
setText(null);
} else {
setText(client.getName());
}
}
}
@Override
public void startEdit() {
super.startEdit();
textField.setText(getItem().getName());
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
textField.selectAll();
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem().getName());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
和使用此单元实现的SSCCE是
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class EditableListView extends Application {
@Override
public void start(Stage primaryStage) {
ListView<Client> listViewClients = new ListView<>();
for (int i= 1 ; i <= 20; i++) {
Client c = new Client();
c.setName("Client "+i);
listViewClients.getItems().add(c);
}
listViewClients.setEditable(true);
listViewClients.setCellFactory(lv -> new ClientListCell());
// debug:
Button debug = new Button("Show clients");
debug.setOnAction(e -> listViewClients.getItems().stream().map(Client::getName).forEach(System.out::println));
BorderPane root = new BorderPane(listViewClients, null, null, debug, null);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}