Java 在具有合并列的TableView中进行选择

Java 在具有合并列的TableView中进行选择,java,javafx,javafx-8,Java,Javafx,Javafx 8,在下面的简化代码中,我硬编码了第一行以跨越TableView的所有三列 除了选择之外,它似乎工作得很好。如果我选择了第一行并按向右键,选择的内容将转到宽度为零的单元格,因此似乎消失了。同样,如果选中第二行的中间单元格并按向上键,则选择将转到同一隐藏单元格 有没有办法告诉选择模型某些单元格应该被忽略?还是我更可能从零开始编写选择模型 import com.sun.javafx.scene.control.skin.TableRowSkin; import javafx.application.Ap

在下面的简化代码中,我硬编码了第一行以跨越TableView的所有三列

除了选择之外,它似乎工作得很好。如果我选择了第一行并按向右键,选择的内容将转到宽度为零的单元格,因此似乎消失了。同样,如果选中第二行的中间单元格并按向上键,则选择将转到同一隐藏单元格

有没有办法告诉选择模型某些单元格应该被忽略?还是我更可能从零开始编写选择模型

import com.sun.javafx.scene.control.skin.TableRowSkin;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TableSkinTest extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        BorderPane pane = new BorderPane();
        Scene scene = new Scene(pane, 500, 500);
        stage.setScene(scene);

        ObservableList<Person> data = FXCollections.observableArrayList(
                new Person("This should span columns because it is long", "", ""),
                new Person("Isabella", "Johnson", "079155882"),
                new Person("Ethan", "Williams", "079155883"),
                new Person("Emma", "Jones", "079155884"),
                new Person("Michael", "Brown", "079155885")
        );

        TableView<Person> table = new TableView<>();

        table.getSelectionModel().setCellSelectionEnabled(true);
        table.setRowFactory(param -> new TableRow<Person>() {
            @Override
            protected Skin<?> createDefaultSkin() {
                return new TableRowSkin<TableSkinTest.Person>(this) {
                    @Override
                    protected void layoutChildren(double x, double y, double w, double h) {
                        checkState();
                        if (cellsMap.isEmpty()) return;
                        ObservableList<? extends TableColumnBase> visibleLeafColumns = getVisibleLeafColumns();
                        if (visibleLeafColumns.isEmpty()) {
                            super.layoutChildren(x, y, w, h);
                            return;
                        }
                        TableRow<TableSkinTest.Person> control = getSkinnable();
                        // layout the individual column cells
                        double width;
                        double height;

                        final double verticalPadding = snappedTopInset() + snappedBottomInset();
                        final double horizontalPadding = snappedLeftInset() + snappedRightInset();
                        final double controlHeight = control.getHeight();

                        int index = control.getIndex();

                        for (int column = 0, max = cells.size(); column < max; column++) {
                            TableCell<TableSkinTest.Person, ?> tableCell = cells.get(column);
                            width = snapSize(tableCell.prefWidth(-1)) - snapSize(horizontalPadding);
                            height = Math.max(controlHeight, tableCell.prefHeight(-1));
                            height = snapSize(height) - snapSize(verticalPadding);
                            if (index == 0 && column > 0) {
                                width = 0; // Hard code for simplification
                            } else if (index == 0) {
                                double width1 = snapSize(cells.get(1).getTableColumn().getWidth());
                                double width2 = snapSize(cells.get(2).getTableColumn().getWidth());
                                width += width1;
                                width += width2;
                            }
                            tableCell.resize(width, height);
                            tableCell.relocate(x, snappedTopInset());
                            x += width;
                        }
                    }
                };
            }
        });

        TableColumn<Person,String> firstNameCol = new TableColumn<>("First Name");
        TableColumn<Person,String> lastNameCol = new TableColumn<>("Last Name");
        TableColumn<Person,String> numberCol = new TableColumn<>("Number");
        firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
        lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));
        numberCol.setCellValueFactory(new PropertyValueFactory<>("number"));

        table.getColumns().addAll(firstNameCol, lastNameCol, numberCol);
        table.getColumns().forEach(col -> col.setPrefWidth(100));

        table.setItems(data);

        pane.setCenter(table);

        stage.show();
    }

    public static class Person {
        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
        private final SimpleStringProperty number;
        private Person(String fName, String lName, String nNumber) {
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.number = new SimpleStringProperty(nNumber);
        }
        public String getFirstName() {return firstName.get();}
        public void setFirstName(String fName) {firstName.set(fName);}
        public String getLastName() {return lastName.get();}
        public void setLastName(String fName) {lastName.set(fName);}
        public String getNumber() {return number.get();}
        public void setNumber(String number) {this.number.set(number);}
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}
import com.sun.javafx.scene.control.skin.TableRowSkin;
导入javafx.application.application;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.scene.scene;
导入javafx.scene.control.*;
导入javafx.scene.control.cell.PropertyValueFactory;
导入javafx.scene.layout.BorderPane;
导入javafx.stage.stage;
公共类测试扩展了应用程序{
@凌驾
public void start(Stage)引发异常{
BorderPane=新的BorderPane();
场景=新场景(窗格,500500);
舞台场景;
ObservableList data=FXCollections.observableArrayList(
new Person(“这应该跨列,因为它很长”、“”、“”),
新人(“伊莎贝拉”、“约翰逊”、“079155882”),
新人(“伊桑”、“威廉姆斯”、“079155883”),
新人(“艾玛”、“琼斯”、“079155884”),
新人(“迈克尔”、“布朗”、“079155885”)
);
TableView table=新TableView();
table.getSelectionModel().setCellSelectionEnabled(true);
table.setRowFactory(参数->新表行(){
@凌驾
受保护的皮肤createDefaultSkin(){
返回新的TableRowSkin(此){
@凌驾
受保护的无效布局子项(双x、双y、双w、双h){
checkState();
if(cellsMap.isEmpty())返回;
ObservableList tableCell=cells.get(列);
宽度=snapSize(tableCell.prefWidth(-1))-snapSize(水平填充);
高度=数学最大值(controlHeight,tableCell.prefHeight(-1));
高度=snapSize(高度)-snapSize(垂直填充);
如果(索引==0&&column>0){
宽度=0;//用于简化的硬代码
}else if(索引==0){
double width1=snapSize(cells.get(1.getTableColumn().getWidth());
double width2=snapSize(cells.get(2.getTableColumn().getWidth());
宽度+=宽度1;
宽度+=宽度2;
}
tableCell.resize(宽度、高度);
tableCell.relocate(x,snapdtopinset());
x+=宽度;
}
}
};
}
});
TableColumn firstNameCol=新的TableColumn(“名字”);
TableColumn lastNameCol=新的TableColumn(“姓氏”);
TableColumn NumberColl=新的TableColumn(“编号”);
firstNameCol.setCellValueFactory(新属性ValueFactory(“firstName”));
lastNameCol.setCellValueFactory(新属性ValueFactory(“lastName”));
numberCol.setCellValueFactory(新属性ValueFactory(“编号”);
table.getColumns().addAll(firstNameCol、lastNameCol、numberCol);
table.getColumns().forEach(col->col.setPrefWidth(100));
表2.设置项目(数据);
窗格。设置中心(表);
stage.show();
}
公共静态类人员{
私有最终SimpleStringProperty名字;
私有最终SimpleStringProperty姓氏;
私人最终SimpleStringProperty编号;
私人(字符串fName、字符串lName、字符串NNNumber){
this.firstName=新的SimpleStringProperty(fName);
this.lastName=新的SimpleStringProperty(lName);
this.number=新的SimpleStringProperty(nnNumber);
}
公共字符串getFirstName(){return firstName.get();}
public void setFirstName(字符串fName){firstName.set(fName);}
公共字符串getLastName(){返回lastName.get();}
public void setLastName(字符串fName){lastName.set(fName);}
公共字符串getNumber(){return number.get();}
public void setNumber(字符串编号){this.number.set(number);}
}
公共静态void main(字符串[]args){
应用程序启动(args);
}
}
有没有办法告诉选择模型某些单元格应该被忽略

不,selectionModels没有取消/选择的概念

还是我更可能从零开始编写选择模型

import com.sun.javafx.scene.control.skin.TableRowSkin;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TableSkinTest extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        BorderPane pane = new BorderPane();
        Scene scene = new Scene(pane, 500, 500);
        stage.setScene(scene);

        ObservableList<Person> data = FXCollections.observableArrayList(
                new Person("This should span columns because it is long", "", ""),
                new Person("Isabella", "Johnson", "079155882"),
                new Person("Ethan", "Williams", "079155883"),
                new Person("Emma", "Jones", "079155884"),
                new Person("Michael", "Brown", "079155885")
        );

        TableView<Person> table = new TableView<>();

        table.getSelectionModel().setCellSelectionEnabled(true);
        table.setRowFactory(param -> new TableRow<Person>() {
            @Override
            protected Skin<?> createDefaultSkin() {
                return new TableRowSkin<TableSkinTest.Person>(this) {
                    @Override
                    protected void layoutChildren(double x, double y, double w, double h) {
                        checkState();
                        if (cellsMap.isEmpty()) return;
                        ObservableList<? extends TableColumnBase> visibleLeafColumns = getVisibleLeafColumns();
                        if (visibleLeafColumns.isEmpty()) {
                            super.layoutChildren(x, y, w, h);
                            return;
                        }
                        TableRow<TableSkinTest.Person> control = getSkinnable();
                        // layout the individual column cells
                        double width;
                        double height;

                        final double verticalPadding = snappedTopInset() + snappedBottomInset();
                        final double horizontalPadding = snappedLeftInset() + snappedRightInset();
                        final double controlHeight = control.getHeight();

                        int index = control.getIndex();

                        for (int column = 0, max = cells.size(); column < max; column++) {
                            TableCell<TableSkinTest.Person, ?> tableCell = cells.get(column);
                            width = snapSize(tableCell.prefWidth(-1)) - snapSize(horizontalPadding);
                            height = Math.max(controlHeight, tableCell.prefHeight(-1));
                            height = snapSize(height) - snapSize(verticalPadding);
                            if (index == 0 && column > 0) {
                                width = 0; // Hard code for simplification
                            } else if (index == 0) {
                                double width1 = snapSize(cells.get(1).getTableColumn().getWidth());
                                double width2 = snapSize(cells.get(2).getTableColumn().getWidth());
                                width += width1;
                                width += width2;
                            }
                            tableCell.resize(width, height);
                            tableCell.relocate(x, snappedTopInset());
                            x += width;
                        }
                    }
                };
            }
        });

        TableColumn<Person,String> firstNameCol = new TableColumn<>("First Name");
        TableColumn<Person,String> lastNameCol = new TableColumn<>("Last Name");
        TableColumn<Person,String> numberCol = new TableColumn<>("Number");
        firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
        lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));
        numberCol.setCellValueFactory(new PropertyValueFactory<>("number"));

        table.getColumns().addAll(firstNameCol, lastNameCol, numberCol);
        table.getColumns().forEach(col -> col.setPrefWidth(100));

        table.setItems(data);

        pane.setCenter(table);

        stage.show();
    }

    public static class Person {
        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
        private final SimpleStringProperty number;
        private Person(String fName, String lName, String nNumber) {
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.number = new SimpleStringProperty(nNumber);
        }
        public String getFirstName() {return firstName.get();}
        public void setFirstName(String fName) {firstName.set(fName);}
        public String getLastName() {return lastName.get();}
        public void setLastName(String fName) {lastName.set(fName);}
        public String getNumber() {return number.get();}
        public void setNumber(String number) {this.number.set(number);}
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}
严格来说,定制的TableViewSelectionModel不能从头开始实现——它扩展了包私有的MultipleSelectionModelBase(就像TableViewArrayListSelectionModel一样,TableView中的一个静态类)。最接近“划痕”的是基本上c&p TableViewArrayListSelectionModel,并根据需要实现忽略单元格。需要对隐藏的超级方法/字段进行肮脏、反射性访问

在我看来,没有很好的解决方案可以满足您的需求(除了从头开始编写TableView)。如果选择了任何一个跨区单元,则可能存在一种脏(与您的皮肤在宽度为0的跨区单元上的布局类似)情况,即保持跨区单元处于选中状态。仍然有一个轻微的可用性问题,在左/右上没有明显的变化,但至少选择没有消失

简本