JavaFX - bind property from another class onto a s

2019-04-13 22:56发布

So, I have a very very simple application that is supposed to handle downloading certain files and updating them into a certain directory. The application has a simple interface with a Status Bar that should tell the user how the download process is going. So far, so good.

I have a few implementations of classes that extend the javafx.concurrent.Task class. Those classes handle the downloading and saving of files for each system, taking care of the particularities for each one. The download process itself (which actually comes from a remote database) is the same for everyone, so, it's implemented in another class which is called FileDownloader as it is, naturally, supposed to be reusable.

So, we have an UpdateTask class with a call() method that, in turn, calls some method in the FileDownloader class.

Trouble is: I would need to bind a progressProperty to the bar, but only UpdateTask has this property, while most of the work I want to show in the bar is done inside FileDownloader. I cannot call updateProgress from within the download method, as it isn't inside the Task class. I cannot pass the task into the method and call the update method there. I cannot make a listener on the progress property and listen for changes on the fields inside the downloader class. It seems I cannot do anything and the only real solution would be to count the work being done inside the call() method. Woe is me.

Is there a solution for this that doesn't involve me writing less reusable code? Example code as follows:

public class UpdateTask extends Task<Void> {

@Override
protected Void call() throws Exception {

//some other operations are performed before this part

FileDownloader downloader = new FileDownloader();

//The stuff I wanna count is in there :(
boolean isDownloaded = downloader.downloadSysVersion();

//some other stuff happens that depends on the completion of the download
}

Whoever shows me how to get the information from inside the downloadSysVersion method gets a free internet cookie.

2条回答
爷的心禁止访问
2楼-- · 2019-04-13 23:09

Add a property in the FileDownloader class representing the progress. For example, if you wanted to expose the number of bytes downloaded, you could do

public class FileDownloader {

    private final ReadOnlyLongWrapper bytesDownloaded = new ReadOnlyLongWrapper();

    public final long getBytesDownloaded() {
        return bytesDownloaded.get();
    }

    public final ReadOnlyLongProperty bytesDownloadedProperty() {
        return bytesDownloaded.getReadOnlyProperty();
    }

    private long totalBytes ;

    public long getTotalBytes() {
        return totalBytes ;
    }

    // other code as before...

    public boolean downloadSysVersion() {
        // code as before, but periodically call
        bytesDownloaded.set(...);
        // ...
    }

}

Now you do

@Override
protected Void call() throws Exception {

    FileDownloader downloader = new FileDownloader();

    downloader.bytesDownloadedProperty().addListener((obs, oldValue, newValue) -> 
        updateProgress(newValue.longValue(), downloader.getTotalBytes()));

    boolean isDownloaded = downloader.downloadSysVersion();

    // ...
}

If you want to make the FileDownloader class independent of the entire JavaFX API (including the property classes), you can use a LongConsumer instead:

public class FileDownloader {

    private LongConsumer progressUpdate ;

    public void setProgressUpdate(LongConsumer progressUpdate) {
        this.progressUpdate = progressUpdate ;
    }

    private long totalBytes ;

    public long getTotalBytes() {
        return totalBytes ;
    }

    public boolean downloadSysVersion() {
        // periodically do
        if (progressUpdate != null) {
            progressUpdate.accept(...); // pass in number of bytes downloaded
        }
        // ...
    }
}

and in this case your task looks like

@Override
protected Void call() throws Exception {

    FileDownloader downloader = new FileDownloader();

    downloader.setProgressUpdate(bytesDownloaded -> 
        updateProgress(bytesDownloaded, downloader.getTotalBytes()));

    boolean isDownloaded = downloader.downloadSysVersion();

    // ...
}

With either of these setups, you can then just bind the progress bar's progressProperty to the task's progressProperty as usual.

查看更多
趁早两清
3楼-- · 2019-04-13 23:26

In your UpdateTask class, add:

@Override
    public void updateProgress(double workDone, double max) {
        super.updateProgress(workDone, max);
    }

You'll need to pass the UpdateTask instance to your FileDownlader instance instance somehow, probably thru constructor, or however. Then you should be able to call updateProgress.

查看更多
登录 后发表回答