How to close javafx and swing window threads between subsequent calls? - javafx

Below is a functioning class that simulates extracting data from a website. The question is how to close the window and threads after the data is retrieved? The error message is
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Platform.exit has been called
On line: final JFXPanel fxPanel = new JFXPanel() {
Here is the code. When Platform.exit() is inserted at the indicated locations, the above error occurs. In the actual code, javafx is used for obtaining and scanning the html text. Also, javafx is used for setting attributes on the server and starting a program on the server, both done using java script. The javafx functionality was removed from this minimal example that demonstrates the error that occurs when using Platform.exit().
public class Step11a {
public static void main(String[] args) throws ParseException, InterruptedException {
Step11a st = new Step11a();
st.getData();
st.getData();
}
private JTable table;
private String url;
private WebView webView;
public Step11a() {
url = "https://www.tsp.gov/investmentfunds/shareprice/sharePriceHistory.shtml";
table = new JTable();
}
private Scene createScene(ArrayBlockingQueue<DefaultTableModel> platfromQueue) {
StackPane root = new StackPane();
Scene scene = new Scene(root);
webView = new WebView();
WebEngine webEngine = webView.getEngine();
Worker<Void> worker = webEngine.getLoadWorker();
worker.stateProperty().addListener((Observable o) -> {
if (worker.getState() == Worker.State.SUCCEEDED) {
DefaultTableModel tableDataModel = (DefaultTableModel) table.getModel();
// Following steps deleted for minimal example
// 1) Get html code from server
// 2) Locate elements to modify
// 3) Create javascript string to modify element values
// 4) Execute javascript to update elemetns
// 5) Execute javascript to start data retrieval on server
// 6) Get html and extract table of data
// For this example, we just set the rows and columns
// in the table
tableDataModel.setColumnCount(11);
tableDataModel.setRowCount(24);;
try {
platfromQueue.put(tableDataModel);
} catch (Exception e) {
e.printStackTrace();
}
}
});
webEngine.load(url);
root.getChildren().add(webView);
return scene;
}
private DefaultTableModel initAndShowGUI() { // This method is invoked on the EDT thread
JFrame frame = new JFrame("WebViewTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JFXPanel fxPanel = new JFXPanel() {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 400);
}
};
frame.add(fxPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ArrayBlockingQueue<DefaultTableModel> platformQueue = new ArrayBlockingQueue<DefaultTableModel>(5) ;
Platform.runLater(() -> {
initFX(fxPanel,platformQueue);
});
DefaultTableModel tspData = null;
try {
tspData = platformQueue.take();
} catch (Exception e) {
e.printStackTrace();
}
// Platform.exit(); // At this location causes error on next call
// error occurs on final JFXPanel fxPanel = new JFXPanel() {
return tspData;
}
private void initFX(JFXPanel fxPanel, ArrayBlockingQueue<DefaultTableModel> platformQueue) {
// This method is invoked on the JavaFX thread
// System.out.println("JavaFx thread name "+Thread.currentThread().getName());
Scene scene = createScene(platformQueue);
fxPanel.setScene(scene);
}
private DefaultTableModel getData() {
ArrayBlockingQueue<DefaultTableModel> swingQueue = new ArrayBlockingQueue<>(5);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
DefaultTableModel tspDataLocal = initAndShowGUI( );
swingQueue.put(tspDataLocal);
} catch (java.lang.ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
DefaultTableModel tableJFrameData = null;
try {
tableJFrameData = swingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Platform.exit(); // At this location causes error on next call
// error occurs on final JFXPanel fxPanel = new JFXPanel() {
return tableJFrameData;
}
}

Related

Java FX UI blocked by ExecutorService

I have a problem how to correctly update Java FX UI. I'm moving from Swing to FX for the first time and also ExecutorService. Problem is I need to show gif file and update progress bar during code execution but instead I get white screen with mouse loading icon. The gif and progressbar are eventually showed but after finishing this for cycle: for (int i = 0; i < futures.size(); i++)
I thought that running tasks in ExecutorService is in separate thread and using Platform.runLater for progressbar will separate UI from long running code in ExecutorService. Can you give me some explanation what is going on please?
Controller.java:
public void initialize() {
ivGif.setImage(new Image(Main.class.getResourceAsStream("/test/loading.gif")));
}
public void synchronizeFiles() {
Platform.runLater(() -> pbDownloading.setVisible(true));
ExecutorService pool = Executors.newFixedThreadPool(1);
ArrayList<Future<Boolean>> futures = new ArrayList<>();
File localFile = new File(simplified code here);
Future<Boolean> f = pool.submit(new DownloadTask(new URL(simplified code here), localFile));
futures.add(f);
for (int i = 0; i < futures.size(); i++) {
final int position = i;
Platform.runLater(() -> {
pbDownloading.setProgress(position / (double) futures.size());
});
if (!futures.get(i).get(600, TimeUnit.SECONDS)) {
System.out.println("ShutdownNow");
pool.shutdownNow();
}
}
}
DownloadTask.java:
public class DownloadTask implements Callable<Boolean> {
protected Category cat = Category.getInstance(DownloadTask.class.getName());
private URL fileURL;
private File toPath;
public DownloadTask(URL fileURL, File toPath) {
this.fileURL = fileURL;
this.toPath = toPath;
}
private void downloadFile(URL fileURL, File toPath) throws IOException {
ReadableByteChannel readableByteChannel = Channels.newChannel(fileURL.openStream());
if (!toPath.getParentFile().exists()) {
if (!toPath.getParentFile().mkdirs()) throw new IOException("Unable to create parent dirs for file: "+toPath.getAbsolutePath());
}
FileOutputStream fileOutputStream = new FileOutputStream(toPath);
fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
URLConnection urlConnection = fileURL.openConnection();
if (!toPath.setLastModified(urlConnection.getLastModified())) cat.error("Unable to write modified time stamp for file: "+toPath.getAbsolutePath());
}
#Override
public Boolean call() {
try {
downloadFile(fileURL, toPath);
} catch (IOException e){
System.out.println(e);
cat.error(e, e);
return false;
}
return true;
}
}
Thank you
EDIT: I didn't realize that the main class is running from FX Application Thread. So I had reverse threads and just applied the new thread (() -> {...}).Start(); in the main class in the start method and it's ok.

Handle multiple JavaFX application launches within a loop

My code currently reads my Gmail inbox via IMAP (imaps) and javamail, and once it finds an email with zip/xap attachment, it displays a stage (window) asking whether to download the file, yes or no.
I want the stage to close once I make a selection, and then return to the place within the loop from which the call came. My problem arises because you cannot launch an application more than once, so I read here that I should write Platform.setImplicitExit(false); in the start method, and then use primartyStage.hide() (?) and then something like Platform.runLater(() -> primaryStage.show()); when I need to display the stage again later.
The problem occuring now is that the flow of command begins in Mail.java's doit() method which loops through my inbox, and launch(args) occurs within a for loop within the method. This means launch(args) then calls start to set the scene, and show the stage. Since there is a Controller.java and fxml associated, the Controller class has an event handler for the stage's buttons which "intercept" the flow once start has shown the stage. Therefore when I click Yes or No it hides the stage but then just hangs there. As if it can't return to the start method to continue the loop from where launch(args) occurred. How do I properly hide/show the stage whenever necessary, allowing the loop to continue whether yes or no was clicked.
Here is the code for Mail.java and Controller.java. Thanks a lot!
Mail.java
[Other variables set here]
public static int launchCount = 0;#FXML public Text subjectHolder;
public static ReceiveMailImap obj = new ReceiveMailImap();
public static void main(String[] args) throws IOException, MessagingException {
ReceiveMailImap.doit();
}
#Override
public void start(Stage primaryStage) throws Exception {
loader = new FXMLLoader(getClass().getResource("prompts.fxml"));
root = loader.load();
controller = loader.getController();
controller.setPrimaryStage(primaryStage);
scene = new Scene(root, 450, 250);
controller.setPrimaryScene(scene);
scene.getStylesheets().add("styleMain.css");
Platform.setImplicitExit(false);
primaryStage.setTitle("Download this file?");
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void doit() throws MessagingException, IOException {
Folder inbox = null;
Store store = null;
try {
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
store = session.getStore("imaps");
store.connect("imap.gmail.com", "myAccount#gmail.com", "Password");
inbox = store.getFolder("Inbox");
inbox.open(Folder.READ_WRITE);
Message[] messages = inbox.getMessages();
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(UIDFolder.FetchProfileItem.FLAGS);
fp.add(UIDFolder.FetchProfileItem.CONTENT_INFO);
fp.add("X-mailer");
inbox.fetch(messages, fp);
int doc = 0;
int maxDocs = 400;
for (int i = messages.length - 1; i >= 0; i--) {
Message message = messages[i];
if (doc < maxDocs) {
doc++;
message.getSubject();
if (!hasAttachments(message)) {
continue;
}
String from = "Sender Unknown";
if (message.getReplyTo().length >= 1) {
from = message.getReplyTo()[0].toString();
} else if (message.getFrom().length >= 1) {
from = message.getFrom()[0].toString();
}
subject = message.getSubject();
if (from.contains("myAccount#gmail.com")) {
saveAttachment(message.getContent());
message.setFlag(Flags.Flag.SEEN, true);
}
}
}
} finally {
if (inbox != null) {
inbox.close(true);
}
if (store != null) {
store.close();
}
}
}
public static boolean hasAttachments(Message msg) throws MessagingException, IOException {
if (msg.isMimeType("multipart/mixed")) {
Multipart mp = (Multipart) msg.getContent();
if (mp.getCount() > 1) return true;
}
return false;
}
public static void saveAttachment(Object content)
throws IOException, MessagingException {
out = null; in = null;
try {
if (content instanceof Multipart) {
Multipart multi = ((Multipart) content);
parts = multi.getCount();
for (int j = 0; j < parts; ++j) {
part = (MimeBodyPart) multi.getBodyPart(j);
if (part.getContent() instanceof Multipart) {
// part-within-a-part, do some recursion...
saveAttachment(part.getContent());
} else {
int allow = 0;
if (part.isMimeType("application/x-silverlight-app")) {
extension = "xap";
allow = 1;
} else {
extension = "zip";
allow = 1;
}
if (allow == 1) {
if (launchCount == 0) {
launch(args);
launchCount++;
} else {
Platform.runLater(() -> primaryStage.show());
}
} else {
continue;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if ( in != null) { in .close();
}
if (out != null) {
out.flush();
out.close();
}
}
}
public static File createFolder(String subject) {
JFileChooser fr = new JFileChooser();
FileSystemView myDocs = fr.getFileSystemView();
String myDocuments = myDocs.getDefaultDirectory().toString();
dir = new File(myDocuments + "\\" + subject);
savePathNoExtension = dir.toString();
dir.mkdir();
System.out.println("Just created: " + dir);
return dir;
}
}
Controller.java
public class Controller implements Initializable {
#FXML
private Text subjectHolder;
public Button yesButton, noButton;
public ReceiveMailImap subject;
#Override
public void initialize(URL url, ResourceBundle rb) {
subject= new ReceiveMailImap();
subjectHolder.setText(subject.returnSubject());
}
public Stage primaryStage;
public Scene scene;
#FXML
ComboBox<String> fieldCombo;
public void setPrimaryStage(Stage stage) {
this.primaryStage = stage;
}
public void setPrimaryScene(Scene scene) {
this.scene = scene;
}
public String buttonPressed(ActionEvent e) throws IOException, MessagingException {
Object source = e.getSource();
if(source==yesButton){
System.out.println("How to tell Mail.java that user clicked Yes?");
return "POSITIVE";}
else{subject.dlOrNot("no");
System.out.println("How to tell Mail.java that user clicked No?");
primaryStage.hide();
return "NEGATIVE";}
}
}
There are a lot of issues with the code you have posted, but let me just try to address the ones you ask about.
The reason the code hangs is that Application.launch(...)
does not return until the application has exited
In general, you've kind of misunderstood the entire lifecycle of a JavaFX application here. You should think of the start(...) method as the equivalent of the main(...) method in a "traditional" Java application. The only thing to be aware of is that start(...) is executed on the FX Application Thread, so if you need to execute any blocking code, you need to put it in a background thread.
The start(...) method is passed a Stage instance for convenience, as the most common thing to do is to create a scene graph and display it in a stage. You are under no obligation to use this stage though, you can ignore it and just create your own stages as and when you need.
I think you can basically structure your code as follows (though, to be honest, I have quite a lot of trouble understanding what you're doing):
public class Mail extends Application {
#Override
public void start(Stage ignored) throws Exception {
Platform.setImplicitExit(false);
Message[] messages = /* retrieve messages */ ;
for (Message message : messages) {
if ( /* need to display window */) {
showMessage(message);
}
}
}
private void showMessage(Message message) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("prompts.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
Scene scene = new Scene(root, 450, 250);
stage.setScene(scene);
stage.initStyle(StageStyle.UNDECORATED);
stage.setTitle(...);
// showAndWait will block execution until the window is hidden, so
// you can query which button was pressed afterwards:
stage.showAndWait();
if (controller.wasYesPressed()) {
// ...
}
}
// for IDEs that don't support directly launching a JavaFX Application:
public static void main(String[] args) {
launch(args);
}
}
Obviously your logic for decided whether to show a window is more complex, but this will give you the basic structure.
To check which button was pressed, use showAndWait as above and then in your controller do
public class Controller {
#FXML
private Button yesButton ;
private boolean yesButtonPressed = false ;
public boolean wasYesPressed() {
return yesButtonPressed ;
}
// use different handlers for different buttons:
#FXML
private void yesButtonPressed() {
yesButtonPressed = true ;
closeWindow();
}
#FXML
private void noButtonPressed() {
yesButtonPressed = false ; // not really needed, but makes things clearer
closeWindow();
}
private void closeWindow() {
// can use any #FXML-injected node here:
yesButton.getScene().getWindow().hide();
}
}

read a text file dynamically in javafx

I need to read aad text file and show the content in text area but the problem is that my text file is updating with every second . IS that possible to show the content of text file in text area dynamically i.e., with every second new data is added into text file and i need to show the new data also
this is my code in the controller
public void initialize(URL url, ResourceBundle rb) {
try {
Scanner s = new Scanner(new File("E:\\work\\programming\\NetBeansProjects\\FinalProject\\src\\logs\\diskCheck.txt")).useDelimiter("\\s+");
while (s.hasNext()) {
if (s.hasNextInt()) { // check if next token is an int
diskchecktextarea.appendText(s.nextInt() + " "); // display the found integer
} else {
diskchecktextarea.appendText(s.next() + " "); // else read the next token
}
}
} catch (FileNotFoundException ex) {
System.err.println(ex);
}
}
I've wanted to test the watch feature for a while now, so I took the chance with your question and made you an example. Please adapt the watchPath to your needs. The file doesn't have to exist initially. It'll be found once you create it.
/**
* Documentation: https://blogs.oracle.com/thejavatutorials/entry/watching_a_directory_for_changes
*/
public class WatchFileChanges extends Application {
Path watchPath = Paths.get("c:/temp/watch.txt");
TextArea textArea;
#Override
public void start(Stage primaryStage) throws IOException {
BorderPane root = new BorderPane();
textArea = new TextArea();
root.setCenter(textArea);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
// load file initally
if (Files.exists(watchPath)) {
loadFile();
}
// watch file
WatchThread watchThread = new WatchThread(watchPath);
watchThread.setDaemon( true);
watchThread.start();
}
private void loadFile() {
try {
String stringFromFile = Files.lines(watchPath).collect(Collectors.joining("\n"));
textArea.setText(stringFromFile);
} catch (Exception e) {
e.printStackTrace();
}
}
private class WatchThread extends Thread {
Path watchPath;
public WatchThread(Path watchPath) {
this.watchPath = watchPath;
}
public void run() {
try {
WatchService watcher = FileSystems.getDefault().newWatchService();
WatchKey key = watchPath.getParent().register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
// wait for key to be signaled
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path path = ev.context();
if (!path.getFileName().equals(watchPath.getFileName())) {
continue;
}
// process file
Platform.runLater(() -> {
loadFile();
});
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException x) {
System.err.println(x);
}
}
}
public static void main(String[] args) {
launch(args);
}
}

Javafx - Bind the background task with Scene

My application having the two Screens.
Screen-1:
It's having two buttons and one Label
1) Download:
If we click on this, then we will start the downloading process but we still in screen1 and allow you to access the "Navigate" control.
2) Navigate: If we click on this, Then we will redirect to the screen-2.
Screen-2:
1)Back: If we click on this, then we will back to the Screen-1.
While downloading process, I want to allow the user to access the other controls as well. If we started the download process and navigates to some other screen and redirects to the download screen, then we will show the current downloading progress instead of opening it as fresh. For this, I implemented like following. I created one class for implementing this download process but I am unable to update the UI of the screen from that class. Please help me on this.
Screen-1
public class MainsceneController implements Initializable {
#FXML
Button Download, Navigate;
#FXML
Label percentage;
#FXML
HBox progTag;
SyncService service = new SyncService(MainsceneController.this);
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
}
#FXML
void DownlaodManager() {
service.downloadProjectFiles();
}
#FXML
void Naviagtion() {
URL location = SecondSceneController.class.getResource("SecondScene.fxml");
ViewManager.getInstance().setView(location);
}
}
Screen-2:
public class SecondSceneController implements Initializable {
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
void goBack() {
URL location = MainsceneController.class.getResource("mainscene.fxml");
ViewManager.getInstance().setView(location);
}
}
Background downloading task
public class SyncService {
long currentDownload, totalFileSize;
MainsceneController controller;
DownloadingFilesTask downloadingFilesTask;
public SyncService(MainsceneController controller) {
this.controller = controller;
downloadingFilesTask = new DownloadingFilesTask();
}
public void downloadProjectFiles() throws IOException {
DownloadingFilesTask downloadingFilesTask = new DownloadingFilesTask();
downloadingFilesTask.progressProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number oldProgress, Number newProgress) {
System.out.println("Progress changed");
controller.percentage.setText("Progress changed:" + currentDownload);
}
});
downloadingFilesTask.stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends Worker.State> source, Worker.State oldState, Worker.State newState) {
if (newState.equals(Worker.State.SUCCEEDED)) {
System.err.println("Completed downloading files");
controller.percentage.setText("Progress changed:" + currentDownload);
}
}
});
//progress listeners.
ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(downloadingFilesTask.progressProperty());
bar.visibleProperty().bind(downloadingFilesTask.runningProperty());
controller.progTag.getChildren().clear();
controller.progTag.getChildren().add(bar);
new Thread(downloadingFilesTask).start();
}
class DownloadingFilesTask extends Task<Void> {
#Override
protected Void call() throws Exception {
try {
String fullUrl = "http://s3-us-west-2.amazonaws.com/absprod/media/25/manuals/53454a73d9eda$$53454a73d9f571397049971.mp4";
String destLocation = "C:\\Users\\naresh.repalle\\Desktop\\ABS Test\\53454a73d9eda$$53454a73d9f571397049971.mp4";
File destFile = new File(destLocation);
URL downloadingUrl = new URL(fullUrl);
RandomAccessFile file = null;
InputStream stream = null;
int downloaded = 0;
int size = -1;
try {
// Open connection to URL.
HttpURLConnection connection = (HttpURLConnection) downloadingUrl.openConnection();
// Specify what portion of file to download.
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
connection.setConnectTimeout(10 * 1000);
connection.setReadTimeout(10 * 1000);
// Connect to server.
connection.connect();
// Make sure response code is in the 200 range.
if (connection.getResponseCode() / 100 != 2) {
System.err.println("Wrong response code while downloading file." + connection.getResponseCode());
}
// Check for valid content length.
int contentLength = connection.getContentLength();
if (contentLength < 1) {
System.err.println("Wrong file size while downloading file." + contentLength);
}
/*
* Set the size for this download if it hasn't been already set.
*/
if (size == -1) {
size = contentLength;
}
totalFileSize = size;
// Open file and seek to the end of it.
file = new RandomAccessFile(destFile, "rw");
file.seek(downloaded);
stream = connection.getInputStream();
int MAX_BUFFER_SIZE = 1024;
while (true) {
/*
* Size buffer according to how much of the file is left to download.
*/
byte buffer[];
if (size - downloaded > MAX_BUFFER_SIZE) {
buffer = new byte[MAX_BUFFER_SIZE];
} else {
buffer = new byte[size - downloaded];
}
// Read from server into buffer.
int read = stream.read(buffer);
if (read == -1) {
System.out.println("read: " + read);
break;
}
// Write buffer to file.
file.write(buffer, 0, read);
downloaded += read;
currentDownload = downloaded;
stateChanged();
}
} catch (Exception e) {
System.err.println("Exception in Downloading file: " + e.toString());
} finally {
/*
* Change status to complete if this point was reached because downloading
* has finished.
*/
// Close file.
if (file != null) {
try {
file.close();
} catch (Exception e) {
}
}
// Close connection to server.
if (stream != null) {
try {
stream.close();
} catch (Exception e) {
System.err.println("exception in downloading: " + e.toString());
}
}
}
} catch (Exception ex) {
System.err.println("Unable to download file: " + ex);
}
return null;
}
private void stateChanged() {
updateProgress(currentDownload, totalFileSize);
}
}
}
Your SyncService doesn't need to reload the FXML; it just needs to communicate with the existing controller. A simple way to do this would be to just pass a reference to SyncService's constructor:
public class SyncService {
MainSceneController controller ;
public SyncService(MainSceneController controller) {
this.controller = controller ;
}
// ...
public void downloadProjectFiles() throws IOException {
DownloadingFilesTask downloadingFilesTask = new DownloadingFilesTask();
// Remove the following:
// URL location = MainsceneController.class.getResource("mainscene.fxml");
// FXMLLoader fxmlLoader = new FXMLLoader();
// fxmlLoader.setLocation(location);
// fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
// final AnchorPane root = (AnchorPane) fxmlLoader.load(location.openStream());
//get the controller
// final MainsceneController controller = (MainsceneController) fxmlLoader.getController();
// Then code as before
// ...
}
}
I think I would actually do it differently: I don't like the strong coupling between the SyncService and the MainSceneController. I would initialize the progress bar in the MainSceneController, expose the progress as a property in the SyncService and bind to it in the controller. But you should be able to use the simpler approach to get it working.

