7

I am using javafx tableview with active sorting and insertion of a new row every millisecond...

I want this functionality:

If I have selected a row then it should remain visible (that is should not go up or down from current visible portion of my table) when new rows are inserted.

4

3 回答 3

7

这可能是很遥远的事情,而且有点骇人听闻,但是当我需要做类似的事情时,它对我有用。

答案的要点是您需要访问VirtualFlow. TableViewSkin这不像听起来那么简单,因为在解析 CSS 之前不会加载皮肤。我在 of the 中添加了一个ListenerskinProperty并且TableView能够以VirtualFlow这种方式获得。

tableView.skinProperty().addListener(new ChangeListener<Skin>()
{
   @Override
   public void changed(ObservableValue<? extends Skin> ov, Skin t, Skin t1)
   {
      if (t1 == null) { return; }

      TableViewSkin tvs = (TableViewSkin)t1;
      ObservableList<Node> kids = tvs.getChildrenUnmodifiable();

      if (kids == null || kids.isEmpty()) { return; }
      flow = (VirtualFlow)kids.get(1);
   }
});

拥有 后VirtualFlow,您可以收听表格的ObservableList更改并检查以确保所选项目仍在视口中。

countries.addListener(new ListChangeListener<Country>()
{
   @Override
   public void onChanged(ListChangeListener.Change<? extends Country> change)
   {
      while (change.next())
      {
          if (change.wasAdded())
          {
             if (flow == null) { return; }
             int first = flow.getFirstVisibleCell().getIndex();
             int last = flow.getLastVisibleCell().getIndex();
             int selected = tableView.getSelectionModel().getSelectedIndex();

             if (selected < first || selected > last)
             {
                flow.show(selected);
             }
          }
       }
   }
});

仍然需要管理一些簿记。例如,如果您想将焦点重新放在桌子上。此外,值得注意的VirtualFlow是,它并不受表格的可见矩形的严格限制,因此即使项目就在视口之外,它也可能被认为是可见的。您可以研究VirtualFlow以了解更多详细信息。

这是一个SSCCE。

JavaFXApplication21.java:

package javafxapplication21;

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

public class JavaFXApplication21 extends Application
{
    @Override
    public void start(Stage stage) throws Exception
    {
        Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));

        Scene scene = new Scene(root);

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

    public static void main(String[] args)
    {
        launch(args);
    }
}

示例.fxml:

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="200.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml" fx:controller="javafxapplication21.SampleController">
  <children>
    <ToolBar AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
      <items>
        <Button fx:id="insertBtn" mnemonicParsing="false" text="Insert" />
      </items>
    </ToolBar>
    <TableView fx:id="tableView" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="31.0">
      <columns>
        <TableColumn prefWidth="100.0" text="Country" fx:id="countryColumn" />
        <TableColumn prefWidth="100.0" text="Capital" fx:id="capitalColumn" />
      </columns>
    </TableView>
  </children>
</AnchorPane>

SampleController.java:

package javafxapplication21;

import com.sun.javafx.scene.control.skin.TableViewSkin;
import com.sun.javafx.scene.control.skin.VirtualFlow;
import java.net.URL;
import java.util.*;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;

public class SampleController implements Initializable
{
    @FXML private Button insertBtn;
    @FXML private TableView<Country> tableView;
    @FXML private TableColumn<Country, String> countryColumn;
    @FXML private TableColumn<Country, String> capitalColumn;

    private VirtualFlow flow;

    private ObservableList<Country> countries =
            FXCollections.observableArrayList();
    private List<Country> insertList = new ArrayList<>();

    public SampleController()
    {
        countries.addAll(new Country("AG", "Buenos Aires"),
                new Country("AU", "Vienna"),
                new Country("BY", "Minsk"),
                new Country("CO", "Bogota"),
                new Country("EG", "Cairo"));

        insertList.add(new Country("ZI", "Harare"));
        insertList.add(new Country("UK", "London"));
        insertList.add(new Country("TW", "Taipei"));
    }

