JavaFX如何在鼠标悬停到列时突出显示TableColumn而不是TableRow

时间:2015-01-05 14:52:12

标签: css javafx

当鼠标位于该列顶部时,我试图找到突出显示表列的解决方案,但我找不到解决方案。有人已经找到了一些解决方案吗?我已经尝试过使用ScenicView查看css和组件,但它没有太大帮助。

2 个答案:

答案 0 :(得分:0)

我想这只适用于许多" hacks"。

TableColumn仅存在于TableView的数据模型中,基于模型呈现的视图部分呈现为行而不是列。因此,每个TableCell都有一个表行作为父表 - 而不是表列。

如果它以相反的方式实现,则表格将以列形式呈现,我们可以轻松实现此目的。


现在,如果您真的想要这样做,则必须执行以下操作:

  1. 编写自定义TableCell并将其注册为所有列的CellRenderer。
  2. 如果您的自定义单元格被(un-)悬停,请检索其列并通知同一列中的所有其他单元格。
  3. 但是:如果表格变大和/或运行应用程序的计算机速度很慢,这会降低您的应用程序速度。

答案 1 :(得分:0)

如果您使用的是Java 8,则可以使用CSS伪类很好地完成此操作。 CSS伪类的性能非常好(至少,它是改变JavaFX控件风格的最快方式),并且由于表视图只创建了少量的表格单元格,即使是非常大的数据集,应该表现得很好。

定义一个外部css类,用于设置应用了自定义伪类的表格单元格的样式,如下所示:

.table-cell:column-hover {
    -fx-background-color: -fx-cell-focus-inner-border, -fx-selection-bar  ;
    -fx-background-insets: 0, 0 0 1 0 ;
    -fx-background: -fx-selection-bar ;
}

(此处-fx-background-color实际上会更改背景颜色:-fx-background只是确保文本填充相对于背景保持合适颜色的技巧。)

现在,对于每一列,定义一个布尔属性。为每个列创建一个单元工厂,当鼠标移过单元格时将boolean属性设置为true,并在鼠标移开时将其设置为false。让每个单元格观察boolean属性,并在单元格发生变化时设置伪类状态。

以下是一个示例,使用standard tutorial中的常用Person表。有趣的代码在createCol(...)方法中:

import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TableViewTest extends Application {

    private static final PseudoClass COLUMN_HOVER_PSEUDO_CLASS = PseudoClass.getPseudoClass("column-hover");

    @Override
    public void start(Stage primaryStage) {
        TableView<Person> table = new TableView<>();
        TableColumn<Person, String> firstNameCol = createCol("First Name", Person::firstNameProperty, 150);
        TableColumn<Person, String> lastNameCol = createCol("Last Name", Person::lastNameProperty, 150);
        TableColumn<Person, String> emailCol = createCol("Email", Person::emailProperty, 200);

        table.getItems().addAll(
            new Person("Jacob", "Smith", "jacob.smith@example.com"),
            new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
            new Person("Ethan", "Williams", "ethan.williams@example.com"),
            new Person("Emma", "Jones", "emma.jones@example.com"),
            new Person("Michael", "Brown", "michael.brown@example.com")
        );

        table.getColumns().add(firstNameCol);
        table.getColumns().add(lastNameCol);
        table.getColumns().add(emailCol);

        VBox root = new VBox(15, table);
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root, 800, 600);

        scene.getStylesheets().add("table-column-hover.css");

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


    private TableColumn<Person, String> createCol(String title, 
            Function<Person, ObservableValue<String>> mapper, double size) {

        TableColumn<Person, String> col = new TableColumn<>(title);

        col.setCellValueFactory(cellData -> mapper.apply(cellData.getValue()));

        // Is the column being hovered over with the mouse?
        BooleanProperty columnHover = new SimpleBooleanProperty();

        col.setCellFactory(column -> {

            // basic table cell:
            TableCell<Person, String> cell = new TableCell<Person, String>() ;
            cell.textProperty().bind(cell.itemProperty());

            // when the mouse hovers over the cell, set the columnHover to indicate 
            // the mouse is over the column:
            cell.hoverProperty().addListener((obs, wasHovered, isNowHovered) -> {
                columnHover.set(isNowHovered);
            });

            // update the column-hover pseudoclass state for this cell when the column is hovered over
            // note this will activate the pseudoclass when the mouse is over any cell in this column
            columnHover.addListener((obs, columnWasHovered, columnIsNowHovered) -> 
                cell.pseudoClassStateChanged(COLUMN_HOVER_PSEUDO_CLASS, columnIsNowHovered)
            );

            return cell ;
        });

        col.setPrefWidth(size);

        return col ;
    }

    public class Person {
        private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
        private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
        private final StringProperty email = new SimpleStringProperty(this, "email");

        public Person(String firstName, String lastName, String email) {
            this.firstName.set(firstName);
            this.lastName.set(lastName);
            this.email.set(email);
        }

        public final StringProperty firstNameProperty() {
            return this.firstName;
        }

        public final java.lang.String getFirstName() {
            return this.firstNameProperty().get();
        }

        public final void setFirstName(final java.lang.String firstName) {
            this.firstNameProperty().set(firstName);
        }

        public final StringProperty lastNameProperty() {
            return this.lastName;
        }

        public final java.lang.String getLastName() {
            return this.lastNameProperty().get();
        }

        public final void setLastName(final java.lang.String lastName) {
            this.lastNameProperty().set(lastName);
        }

        public final StringProperty emailProperty() {
            return this.email;
        }

        public final java.lang.String getEmail() {
            return this.emailProperty().get();
        }

        public final void setEmail(final java.lang.String email) {
            this.emailProperty().set(email);
        }

    }

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