Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
113 views
in Technique[技术] by (71.8m points)

java - Remove TableView entries when status change

I am having a problem trying to figure out how to make a TableView show the correct data based on each entry response status. I thought FilteredList would get the job done but it's not. Basically, I am checking URLs and getting their status codes. I am using a FilteredList to show all URLs that are pending, that was successful, etc. If I change the ChoiceBox from All to Pending, the FilteredList does show only pending URLs, but as the URLs change to Success or something else the FilteredList does not filter them out the current view. What should happen is when I change to Pending, any URLs that receives a status change should drop from the current view. How do I get the FilteredList/TableView to do real-time updates?

Main

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication240 extends Application
{

    @Override
    public void start(Stage stage) throws Exception
    {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}

Controller

import com.sun.javafx.application.HostServicesDelegate;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;

/**
 *
 * @author blj0011
 */
public class FXMLDocumentController implements Initializable
{

    @FXML
    private Button btnProcess;
    @FXML
    private TableView<Model> tvMain;
    @FXML
    private TableColumn<Model, String> tcBibId, tcHoldingId, tcUrl, tcStatus;
    @FXML
    private TableColumn<Model, Integer> tcId;
    @FXML
    private ChoiceBox<String> cbMain;

    private final ObservableList<Model> masterData = FXCollections.observableArrayList();
    FilteredList<Model> filteredData;
    HostServicesDelegate hostServicesDelegate;
    AtomicInteger processUrlCounter;
    int tableViewSize = -1;
    AtomicInteger seconds = new AtomicInteger();

    @Override
    public void initialize(URL url, ResourceBundle rb)
    {
        // TODO
        processUrlCounter = new AtomicInteger();
        setupChoiceBox();
        setupTableView();
        btnProcess.setOnAction((event) -> {
            btnProcess.setDisable(true);
            List<Task<String>> tasks = new ArrayList();
            tvMain.getItems().forEach((t) -> {
                Model tempModel = (Model) t;
                tasks.add(processUrl(tempModel));
            });

            tasks.forEach(exec::execute);
        });
    }

    private List<Model> getTestData()
    {
        List<Model> testList = new ArrayList();
        Random random = new Random();

        try (Scanner input = new Scanner(new File("testdata.txt"))) {
            while (input.hasNext()) {
                Model tempModel = new Model(Integer.toString(random.nextInt(100000) + 100000), Integer.toString(random.nextInt(100000) + 100000), input.next());
                testList.add(tempModel);
                System.out.println(tempModel.toString());
            }
        }
        catch (FileNotFoundException ex) {
            Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
        }

        return testList;
    }

    private void setupChoiceBox()
    {
        List<String> STATUS_LIST = Arrays.asList("ALL", "PENDING", "SUCCESS");

        cbMain.getItems().addAll(STATUS_LIST);
        cbMain.getSelectionModel().selectFirst();
        cbMain.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal)
                -> {
            List<Model> tempList = new ArrayList();
            switch (newVal) {
                case "ALL":
                    tempList = tvMain.getItems();
                    filteredData.setPredicate(null);
                    break;
                case "PENDING":
                    filteredData.setPredicate((t) -> {
                        return t.getStatus().trim().equals("PENDING");
                    });
                    tempList = tvMain.getItems().filtered((t) -> {
                        return t.getStatus().equals("PENDING");
                    });
                    break;
                case "SUCCESS":
                    filteredData.setPredicate((t) -> {
                        return t.getStatus().trim().matches("2\d\d");
                    });
                    tempList = tvMain.getItems().filtered((t) -> {
                        return t.getStatus().matches("2\d\d");
                    });
                    break;
            }
        });
    }

    private void setupTableView()
    {
        tcId.setCellValueFactory(new PropertyValueFactory("id"));
        tcBibId.setCellValueFactory(cell -> cell.getValue().bibIdProperty());
        tcHoldingId.setCellValueFactory(cell -> cell.getValue().holdingIdProperty());
        tcUrl.setCellValueFactory(cell -> cell.getValue().urlProperty());
        tcStatus.setCellValueFactory(cell -> cell.getValue().statusProperty());

        masterData.setAll(getTestData());
        filteredData = new FilteredList(masterData, null);
        tvMain.setItems(filteredData);
        tableViewSize = masterData.size();

    }

    private final ExecutorService exec = Executors.newFixedThreadPool(2500, r -> {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    });

    private Task<String> processUrl(Model model)
    {
        Task<String> task = new Task<String>()
        {
            @Override
            protected String call() throws Exception
            {
                CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()).setRedirectStrategy(new LaxRedirectStrategy()).build();

                HttpGet httpGet = new HttpGet(model.getUrl());
                try (CloseableHttpResponse response1 = httpclient.execute(httpGet)) {
                    String tempResponse = response1.getStatusLine().toString().split(" ")[1];

                    return tempResponse;
                }
                catch (IOException ex) {
                    String tempString = ex.getClass().toString().split(" ")[1];
                    String[] tempException = tempString.split("\.");

                    return tempException[tempException.length - 1];
                }
            }
        };

        task.setOnSucceeded((event) -> {
            model.setStatus(task.getValue());
            if (processUrlCounter.incrementAndGet() == tableViewSize) {
                btnProcess.setDisable(false);
            }
            String tempOutput = "Processed URLs: " + processUrlCounter.get() + " of " + tableViewSize;
        });

        return task;
    }
}

