我正在尝试了解如何使用服务更新JavaFX2.2 UI中的一组文本字段,但文档中没有样本或提示。 Addressbook和Ensemble样本都处理表格。该服务将返回一个Observable字符串列表。 怎么做绑定?
textField0< ---> observableList [0]
textField1< ---> observableList [1]
我修改了ServiceSample(http://download.oracle.com/otndocs/products/javafx/2.2/samples/Ensemble/index.html处的Concurrency | Service示例)以获得 TextViews的ListView ,但我不知道如何进行绑定。 我该怎么做而不是
tableView.itemsProperty().bind(service.valueProperty());
这一行甚至不会编译:
listView.itemsProperty().bind(service.valueProperty());
修改后的并发性|服务样本位于http://download.oracle.com/otndocs/products/javafx/2.2/samples/Ensemble/index.html
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.Date;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
/**
* A sample showing use of a Service to retrieve data in a background thread.
* Selecting the Refresh button restarts the Service.
*
* @see javafx.collections.FXCollections
* @see javafx.concurrent.Service
* @see javafx.concurrent.Task
* @see javafx.scene.control.ProgressIndicator
* @see javafx.scene.control.TableColumn
* @see javafx.scene.control.TableView
*/
public class ServiceSample extends Application {
final GetDailySalesService service = new GetDailySalesService();
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
VBox vbox = new VBox(5);
vbox.setPadding(new Insets(12));
ListView<TextField> listView = new ListView<TextField>();
Button button = new Button("Refresh");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
service.restart();
}
});
vbox.getChildren().addAll( listView, button);
Region veil = new Region();
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
ProgressIndicator p = new ProgressIndicator();
p.setMaxSize(150, 150);
p.progressProperty().bind(service.progressProperty());
veil.visibleProperty().bind(service.runningProperty());
p.visibleProperty().bind(service.runningProperty());
// what to put here ? tableView.itemsProperty().bind(service.valueProperty());
StackPane stack = new StackPane();
stack.getChildren().addAll(vbox, veil, p);
root.getChildren().add(stack);
service.start();
}
/**
* A service for getting the DailySales data. This service exposes an
* ObservableList for convenience when using the service. This
* <code>results</code> list is final, though its contents are replaced when
* a service call successfully concludes.
*/
public class GetDailySalesService extends Service<ObservableList<String>> {
/**
* Create and return the task for fetching the data. Note that this
* method is called on the background thread (all other code in this
* application is on the JavaFX Application Thread!).
*
* @return A task
*/
@Override
protected Task createTask() {
return new GetDailySalesTask();
}
}
public class GetDailySalesTask extends Task<ObservableList<String>> {
@Override protected ObservableList<String> call() throws Exception {
for (int i = 0; i < 500; i++) {
updateProgress(i, 500);
Thread.sleep(5);
}
ObservableList<String> sales = FXCollections.observableArrayList();
sales.add(new String("A1"));
sales.add(new String("A2"));
return sales;
}
}
@Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
答案 0 :(得分:3)
此答案演示如何使用绑定从服务更新一组JavaFX 2 TextField。答案的基础是您的问题中发布的原始示例代码。更新有效,因为ListView(字符串)的值类型现在与从服务返回的值的类型相匹配。
根据原始代码,进行以下修改:
变化:
ListView<TextField> listView = new ListView<TextField>();
要:
ListView<String> listView = new ListView<String>();
变化:
// what to put here ? tableView.itemsProperty().bind(service.valueProperty());
要:
listView.itemsProperty().bind(service.valueProperty());
现在listView将根据服务结果进行更新(为了演示更改,您可以在服务任务中添加一些随机数据,而不是使用固定数据)。
所以现在你有了一个ListView,它的数据会根据列表的值来更新。但你需要一个TextFields列表,而不是一个静态标签列表。要将TextFields放入ListView,您需要创建自己的单元工厂。
以下是原始示例的更新。该更新提供了几个单元工厂来处理TextField创建。 InstantEditingCell为所有项创建TextFields。单击项目时,ClickableEditingCell会创建单元格(该类的代码是从JavaFX TableView sample documentation中的EditingCell复制的。)
import java.util.Random;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
/**
* A sample showing use of a Service to retrieve data in a background thread.
* Selecting the Refresh button restarts the Service.
*
* @see javafx.collections.FXCollections
* @see javafx.concurrent.Service
* @see javafx.concurrent.Task
* @see javafx.scene.control.ProgressIndicator
* @see javafx.scene.control.TableColumn
* @see javafx.scene.control.TableView
*/
public class ServiceSample extends Application {
final GetDailySalesService service = new GetDailySalesService();
final Random random = new Random();
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
VBox vbox = new VBox(5);
vbox.setPadding(new Insets(12));
ListView<String> listView = new ListView<>();
listView.setEditable(true);
Button button = new Button("Refresh");
button.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent t) {
service.restart();
}
});
listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override public ListCell<String> call(ListView<String> param) {
return new InstantEditingCell();
}
});
listView.setPrefHeight(200);
vbox.getChildren().addAll( listView, button);
Region veil = new Region();
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
ProgressIndicator p = new ProgressIndicator();
p.setMaxSize(150, 150);
p.progressProperty().bind(service.progressProperty());
veil.visibleProperty().bind(service.runningProperty());
p.visibleProperty().bind(service.runningProperty());
listView.itemsProperty().bind(service.valueProperty());
StackPane stack = new StackPane();
stack.getChildren().addAll(vbox, veil, p);
root.getChildren().add(stack);
service.start();
}
/**
* A service for getting the DailySales data. This service exposes an
* ObservableList for convenience when using the service. This
* <code>results</code> list is final, though its contents are replaced when
* a service call successfully concludes.
*/
public class GetDailySalesService extends Service<ObservableList<String>> {
/**
* Create and return the task for fetching the data. Note that this
* method is called on the background thread (all other code in this
* application is on the JavaFX Application Thread!).
*
* @return A task
*/
@Override
protected Task createTask() {
return new GetDailySalesTask();
}
}
public class GetDailySalesTask extends Task<ObservableList<String>> {
@Override protected ObservableList<String> call() throws Exception {
for (int i = 0; i < 500; i++) {
updateProgress(i, 500);
Thread.sleep(5);
}
ObservableList<String> sales = FXCollections.observableArrayList();
sales.add("A1: " + random.nextInt());
sales.add("A2: " + random.nextInt());
return sales;
}
}
@Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
class InstantEditingCell extends ListCell<String> {
@Override public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
final TextField textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>(){
@Override public void changed(ObservableValue<? extends Boolean> value, Boolean wasFocused, Boolean isFocused) {
if (!isFocused) {
commitEdit(textField.getText());
}
}
});
setGraphic(textField);
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
class ClickableEditingCell extends ListCell<String> {
private TextField textField;
public ClickableEditingCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>(){
@Override public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
因为TextFields是可编辑的对象而不是静态对象,所以您可能还需要通过某种方式将编辑内容提交回原始数据的来源。
示例程序输出:
答案 1 :(得分:0)
这种方法有效,但没有“自动”方式将TextField列表绑定到服务的输出。
import java.lang.String;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.List;
import java.util.ListIterator;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
/**
* A sample showing use of a Service to retrieve data in a background thread.
* Selecting the Refresh button restarts the Service.
*
* @see javafx.collections.FXCollections
* @see javafx.concurrent.Service
* @see javafx.concurrent.Task
* @see javafx.scene.control.ProgressIndicator
* @see javafx.scene.control.TableColumn
* @see javafx.scene.control.TableView
*/
public class ServiceSample extends Application {
final GetDailySalesService service = new GetDailySalesService();
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
final VBox vbox = new VBox(5);
vbox.setPadding(new Insets(12));
Button button = new Button("Refresh");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
service.restart();
}
});
TextField tf1, tf2, tf3;
tf1 = new TextField();
tf1.setId("0");
tf2 = new TextField();
tf2.setId("1");
tf3 = new TextField();
tf3.setId("2");
vbox.getChildren().addAll(tf1,tf2,tf3,button);
Region veil = new Region();
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
ProgressIndicator p = new ProgressIndicator();
p.setMaxSize(150, 150);
p.progressProperty().bind(service.progressProperty());
veil.visibleProperty().bind(service.runningProperty());
p.visibleProperty().bind(service.runningProperty());
StackPane stack = new StackPane();
stack.getChildren().addAll(vbox, veil, p);
root.getChildren().add(stack);
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
System.out.println("done:" + t.getSource().getValue());
List list = service.valueProperty().getValue();
String[] sa = (String[]) list.toArray(new String[0]);
for(int i=0; i<sa.length;i++) {
TextField tf = (TextField) vbox.lookup("#"+i);
tf.setText(sa[i]);
}
}
});
service.start();
}
/**
* A service for getting the DailySales data. This service exposes an
* ObservableList for convenience when using the service. This
* <code>results</code> list is final, though its contents are replaced when
* a service call successfully concludes.
*/
public class GetDailySalesService extends Service<ObservableList<String>> {
/**
* Create and return the task for fetching the data. Note that this
* method is called on the background thread (all other code in this
* application is on the JavaFX Application Thread!).
*
* @return A task
*/
@Override
protected Task createTask() {
return new GetDailySalesTask();
}
}
public class GetDailySalesTask extends Task<ObservableList<String>> {
@Override protected ObservableList<String> call() throws Exception {
for (int i = 0; i < 500; i++) {
updateProgress(i, 500);
Thread.sleep(5);
}
ObservableList<String> sales = FXCollections.observableArrayList();
sales.add(new String("A1 " + System.currentTimeMillis()));
sales.add(new String("A2 " + System.currentTimeMillis()));
sales.add(new String("A3 " + System.currentTimeMillis()));
return sales;
}
}
@Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}