JavaFX:使用REST服务并在前端显示数据 [英] JavaFX : Consuming REST service and displaying the data in front-end

查看:198
本文介绍了JavaFX:使用REST服务并在前端显示数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个JavaFX(在带有SceneBuilder的JDK8上)项目,该项目应该与基于Spring-MVC的服务器连接,我想从服务器访问一些对象并显示它。我已经编写了Spring服务器来根据请求回放所需的对象,但是我对UI编程和JavaFX的不熟悉使它变得有点困难。

I am working on a JavaFX(On JDK8 with SceneBuilder) project which should connect with a Spring-MVC based server and I would like to access some Objects from the server and display it. I have already programmed the Spring server to give back the desired object upon request, but my unfamiliarity in UI programming and JavaFX is making it a bit difficult.

在FXML文件中,我已经添加了一个网格窗格,我想在那里显示对象。我很感激你们的任何帮助,让我们开始。我只有基本代码,但我在下面粘贴它:

In the FXML file, I already have added a grid-pane and I would like to display the objects there. I would appreciate any help from you guys to get started up. I have only basic code, but I am pasting it below :

Canvas.fxml:

Canvas.fxml :

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>

<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
  <columnConstraints>
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
  </columnConstraints>
  <rowConstraints>
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
  </rowConstraints>
</GridPane>

CanvasModel类:

CanvasModel class :

public class Canvas {

    private int canvasid;

    private final StringProperty canvasName;

    private final StringProperty canvasTitle;

    private final StringProperty canvasImage;

    private byte[] canvasImageInBytes;


    public Canvas(String canvasName, String canvasTitle, String canvasImage){
        this.canvasName = new SimpleStringProperty(canvasName);
        this.canvasTitle = new SimpleStringProperty(canvasTitle);
        this.canvasImage = new SimpleStringProperty(canvasImage);
    }
//Getters and setters ommitted
}

Main class:

Main class :

public class Main extends Application {

    private Stage primaryStage;

    @Override
    public void start(Stage primaryStage) throws Exception{
       Parent root = FXMLLoader.load(getClass().getResource("../View/mainui.fxml"));

primaryStage.setTitle("CheckAPP");
       primaryStage.setScene(new Scene(root, 300, 600));
        primaryStage.setFullScreen(false);
        primaryStage.setMaximized(false);

        primaryStage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }

    public Stage getPrimaryStage() {
        return this.primaryStage;
    }

}

现在向下方发出请求:

Now making a request to below :

http://localhost:8080/canvaslist

它将返回一个我希望在GridPane中显示的java.util.List。图像采用String格式。我明白这不是一个常规问题,因为这不起作用,但我试图绕过我的头脑进行UI编程,我不知道还有什么要转向。非常感谢你的帮助。

It will return a java.util.List which I want to display in GridPane. The images are in String format. I understand this is not a regular question like this is not working and all, but I am trying to wrap around my head for UI programming, and I didn't knew where else to turn to. Thanks a lot for your help.

推荐答案

只需加上我的几美分来帮助你。

Just adding my few cents to help you.


  1. 使用后台线程从服务中获取数据,因为可能需要一段时间才能获得响应。在JavaFX应用程序线程上执行它可能会导致不良用户体验。在这种情况下, 任务 将为您提供帮助。

  2. 获得回复后,构建必要的对象/对象集合它将用于更新场景图上的元素。

  3. 因为 JavaFX应用程序线程是唯一可以使用它的线程访问实时场景图元素,你不能在后台线程中直接使用它们(使用它们会导致 IllegalStateException )。您可以使用 Platform.runLater 或调用任务的方法保证在FX应用程序线程上更新状态,如 updateProgress(...) updateMessage(...) getValue(。 ..) 等。

  4. 使用您构建的对象/对象集合 Step-2 中创建(或更新)FX场景图元素。

  5. 在c中虽然你有一个控件(比如TableView或ListView)接受 ObservableList 作为其内容,你可以绑定内容和其中一个任务的属性,根据其使用情况在执行期间/之后自动更新它们。

  6. 但在你的情况下,因为你有 GridPane ,所以我们可能需要更进一步,编写逻辑来创建控件并将它们添加到 GridPane 。 / li>
  1. Use a background thread to fetch the data from the service, as it may take time to get response back. Executing it on the JavaFX application thread might result in undesirable user experience. In this case Task will help you.
  2. Once you have response, construct a necessary object / collection of object from it which you will be using to update the elements on scene graph.
  3. Since JavaFX Application Thread is the only thread on which you can access live scene graph elements, you cannot directly use them in your background thread ( Using them will result in IllegalStateException ). You can update the data on the JavaFX scene graph by either using Platform.runLater or calling the methods of the task which are guaranteed to update state on the FX Application Thread, like updateProgress(...), updateMessage(...), getValue(...) etc.
  4. Use the object / collection of object that you constructed in Step-2 to create (or update) FX scene graph elements.
  5. In case you have a control (like TableView or ListView) which accepts ObservableList as its contents, you can just bind the contents and one of the Task's property, to update them automatically during/ after the execution depending on their usage.
  6. But in your case, since you have GridPane, so we might have to take a step further and write logic to create controls and add them to the GridPane.



