2

我正在尝试了解如何使用服务更新 JavaFX2.2 UI 中的一组文本字段,但文档中没有示例或提示。Addressbook 和 Ensemble 示例都处理表格。该服务将返回一个 ObservableList 字符串。如何进行绑定?

textField0 <---> observableList[0]

textField1 <---> observableList[1]

我修改了 ServiceSample (并发 | 服务示例http://download.oracle.com/otndocs/products/javafx/2.2/samples/Ensemble/index.html)有一个TextFields 的 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); }
}
4

2 回答 2

3

此答案演示了使用绑定从服务更新一组 JavaFX 2 文本字段。答案的基础是您问题中发布的原始示例代码。更新有效,因为 ListView (Strings) 的值类型现在与从服务返回的值的类型相匹配。

对您的原始代码进行以下修改:

改变:

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,它的数据会根据传入的列表的值进行更新。但是你想要一个 TextField 列表,而不是静态标签列表。要将 TextFields 放入 ListView,您需要创建自己的单元工厂。

这是对原始示例的更新。该更新提供了几个单元工厂来处理 TextField 创建。InstantEditingCell 为所有项目创建文本字段。ClickableEditingCell 在您单击项目时创建单元格(该类的代码是从JavaFX TableView 示例文档中的 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();
        }
    }

}

因为 TextField 是可编辑对象而不是静态对象,所以您可能还想想办法将您的编辑提交回原始数据的来源。

示例程序输出:

文本字段列表

于 2012-10-31T23:51:55.260 回答
0

这种排序有效,但没有“自动”方式将文本字段列表绑定到服务的输出。

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); }
}
于 2012-10-31T22:05:01.410 回答