【问题标题】:Highlight TableView rows in JavaFX when Time cell is past current time当时间单元格超过当前时间时,突出显示 JavaFX 中的 TableView 行
【发布时间】:2016-08-27 16:48:13
【问题描述】:

过去几个小时我一直在网上搜索并尝试实现此功能,但没有成功。

我有一个包含各种列的 TableView,其中之一是一个保存时间的字符串字段。我想突出显示所有 timeCol 值晚于当前时间的行。

必须每 5 分钟检查一次,并相应地突出显示表格。 我假设这需要在后台线程上运行,但我对并发缺乏经验。

表格视图内容

@FXML
private TableView<BookingImpl> bookingTableView;
@FXML
private TableColumn<BookingImpl, String> timeCol;
@FXML
private TableColumn<BookingImpl, String> nameCol;
@FXML
private TableColumn<BookingImpl, String> pickUpCol;
@FXML
private TableColumn<BookingImpl, String> dropOffCol;
@FXML
private TableColumn<BookingImpl, String> commentCol;
@FXML
private TableColumn<BookingImpl, String> priceCol;

我看过JavaFX tableview colors这个帖子,看起来是我想要实现的,高光颜色需要是淡紫色的。

任何指导将不胜感激。

更新 - James_D 解决方案

我已对此进行了调整以适合我的应用程序,但它仅突出显示输入时的延迟预订,即如果我从现在开始 1 分钟输入预订并等待它不会突出显示。 我已禁用列的 FXML 实现,但更愿意使用它。

我正在从我的主控制器类中引用这个新类,如下所示;

TableRowController rowController = new TableRowController();
rowController.start(bookingTableView);

TableRowController

封装控制器;

import Model.BookingImpl;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.css.PseudoClass;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;

import java.time.LocalTime;
import java.util.function.Function;

public class TableRowController{

private final PseudoClass future = PseudoClass.getPseudoClass("future");

public void start(TableView table){
    ObjectProperty<LocalTime> now = new SimpleObjectProperty<>(LocalTime.now());
    table.setRowFactory(tv -> {
        TableRow<BookingImpl> row = new TableRow<>();
        ChangeListener<LocalTime> listener = (obs, oldTime, newTime) -> updateRow(row, now.get());
        now.addListener(listener);
        row.itemProperty().addListener((obs, oldItem, newItem) -> {
            if (oldItem != null) {
                oldItem.getTimeProperty().removeListener(listener);
            }
            if (newItem != null) {
                newItem.getTimeProperty().addListener(listener);
            }
            updateRow(row, now.get());
        });
        return row ;
    });

    configureTable(table);
}

public void updateRow(TableRow<BookingImpl> row, LocalTime now) {
    boolean isFuture = false ;
    if (row.getItem() != null) {
        isFuture = row.getItem().getTime().isBefore(now);
    }
    row.pseudoClassStateChanged(future, isFuture);
}


private void configureTable(TableView<BookingImpl> table) {
    table.getColumns().add(column("Time", (Function<BookingImpl, Property<LocalTime>>) (t) -> t.getTimeProperty()));
    table.getColumns().add(column("Name", (Function<BookingImpl, Property<String>>) (t) -> new SimpleStringProperty(t.getClientName())));
    table.getColumns().add(column("Pickup", (Function<BookingImpl, Property<String>>) (t) -> new SimpleStringProperty(t.getPickUpAddress())));
    table.getColumns().add(column("Dropoff", (Function<BookingImpl, Property<String>>) (t) -> new SimpleStringProperty(t.getDropOffAddress())));
    table.getColumns().add(column("Comment", (Function<BookingImpl, Property<String>>) (t) -> new SimpleStringProperty(t.getComments())));
    table.getColumns().add(column("Price", (Function<BookingImpl, Property<String>>) (t) -> new SimpleStringProperty(t.getFormattedPrice())));
    table.setItems(ObservableLists.bookingsList);
}

private <S,T> TableColumn<S,T> column(String title, Function<S, Property<T>> property) {
    TableColumn<S,T> column = new TableColumn<>(title);
    column.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
    return column ;
}

}