    @Override
    public void initialize(URL url, ResourceBundle rb)
    {
        countryColumn.setCellValueFactory(
                new PropertyValueFactory<Country, String>("name"));
        capitalColumn.setCellValueFactory(
                new PropertyValueFactory<Country, String>("capital"));

        tableView.setItems(countries);

        tableView.skinProperty().addListener(new ChangeListener<Skin>()
        {
            @Override
            public void changed(ObservableValue<? extends Skin> ov,
                Skin t, Skin t1)
            {
                if (t1 == null) { return; }

                TableViewSkin tvs = (TableViewSkin)t1;
                ObservableList<Node> kids = tvs.getChildrenUnmodifiable();

                if (kids == null || kids.isEmpty()) { return; }

                flow = (VirtualFlow)kids.get(1);
            }
        });

        insertBtn.setOnAction(new EventHandler<ActionEvent>()
        {
            @Override
            public void handle(ActionEvent t)
            {
                if (!insertList.isEmpty())
                {
                    countries.add(2, insertList.get(0));
                    insertList.remove(0);
                }
            }
        });

        countries.addListener(new ListChangeListener<Country>()
        {
            @Override
            public void onChanged(ListChangeListener.Change<? extends Country> change)
            {
                while (change.next())
                {
                    if (change.wasAdded())
                    {
                        if (flow == null) { return; }
                        int first = flow.getFirstVisibleCell().getIndex();
                        int last = flow.getLastVisibleCell().getIndex();
                        int selected = tableView.getSelectionModel().getSelectedIndex();

                        if (selected < first || selected > last)
                        {
                            flow.show(selected);
                        }
                    }
                }
            }
        });
    }

    public class Country
    {
        private SimpleStringProperty name;
        private SimpleStringProperty capital;

        public Country(String name, String capital)
        {
            this.name = new SimpleStringProperty(name);
            this.capital = new SimpleStringProperty(capital);
        }

        public SimpleStringProperty nameProperty() { return name; }
        public SimpleStringProperty capitalProperty() { return capital; }
    }
}
于 2013-06-25T17:54:43.680 回答
1

我也有同样的问题。您可以使用 cellEventHandler 类来实现 EventHandler 接口,以便在表中选择单元格并将最后选择的行索引设置为变量时触发。课程如下。

    public class CellEventHandler implements EventHandler<MouseEvent>

{
     CellEventHandler()
    {
    }

    @Override
    public void handle(MouseEvent t)
    {
        TableCell c;
        c = (TableCell) t.getSource();
        c.getIndex();
        PanelController.selectedItem = c.getIndex();
    }
}`

那么您在 PanelController 类中的表的表类渲染应该如下所示。

typeCol.setCellFactory(new Callback<TableColumn<tableModel, String>, TableCell<tableModel, String>>() 
    {
        @Override
        public TableCell<tableModel, String> call(TableColumn<tableModel, String> tableColumn)
        {
            LableCell lableCell = new LableCell(Pos.CENTER, true);
            lableCell.addEventFilter(MouseEvent.MOUSE_CLICKED, new CellEventHandler());
            return lableCell;
        }
    });

“selectedItem”字段应在 panelController 类中声明为静态字段,然后当您刷新表格行数据时,“selectedItem”应设置为在表格中选择。

 myTable.getSelectionModel().select(selectedItem);    
于 2013-11-11T08:39:21.330 回答
0

这对我有用:

tableView.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, t1) -> {

        TableViewSkin tvs = (TableViewSkin)tableView.getSkin();
        ObservableList<Node> kids = tvs.getChildren();

        if (kids == null || kids.isEmpty()) { return; }
        VirtualFlow flow = (VirtualFlow)kids.get(1);
        int first =-1;
        if (flow.getFirstVisibleCell() != null) first = flow.getFirstVisibleCell().getIndex();
        int last = -1;
        if (flow.getFirstVisibleCell() != null) last = flow.getLastVisibleCell().getIndex();

        if(first !=-1 && t1.intValue()<first)
            flow.scrollTo(t1.intValue());
        if(last != -1 && t1.intValue()>last)
            flow.scrollTo(t1.intValue()+1);

    });
于 2021-08-06T03:55:53.997 回答