示例



我创建了一个使用服务的示例,解析JSON数据并从中创建 GridPane 。 JSON在StackOverflow 上有几个人的列表,上面有他们的名字,他们的喜欢(根据我)和SO上的个人资料。

Example

I have created an example with consumes a service, parses the JSON data and creates a GridPane out of it. The JSON has list of few people on StackOverflow with their names, their likes (according to me) and profile pic on SO.

它使用后台任务从服务加载JSON数据,使用 setOnSucceeded(...) 任务的处理程序将其传递给 createGridPane(onservableList)创建一个 GridPane

It uses a background Task to load the JSON data from the service, uses setOnSucceeded(...) handler of the Task to pass it to a createGridPane(onservableList) which creates a GridPane.

其中一个 GridPane 的列包含相应人员的个人资料图片。由于这些图像可能需要一段时间才能下载,因此我将多个线程生成为每个用户加载图像

One of the columns of the GridPane contains the profile picture of the respective person. Since these images can take time to download, I spawn multiple threads to load the image for each user.

您可以在此处找到源代码

它使用 GSON 作为将 JSON 转换为 POJO 类的库。

It uses GSON as a library to convert JSON to POJO class.

import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ConsumeJSON extends Application {

    private ObservableList<PeopleOnSO> listOfPeople;
    private static final String JSON_URL = "https://api.myjson.com/bins/3jwmh";
    private static final String IMAGE_URL = "http://www.fontspring.com/presentation_20150512/images/ajax_loader_blue_512.gif";
    private final ExecutorService executorService = Executors.newCachedThreadPool();
    private Image loadImage;

    @Override
    public void start(Stage stage) throws Exception {

        loadImage = new Image(IMAGE_URL);
        VBox root = new VBox();
        root.setAlignment(Pos.TOP_CENTER);
        root.setPadding(new Insets(20));
        root.setSpacing(20);

        Button button = new Button("Fill GridPane");

        root.getChildren().addAll(button);


        button.setOnAction(e -> {
            // Display loading image
            ImageView loading = new ImageView(loadImage);
            loading.setFitWidth(60);
            loading.setFitHeight(60);
            root.getChildren().add(loading);
            executorService.submit(fetchList);
        });


        fetchList.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent t) {
                listOfPeople = FXCollections.observableArrayList(fetchList.getValue());
                GridPane gridPane = createGridPane(listOfPeople);
                //Remove Loading Image and add GridPane
                root.getChildren().remove(1);
                VBox.setVgrow(gridPane, Priority.ALWAYS);
                root.getChildren().add(gridPane);
                stage.sizeToScene();
            }
        });

        ScrollPane scrollPane = new ScrollPane(root);
        Scene scene = new Scene(scrollPane, 600, 500);
        stage.setScene(scene);
        stage.setTitle("Load Data from JSON");
        stage.show();

        stage.setOnCloseRequest(e -> {
            executorService.shutdown();
        });
    }

    public GridPane createGridPane(ObservableList<PeopleOnSO> listOfPeople){
        GridPane gridPane = new GridPane();
        gridPane.setAlignment(Pos.CENTER);
        gridPane.setGridLinesVisible(true);
        gridPane.setPadding(new Insets(20));
        gridPane.setMinHeight(500);
        gridPane.setMaxWidth(500);

        //Create headings
        Label nameHeading = new Label("Name");
        nameHeading.setStyle("-fx-font-weight: bold");
        Label likeHeading = new Label("Likes");
        likeHeading.setStyle("-fx-font-weight: bold");
        Label imageHeading = new Label("Image");
        imageHeading.setStyle("-fx-font-weight: bold");

        gridPane.add(nameHeading, 0, 0);
        gridPane.add(likeHeading, 1, 0);
        gridPane.add(imageHeading, 2, 0);

        // Aligning at center
        alignElements(nameHeading, likeHeading, imageHeading);

        // Setting Constraints
        for (int i = 0; i < 3; i++) {
            ColumnConstraints column = new ColumnConstraints(150);
            // column.setPercentWidth(80);
            gridPane.getColumnConstraints().add(column);
        }

        for (int i = 0; i < listOfPeople.size(); i++) {

            PeopleOnSO people = listOfPeople.get(i);
            Label nameLabel = new Label(people.getName());
            Label likeLabel = new Label(people.getLike());
            ImageView imageView = new ImageView(loadImage);
            imageView.setFitHeight(60);
            imageView.setFitWidth(60);

            //Thread for loading images later
            FetchImage fetchImage = new FetchImage(people.getImageUrl());
            fetchImage.setOnSucceeded(worker -> {
                imageView.setImage((Image) fetchImage.getValue());
            });

            executorService.submit(fetchImage);

            // Adding to GridPane and necessary configuration
            gridPane.add(nameLabel, 0, i + 1);
            gridPane.add(likeLabel, 1, i + 1);
            gridPane.add(imageView, 2, i + 1);

            //Aligning at center
            alignElements(nameLabel, likeLabel, imageView);

            gridPane.getRowConstraints().add(new RowConstraints(80));
        }
        return gridPane;
    }

    /**
     * Align elements at the center
     * @param nodes
     */
    private void alignElements(Node ... nodes ) {
        for(Node node : nodes) {
            GridPane.setHalignment(node, HPos.CENTER);
            GridPane.setValignment(node, VPos.CENTER);
        }
    }

    /**
     * Task to fetch details from JSONURL
     * @param <V>
     */
    private Task<List<PeopleOnSO>> fetchList = new Task() {
        @Override
        protected List<PeopleOnSO> call() throws Exception {
            List<PeopleOnSO> list = null;
            try {
                Gson gson = new Gson();
                list = new Gson().fromJson(readUrl(JSON_URL), new TypeToken<List<PeopleOnSO>>() {
                }.getType());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return list;
        }
    };

    /**
     * Task to fetch images for individual ImageViews
     * @param <V>
     */
    private class FetchImage<V> extends Task<Image> {

        private String imageUrl;

        public FetchImage(String imageUrl) {
            this.imageUrl = imageUrl;
        }

        @Override
        protected Image call() throws Exception {
            Image image = new Image(imageUrl);
            return image;
        }

    }

    /**
     * Read the URL and return the json data
     * @param urlString
     * @return
     * @throws Exception
     */
    private static String readUrl(String urlString) throws Exception {
        BufferedReader reader = null;
        try {
            URL url = new URL(urlString);
            reader = new BufferedReader(new InputStreamReader(url.openStream()));
            StringBuffer buffer = new StringBuffer();
            int read;
            char[] chars = new char[1024];
            while ((read = reader.read(chars)) != -1)
                buffer.append(chars, 0, read);

            return buffer.toString();
        } finally {
            if (reader != null)
                reader.close();
        }
    }

    private class PeopleOnSO {

        private final String name;
        private final String like;
        private final String imageUrl;

        public PeopleOnSO(String name, String like, String imageUrl){
            this.name = new String(name);
            this.like = new String(like);
            this.imageUrl = new String(imageUrl);
        }

        public String getName() {
            return name;
        }

        public String getLike() {
            return like;
        }

        public String getImageUrl() {
            return imageUrl;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这篇关于JavaFX:使用REST服务并在前端显示数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