BookingImpl 类

public class BookingImpl implements Booking {
public static int bookingNumberCounter = 1001;
private final int bookingNumber;
private Account account;
private String vehicleType;
private String noPassengers;
private LocalDate date;
private ObjectProperty<LocalTime> time = new SimpleObjectProperty<>();
private String pickUpAddress;
private String dropOffAddress;
private String clientName;
private String clientTel;
private String clientEmail;
private String comments;
private double price;
private boolean completed = false;
private Driver driver;

public BookingImpl(Account account){
    this.bookingNumber = bookingNumberCounter;
    bookingNumberCounter++;
    Archive.allBookings.add(this);
    Archive.incompleteBookings.add(this);
    ObservableLists.bookingsList.clear();
    ObservableLists.bookingsList.addAll(Archive.incompleteBookings);
    account.newBooking(this);
}


@Override
public void deleteBooking() {
    account.deleteBooking(this);
    Archive.allBookings.remove(this);
}

public int getBookingNumber() {
    return bookingNumber;
}

public Account getAccount() {
    return account;
}

public void setAccount(Account account) {
    this.account = account;
}

public String getVehicleType() {
    return vehicleType;
}

public void setVehicleType(String vehicleType) {
    this.vehicleType = vehicleType;
}

public String getNoPassengers() {
    return noPassengers;
}

public void setNoPassengers(String noPassengers) {
    this.noPassengers = noPassengers;
}

public String getDate() {
    return new SimpleDateFormat("dd/MM/yyyy").format(date);
}

public void setDate(LocalDate date) {
    this.date = date;
}

public ObjectProperty<LocalTime> getTimeProperty() {
    return time;
}

public LocalTime getTime() {
    return this.getTimeProperty().get();
}

public void setTime(LocalTime time) {this.time.set(time);}

public String getPickUpAddress() {
    return pickUpAddress;
}

public void setPickUpAddress(String pickUpAddress) {
    this.pickUpAddress = pickUpAddress;
}

public String getDropOffAddress() {
    return dropOffAddress;
}

public void setDropOffAddress(String dropOffAddress) {
    this.dropOffAddress = dropOffAddress;
}

public String getClientName() {
    return clientName;
}

public void setClientName(String clientName) {
    this.clientName = clientName;
}

public String getClientTel() {
    return clientTel;
}

public void setClientTel(String clientTel) {
    this.clientTel = clientTel;
}

public String getClientEmail() {
    return clientEmail;
}

public void setClientEmail(String clientEmail) {
    this.clientEmail = clientEmail;
}

public String getComments() {
    return comments;
}

public void setComments(String comments) {
    this.comments = comments;
}

public double getPrice() {
    return price;
}

public String getFormattedPrice() {
    DecimalFormat df = new DecimalFormat("#.00");
    return "£"+df.format(this.price);
}
public void setPrice(double price) {
    this.price = price;
}

public boolean isCompleted() {
    return completed;
}

public void setCompleted(boolean completed) {
    this.completed = completed;
}

public Driver getDriver() {
    return driver;
}

public void setDriver(Driver driver) {
    this.driver = driver;
}
}

【问题讨论】:

  • “保存时间的String 字段...”使用LocalTime 代替String 不是更有意义吗?
  • 嗨,这可以更改为LocalTime 没有问题。类型实际上是Date,但保存在字符串字段中。
  • 您的单元格值工厂看起来不对。您的 BookingImpl 类是否使用 JavaFX 属性?另外,你在哪里打电话start()
  • 您需要使用JavaFX properties 实现您的模型类,以便正确观察它们。 (即使用与Item 相同的模式实现BookingImpl。)
  • 只有LocalTime 字段使用JavaFX 属性,start() 是在initialize() 方法中从我的主控制器类调用的。将按照建议调整课程,希望它有效:)

标签: java css user-interface javafx tableview


【解决方案1】:

我会这样处理:

