The approach looks reasonable, and this ListCell<URL>
example focuses on the problem you encounter. The variation below combines your SoupService
with a corresponding cell factory. The hyperlink's action handler invokes the service and the updateItem
implementation conditions the hyperlink to display the link text:
private final Hyperlink link = new Hyperlink(); {
link.setOnAction(e ->
service.load(getItem().attr("abs:href")));
}
@Override
protected void updateItem(Element item, boolean empty) {
…
link.setText(item.text());
…
}
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
/**
* @see https://mcmap.net/q/1921234/-displaying-a-listview-of-hyperlinks-using-jsoup-as-a-service/230513
* @see https://github.com/jhy/jsoup/
*/
public class SoupTest extends Application {
private static final String HOME = "https://www.example.com";
@Override
public void start(Stage stage) {
var view = new ListView<Element>();
var url = new TextField(HOME);
url.setPrefColumnCount(url.getPrefColumnCount() * 2);
var service = new SoupService();
service.url.bindBidirectional(url.textProperty());
service.setOnSucceeded(o -> view.setItems(service.getValue()));
var home = new Button("Home");
home.setOnAction(e -> service.load(HOME));
url.setOnAction(e -> service.restart());
view.setCellFactory(c -> {
// https://mcmap.net/q/1869833/-when-click-on-hyperlink-in-javafx-a-relevant-url-should-open-in-browser
ListCell<Element> cell = new ListCell<>() {
private final Hyperlink link = new Hyperlink();
{
link.setOnAction(e
-> service.load(getItem().attr("abs:href")));
}
@Override
protected void updateItem(Element item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
link.setText(item.text());
setGraphic(link);
} else {
setGraphic(null);
}
}
};
return cell;
});
var progress = new ProgressIndicator();
progress.visibleProperty().bind(service.runningProperty());
var stack = new StackPane(view, progress);
var root = new BorderPane();
root.setCenter(stack);
root.setBottom(new ToolBar(url, home));
stage.setTitle("SoupTest");
stage.setScene(new Scene(root));
stage.show();
service.start();
}
private static class SoupService extends Service<ObservableList<Element>> {
private final StringProperty url = new SimpleStringProperty();
@Override
protected Task<ObservableList<Element>> createTask() {
final var site = url.get();
ObservableList<Element> list = FXCollections.observableArrayList();
return new Task<ObservableList<Element>>() {
@Override
protected ObservableList<Element> call() throws Exception {
Document document = Jsoup.connect(site).get();
Elements elements = document.select("a[href]");
for (Element element : elements) {
list.add(element);
}
return list;
}
};
}
private void load(String url) {
this.url.set(url);
this.restart();
}
}
public static void main(String[] args) {
launch(args);
}
}