Asynchronous load of content within Pagination

I am search an pagination example for loading data asynchron from a server. I have no clue how to solve that with the pagination control of javafx. Well i hava an example where an observable list is loaded in the background with 10k of items. But i only want to load the items for a page when its actually needed. So only when the user switcht to the next page i want to grab the next 20 items with a task. When the task is done the page should be rendered..
Thanks for any advice and help!
Link to observable example:
https://forums.oracle.com/forums/thread.jspa?messageID=10976705#10976705
All you need is to start a background thread with your task once user clicked on a page. See next example which uses sites downloading for a long task:
public class Pages extends Application {
#Override
public void start(Stage primaryStage) {
final Pagination root = new Pagination(urls.length, 0);
root.setPageFactory(new Callback<Integer, Node>() {
// This method will be called every time user clicks on page button
public Node call(final Integer pageIndex) {
final Label content = new Label("Please, wait");
content.setWrapText(true);
StackPane box = new StackPane();
box.getChildren().add(content);
// here we starts long operation in another thread
new Thread() {
String result;
public void run() {
try {
URL url = new URL(urls[pageIndex]);
URLConnection urlConnection = url.openConnection();
urlConnection.setConnectTimeout(1000);
urlConnection.setReadTimeout(1000);
BufferedReader breader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = breader.readLine()) != null) {
stringBuilder.append(line);
}
result = stringBuilder.toString();
} catch (Exception ex) {
result = "Download failed";
}
// once operation is finished we update UI with results
Platform.runLater(new Runnable() {
#Override
public void run() {
content.setText(result);
}
});
}
}.start();
return box;
}
});
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Pages!");
primaryStage.setScene(scene);
primaryStage.show();
}
private final static String[] urls = {"http://oracle.com", "http://stackoverflow.com", "http://stackexchange~.com", "http://google.com", "http://javafx.com"};
public static void main(String[] args) {
launch(args);
}
}

Resources