I am writing an application that is using a JavaFX8 TreeTableView. The tree table has three columns, two of which are String properties (name and value) and one which has a Canvas widget in it that draws a picture from from data from a database (waveforms). There is also a control on the application that allows the display (of all of the drawings) to be zoomed out or in (or for that matter scrolled left and right).

The name and value columns use StringProperty values from my data model so there are CellValueFactory set for those columns. The drawing column uses both a CellFactory and CellValueFactory like this:

    // Waveform column
    TreeTableColumn<DrawRow, WaveformTraceBox> waveColumn = new TreeTableColumn<>();
    waveColumn.setCellFactory(new Callback<TreeTableColumn<DrawRow, WaveformTraceBox>, TreeTableCell<DrawRow, WaveformTraceBox>>() {
        @Override
        public TreeTableCell<DrawRow, WaveformTraceBox> call(TreeTableColumn<DrawRow, WaveformTraceBox> param) {
            return new WaveformTraceBoxTreeTableViewCell<>();
        }
    });
    waveColumn.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<DrawRow, WaveformTraceBox>, ObservableValue<WaveformTraceBox>>() {
        @Override
        public ObservableValue<WaveformTraceBox> call(TreeTableColumn.CellDataFeatures<DrawRow, WaveformTraceBox> param) {
            return new ReadOnlyObjectWrapper<>(new WaveformTraceBox());
        }
    });

Where WaveformTraceBoxTreeTableViewCell is:

protected static class WaveformTraceBoxTreeTableViewCell<T> extends TreeTableCell<DrawRow, T> {

    public WaveformTraceBoxTreeTableViewCell() {
        super();
    }

    @Override
    protected void updateItem(T value, boolean empty) {
        super.updateItem(value, empty);
        setText(null);
        setGraphic(null);
        if (!empty && getTreeTableRow().getItem() != null) {
            getTreeTableRow().getItem().setTraceBox((WaveformTraceBox)value);
            setGraphic((WaveformTraceBox) value);
        }
    }

DrawRow is my data model. When the user zooms out or in via the controls on the window the draw row model will notify it's associated Canvas drawing item to re-draw its display. The drawing of the display can take some time to do because of the large amount of data that needs to be processed to generate the display.

Now my problem: As the TreeTableView widget is scrolled it will ask for new Canvas widgets -- which get associated with DrawRow items from the data model. However widgets from the list that get scrolled off the screen will get thrown away by the tree widget.

I have found no way to tell if the item I am working with has been thrown away or is not being used any more. So the code is doing much more work than it needs to because it is trying to draw cells that are no longer being used or shown. Eventually this will cause other problems because of garbage collection I think.

So my real question is how can I tell if a cell has been abandoned by the tree table so I can stop trying to update it? Any help with this would greatly be appreciated. I am not able to find this anywhere on the various web searches I have done.

有帮助吗?

解决方案

Do you need to worry here? What is still "drawing cells that are no longer being used"? Are you running some kind of update in a background thread on the WaveformTraceBox?

In any event, you've structured this pretty strangely.

First, (less important) why is your WaveformTraceBoxTreeTableViewCell generic? Surely you want

protected static class WaveformTraceBoxTreeTableViewCell extends TreeTableCell<DrawRow, WaveformTraceBox>

and then you can replace T with WaveformTraceBox throughout and get rid of the casts, etc.

Second: if I understand this correctly, WaveformTraceBox is a custom Node subclass of some kind; i.e. it's a UI component. The cell value factory shouldn't really return a UI component - it should return the data to display. The cell factory should then use some UI component to display the data.

That way, you can create a single WaveFormTraceBox in the cell implementation, and update the data it displays in the updateItem(...) method.

So something like:

// Waveform column
TreeTableColumn<DrawRow, WaveformData> waveColumn = new TreeTableColumn<>();
waveColumn.setCellFactory(new Callback<TreeTableColumn<DrawRow, WaveformData>, TreeTableCell<DrawRow, WaveformData>>() {
    @Override
    public TreeTableCell<DrawRow, WaveformData> call(TreeTableColumn<DrawRow, WaveformData> param) {
        return new WaveformTraceBoxTreeTableViewCell();
    }
});
waveColumn.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<DrawRow, WaveformData>, ObservableValue<WaveformData>>() {
    @Override
    public ObservableValue<WaveformTraceBox> call(TreeTableColumn.CellDataFeatures<DrawRow, WaveformTraceBox> param) {
        return new ReadOnlyObjectWrapper<>(getDataToDisplayForItem(param.getValue()));
    }
});



protected static class WaveformTraceBoxTreeTableViewCell extends TreeTableCell<DrawRow, WaveFormData> {

    private WaveformTraceBox traceBox = new WaveformTraceBox();

    public WaveformTraceBoxTreeTableViewCell() {
        super();
    }

    @Override
    protected void updateItem(WaveFormData value, boolean empty) {
        super.updateItem(value, empty);
        setText(null);
        setGraphic(null);
        if (!empty && getTreeTableRow().getItem() != null) {
            traceBox.setData(value);
            setGraphic(traceBox);
        } else {
            setGraphic(null);
        }
    }
}

Obviously you need to define the WaveFormData class to encapsulate the data your WaveFormTraceBox will display, and give the WaveFormTraceBox a setData(WaveFormData) method. If you are using any resources that need to be cleaned up, the invocation of setData(...) will indicate that the previous data is no longer being accessed by that WaveformTraceBox.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top