简体   繁体   English

Javafx Tableview在当前视图中保留选定的行

[英]Javafx Tableview Keep selected row in current view

I am using javafx tableview with active sorting and insertion of a new row every millisecond... 我正在使用javafx tableview进行主动排序并每毫秒插入一个新行...

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. 如果我选择了一行,那么当插入新行时,它应该保持可见(不应该从我表的当前可见部分向上或向下)。

This may be the long away around it and it's kind of hackish, but it worked for me when I needed to do something similar. 这可能是它周围的很长时间,它有点hackish,但当我需要做类似的事情时,它对我有用。

The gist of the answer is that you need to get access to the VirtualFlow member of the TableViewSkin . 答案的要点是您需要访问TableViewSkinVirtualFlow成员。 That's not as simple as is sounds because the skin isn't loaded until the CSS is parsed. 这并不像声音那么简单,因为在解析CSS之前不会加载外观。 I added a Listener to the skinProperty of the TableView and was able to get the VirtualFlow that way. 我在TableViewskinProperty中添加了一个Listener ,并且能够以这种方式获得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);
   }
});

After you have the VirtualFlow , you can listen to the table's ObservableList for changes and check to make sure the selected item is still in the viewport. 拥有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);
             }
          }
       }
   }
});

There will still be a bit of bookkeeping to manage. 仍然会有一些簿记要管理。 For instance, if you wanted to place focus back on the table. 例如,如果您想将焦点放回桌面上。 Also, it's worth noting that the VirtualFlow isn't strictly bound by the visible rectangle of the table, so an item may be considered visible even if it is just outside the viewport. 此外,值得注意的是, VirtualFlow并未严格受到表格可见矩形的约束,因此即使项目位于视口之外,也可视为可见项目。 You can study the VirtualFlow for more details. 您可以学习VirtualFlow以获取更多详细信息。

Here's a SSCCE. 这是一个SSCCE。

JavaFXApplication21.java: 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);
    }
}

Sample.fxml: Sample.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: 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; }
    }
}

I also had the same problem .You can use cellEventHandler class to which implemets EventHandler interface to fired when a cell is selcted in table and set last selected row index to a variable. 我也有同样的问题。你可以使用cellEventHandler类来实现EventHandler接口,当在表中选择一个单元格并将最后选择的行索引设置为变量时触发它。 The class is as followed. 课程如下。

    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();
    }
}`

Then your table class render of the table in PanelController Class should be as followed. 那么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;
        }
    });

The "selectedItem" field should be declared in the panelController class as static field and then that "selectedItem " should set as selected in the table when you refresh the table row data. 应该在panelController类中将“selectedItem”字段声明为静态字段,然后在刷新表行数据时将“selectedItem”设置为在表中选择。

 myTable.getSelectionModel().select(selectedItem);    

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM