JavaFX:使用REST服务并在前端显示数据 [英] JavaFX : Consuming REST service and displaying the data in front-end
问题描述
我正在开发一个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.
- 使用
后台线程
从服务中获取数据,因为可能需要一段时间才能获得响应。在JavaFX应用程序线程上执行它可能会导致不良用户体验。在这种情况下,任务
将为您提供帮助。 - 获得回复后,构建必要的
对象/对象集合
它将用于更新场景图上的元素。 - 因为
JavaFX应用程序线程
是唯一可以使用它的线程访问实时场景图
元素,你不能在后台线程中直接使用它们(使用它们会导致IllegalStateException
)。您可以使用Platform.runLater
或调用任务
的方法保证在FX应用程序线程上更新状态,如updateProgress(...)
,updateMessage(...)
,getValue(。 ..)
等。 - 使用您构建的
对象/对象集合
在Step-2
中创建(或更新)FX场景图元素。 - 在c中虽然你有一个控件(比如TableView或ListView)接受
ObservableList
作为其内容,你可以绑定内容和其中一个任务
的属性,根据其使用情况在执行期间/之后自动更新它们。 - 但在你的情况下,因为你有
GridPane
,所以我们可能需要更进一步,编写逻辑来创建控件并将它们添加到GridPane
。 / li>
- 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 caseTask
will help you. - 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. - Since
JavaFX Application Thread
is the only thread on which you can accesslive scene graph
elements, you cannot directly use them in your background thread ( Using them will result inIllegalStateException
). You can update the data on the JavaFX scene graph by either usingPlatform.runLater
or calling the methods of thetask
which are guaranteed to update state on the FX Application Thread, likeupdateProgress(...)
,updateMessage(...)
,getValue(...)
etc. - Use the
object / collection of object
that you constructed inStep-2
to create (or update) FX scene graph elements. - 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 theTask
's property, to update them automatically during/ after the execution depending on their usage. - 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 theGridPane
.
示例
我创建了一个使用服务的示例,解析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屋!