在桌子上使用rowFactory。您创建的行将观察当前项目的时间属性,并在其更改时更新 CSS PseudoClass。您还需要该行来观察当前时间:为此创建一个ObjectProperty&lt;LocalTime&gt; 并从AnimationTimer 更新它。

使用 CSS PseudoClass 的优点是您可以在外部 CSS 文件中定义样式。

这是一个例子:

import java.time.LocalTime;
import java.util.Random;
import java.util.function.Function;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class HighlightFutureItems extends Application {

    private final PseudoClass future = PseudoClass.getPseudoClass("future");

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> table = new TableView<>();

        ObjectProperty<LocalTime> now = new SimpleObjectProperty<>(LocalTime.now());

        startClock(now);

        table.setRowFactory(tv -> {
            TableRow<Item> row = new TableRow<>();
            ChangeListener<LocalTime> listener = (obs, oldTime, newTime) -> updateRow(row, now.get());
            now.addListener(listener);
            row.itemProperty().addListener((obs, oldItem, newItem) -> {
                if (oldItem != null) {
                    oldItem.timeProperty().removeListener(listener);
                }
                if (newItem != null) {
                    newItem.timeProperty().addListener(listener);
                }
                updateRow(row, now.get());
            });
            return row ;
        });

        configureTable(table);

        BorderPane root = new BorderPane(table);
        Label clock = new Label();
        root.setTop(clock);

        clock.textProperty().bind(now.asString());

        Scene scene = new Scene(root, 600, 600);
        scene.getStylesheets().add("future-highlighting-table.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void updateRow(TableRow<Item> row, LocalTime now) {
        boolean isFuture = false ;
        if (row.getItem() != null) {
            isFuture = row.getItem().getTime().isAfter(now);
        }
        row.pseudoClassStateChanged(future, isFuture);
    }

    private void startClock(ObjectProperty<LocalTime> clock) {
        new AnimationTimer() {
            @Override
            public void handle(long timestamp) {
                clock.set(LocalTime.now());
            }
        }.start();
    }

    private void configureTable(TableView<Item> table) {
        table.getColumns().add(column("Item", Item::nameProperty));
        table.getColumns().add(column("Time", Item::timeProperty));

        Random rng = new Random();
        LocalTime now = LocalTime.now();
        for (int i = 1 ; i <= 50 ; i++) {
            Item item = new Item("Item "+i, now.plusSeconds(rng.nextInt(120) - 60));
            table.getItems().add(item);
        }
    }

    private <S,T> TableColumn<S,T> column(String title, Function<S, Property<T>> property) {
        TableColumn<S,T> column = new TableColumn<>(title);
        column.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        return column ;
    }

    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final ObjectProperty<LocalTime> time = new SimpleObjectProperty<>();

        public Item(String name, LocalTime time) {
            setName(name);
            setTime(time);
        }

        public final StringProperty nameProperty() {
            return this.name;
        }


        public final String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final String name) {
            this.nameProperty().set(name);
        }


        public final ObjectProperty<LocalTime> timeProperty() {
            return this.time;
        }


        public final LocalTime getTime() {
            return this.timeProperty().get();
        }


        public final void setTime(final LocalTime time) {
            this.timeProperty().set(time);
        }



    }

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

CSS 文件 (future-highlighting-table.css") 可以很简单

.table-row-cell:future {
    -fx-background: palevioletred;
}

【讨论】:

  • 很好的例子!这对我很有用我非常感谢你的回答@James_D
  • 我注意到您使用&lt;Item&gt; 作为表类型,我尝试将其更改为&lt;BookingImpl&gt; 并重构代码,但我一直遇到问题。我必须为 TableView 使用&lt;Item&gt; 吗?
  • 不,当然不是。 Item 只是我在示例中定义的类的名称。
  • 谢谢,我已尽力让它工作,但在输入后突出显示它有问题。我已经编辑了我的原始帖子以包含代码。
猜你喜欢
  • 2018-06-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-18
  • 2018-12-04
  • 2012-11-28
  • 2020-06-21
  • 1970-01-01
  • 2020-08-13
相关资源
最近更新 更多