FXML

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>

<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="862.0" prefWidth="748.0" spacing="10.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication240.FXMLDocumentController">
    <children>
        <ChoiceBox fx:id="cbMain" prefWidth="150.0" />
        <TableView fx:id="tvMain" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
            <columns>
                <TableColumn fx:id="tcId" prefWidth="85.0" text="ID" />
                <TableColumn fx:id="tcBibId" prefWidth="102.0" text="Bib ID" />
                <TableColumn fx:id="tcHoldingId" prefWidth="113.0" text="Holding ID" />
                <TableColumn fx:id="tcUrl" prefWidth="313.0" text="URL" />
                <TableColumn fx:id="tcStatus" prefWidth="132.0" text="Status" />
            </columns>
        </TableView>
        <Button fx:id="btnProcess" mnemonicParsing="false" text="Process" />
    </children>
    <padding>
        <Insets bottom="10.0" top="10.0" />
    </padding>
</VBox>

Model Class

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 *
 * @author Sedrick
 */
public class Model
{

    final private IntegerProperty id = new SimpleIntegerProperty();
    final private StringProperty bibId = new SimpleStringProperty();
    final private StringProperty holdingId = new SimpleStringProperty();
    final private StringProperty url = new SimpleStringProperty();
    final private StringProperty status = new SimpleStringProperty();

    public Model(int id, String bibId, String holdingID, String url)
    {
        this.id.set(id);
        this.bibId.set(bibId);
        this.holdingId.set(holdingID);
        this.url.set(url);
        this.status.set("PENDING");
    }

    public Model(String bibId, String holdingId, String url)
    {
        this.id.set(-1);
        this.bibId.set(bibId);
        this.holdingId.set(holdingId);
        this.url.set(url);
        this.status.set("PENDING");
    }

    public IntegerProperty idProperty()
    {
        return this.id;
    }

    public StringProperty bibIdProperty()
    {
        return this.bibId;
    }

    public StringProperty holdingIdProperty()
    {
        return this.holdingId;
    }

    public StringProperty urlProperty()
    {
        return this.url;
    }

    public StringProperty statusProperty()
    {
        return this.status;
    }

    public void se

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

A FilteredList will update whenever its Predicate changes or whenever it detects a change in the source ObservableList. The type of event you want to fire is an update event. This event signifies one or more elements have been updated (e.g. when a property changes). In order to do this you have to construct the ObservableList with the appropriate factory method: FXCollections.observableArrayList(Callback).

This factory method takes a Callback that accepts an element of the ObservableList and returns an Observable[]. The Observables in the array will be listened to for invalidation events and, when detected, will cause the ObservableList to fire an update change.

From looking at your code it seems1 like the Model class has a status property. If you want to fire updates when the status changes you should use:

ObservableList<Model> masterData = FXCollections.observableArrayList(model ->
        new Observable[]{model.statusProperty()});

You can add more Observables to the array if you wish for updates to be fired for more than just changes to the status property.

Now when the status property changes the FilteredList will notice and filter the element(s) if needed.


1. You hadn't posted the Model class when I wrote this answer. However, I manage to "reverse engineer" it from the available code and tested it using the Callback extractor. The elements were removed from the FilteredList, and thus the TableView, when the status changed from PENDING to whatever the new status ended up being.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...