英文:
How to show/hide the thousand commas in the column of JavaFX?
问题
在JavaFX中,我有一个表格。我希望能够控制千位分隔符的显示/隐藏。目前,我可以通过column1.setStyle("-fx-text-fill: green")
来控制颜色,但是如何通过类似的方法显示千位分隔符(然后在稍后的阶段将它们隐藏起来)呢?
public class Main_fx extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
TableView tableView = new TableView();
TableColumn<Integer, Person> column1 = new TableColumn<>("Salary");
column1.setCellValueFactory(new PropertyValueFactory<>("salary"));
// 你可以在这里设置一个单元格工厂来自定义单元格的显示方式
column1.setCellFactory(column -> new TableCell<Integer, Person>() {
@Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (empty || person == null) {
setText(null);
} else {
// 在此处处理千位分隔符的显示和隐藏
// 例如:setText(NumberFormat.getNumberInstance(Locale.US).format(person.getSalary()));
setTextFill(Color.GREEN); // 设置文字颜色
}
}
});
tableView.getColumns().add(column1);
tableView.getItems().add(new Person(27000));
tableView.getItems().add(new Person(48000));
VBox vbox = new VBox(tableView);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Person类:
import javafx.beans.property.SimpleIntegerProperty;
public class Person {
private SimpleIntegerProperty salaryProperty;
public Person() {
}
public Person(int salary) {
this.salaryProperty = new SimpleIntegerProperty(salary);
}
public int getSalary() {
return salaryProperty.get();
}
public void setSalary(int salary) {
this.salaryProperty.set(salary);
}
}
英文:
I have a table in JavaFX. I want to control the show/hide of the thousand commas. Currently, I can control the color by column1.setStyle("-fx-text-fill: green")
, but how can I show the thousand commas (and then hide them back in some later stage), probably via a similar approach?
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
TableView tableView = new TableView();
TableColumn<Integer, Person> column1 = new TableColumn<>("Salary");
column1.setCellValueFactory(new PropertyValueFactory("salary"));
tableView.getColumns().add(column1);
tableView.getItems().add(new Person(27000));
tableView.getItems().add(new Person(48000));
column1.setStyle("-fx-text-fill: green");
VBox vbox = new VBox(tableView);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Person:
import javafx.beans.property.SimpleIntegerProperty;
public class Person {
private SimpleIntegerProperty salaryProperty;
public Person() {
}
public Person(int salary) {
this.salaryProperty = new SimpleIntegerProperty(salary);
}
public int getSalary() {
return salaryProperty.get();
}
public void setSalary(int salary) {
this.salaryProperty = new SimpleIntegerProperty(salary);
}
}
答案1
得分: 4
我认为使用CSS是不可能实现的。您需要使用类似于以下示例中的NumberFormat
:
App:
package formatter;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.text.NumberFormat;
import java.util.Locale;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
// 创建表格视图及其列(如您已经做过的):
TableView<Person> tableView = new TableView<>();
TableColumn<Person, Integer> salaryColumn = new TableColumn<>("Salary");
salaryColumn.setCellValueFactory(new PropertyValueFactory<>("salary"));
tableView.getColumns().add(salaryColumn);
// 在此示例中使用复选框来切换格式:
CheckBox useGroupingCheckBox = new CheckBox("use grouping");
// 使用区域设置创建货币格式化程序,这对于国际化很重要:
NumberFormat formatter = NumberFormat.getCurrencyInstance(Locale.CANADA);
formatter.setMaximumFractionDigits(0);
// 创建自定义单元格:
salaryColumn.setCellFactory(column -> new TableCell<>() {
@Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
// 复选框选中时使用分组,未选中时不使用:
formatter.setGroupingUsed(useGroupingCheckBox.isSelected());
setText(formatter.format(item));
}
}
});
// 在复选框动作上刷新表格:
useGroupingCheckBox.setOnAction(event -> tableView.refresh());
// 添加一些测试数据:
tableView.getItems().add(new Person(27000));
tableView.getItems().add(new Person(48000));
// 准备场景和舞台:
VBox vbox = new VBox(useGroupingCheckBox, tableView);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
Person 类:
package formatter;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class Person {
private IntegerProperty salary;
public Person() {
salary = new SimpleIntegerProperty();
}
public Person(int salary) {
this();
this.salary.set(salary);
}
public Integer getSalary() {
return salary.get();
}
public IntegerProperty salaryProperty() {
return salary;
}
public void setSalary(int salary) {
this.salary.set(salary);
}
}
英文:
I think that it is not possible to do with css. You need to use some kind of NumberFormat
like in this example:
App:
package formatter;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.text.NumberFormat;
import java.util.Locale;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
// Create the table view and its column (like you already did):
TableView<Person> tableView = new TableView<>();
TableColumn<Person, Integer> salaryColumn = new TableColumn<>("Salary");
salaryColumn.setCellValueFactory(new PropertyValueFactory<>("salary"));
tableView.getColumns().add(salaryColumn);
// Using a check box in this example to change between formats::
CheckBox useGroupingCheckBox = new CheckBox("use grouping");
// Create a currency formatter with a locale which is important for internationalization:
NumberFormat formatter = NumberFormat.getCurrencyInstance(Locale.CANADA);
formatter.setMaximumFractionDigits(0);
// Create a custom cell:
salaryColumn.setCellFactory(column -> new TableCell<>() {
@Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
// Use grouping when check box selected, don't when not selected:
formatter.setGroupingUsed(useGroupingCheckBox.isSelected());
setText(formatter.format(item));
}
}
});
// Refresh table on check box action:
useGroupingCheckBox.setOnAction(event -> tableView.refresh());
// Add some test data:
tableView.getItems().add(new Person(27000));
tableView.getItems().add(new Person(48000));
// Prepare scene and stage:
VBox vbox = new VBox(useGroupingCheckBox, tableView);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
Person class:
package formatter;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class Person {
private IntegerProperty salary;
public Person() {
salary = new SimpleIntegerProperty();
}
public Person(int salary) {
this();
this.salary.set(salary);
}
public Integer getSalary() {
return salary.get();
}
public IntegerProperty salaryProperty() {
return salary;
}
public void setSalary(int salary) {
this.salary.set(salary);
}
}
Preview:
答案2
得分: 1
已接受的答案虽然可以,但有点不太合理,因为它需要在更改格式属性时手动刷新表格。
另一种选择是:
- 将格式(不可观察)包装到可观察的内容中
- 实现一个自定义单元格,监听变化并根据需要进行更新。
关于如何实现第一个选项(用于分组状态)的示例在下面的代码中的FormattingHandler中。注意:
- 包装属性本身实现了包含的格式的更新
- NumberFormat完全隐藏在处理程序内部:尽量不允许在处理程序的控制下更改其属性(显然这不是百分之百可靠的,因为外部代码仍然可以保留对格式的引用,并随意更改它——这类似于核心ObservableList实现中支持列表的隔离级别)
实现第二个选项的示例是FormattingCell。它接受一个非空的FormattingHandler,注册一个监听器来监视分组属性,并在无效时进行更新通知。注意,这可能会引入内存泄漏(即使监听器是弱引用!)如果可观察对象根本不改变(这是弱监听器设计中的一个已知问题,不幸的是,不会进行更改)——唯一的解决方法是将监听放入自定义单元格皮肤中,并在皮肤释放时移除监听器。
代码(从Anko的答案中借来的样板代码:)
public class DynamicFormattingCellBinding extends Application {
/**
* 可观察的NumberFormat包装器。
*/
public static class FormattingHandler {
/*
* 控制格式分组的属性。
*/
private BooleanProperty useGrouping = new SimpleBooleanProperty(this, "useGrouping", false) {
@Override
protected void invalidated() {
super.invalidated();
groupingInvalidated();
}
};
private NumberFormat formatter;
public FormattingHandler(NumberFormat formatter) {
this.formatter = formatter;
setGrouping(formatter.isGroupingUsed());
}
public BooleanProperty groupingProperty() {
return useGrouping;
}
public boolean isGrouping() {
return groupingProperty().get();
}
public void setGrouping(boolean grouping) {
groupingProperty().set(grouping);
}
public String format(Number number) {
return formatter.format(number);
}
private void groupingInvalidated() {
formatter.setGroupingUsed(isGrouping());
}
}
public static class FormattingCell<T, S extends Number> extends TableCell<T, S> {
private FormattingHandler formattingHandler;
private InvalidationListener groupingListener = o -> updateItem(getItem(), isEmpty());
public FormattingCell(FormattingHandler formattingHandler) {
this.formattingHandler = Objects.requireNonNull(formattingHandler, "formatter must not be null");
// 注意:弱引用监听器并不完全安全
// 如果可观察对象不改变,将会引入内存泄漏!
formattingHandler.groupingProperty().addListener(new WeakInvalidationListener(groupingListener));
}
@Override
protected void updateItem(S item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
setText(formattingHandler.format(item));
}
}
}
@Override
public void start(Stage primaryStage) {
TableView<Person> tableView = new TableView<>();
TableColumn<Person, Integer> salaryColumn = new TableColumn<>("Salary");
salaryColumn.setCellValueFactory(cc -> cc.getValue().salaryProperty().asObject());
tableView.getColumns().add(salaryColumn);
// 实例化格式支持并在视图元素中注册双向绑定
FormattingHandler formatter = new FormattingHandler(NumberFormat.getCurrencyInstance());
CheckBox useGroupingCheckBox = new CheckBox("use grouping");
useGroupingCheckBox.selectedProperty().bindBidirectional(formatter.groupingProperty());
// 安装自定义格式单元格
salaryColumn.setCellFactory(column -> new FormattingCell<>(formatter));
// 添加一些测试数据:
tableView.getItems().add(new Person(27000));
tableView.getItems().add(new Person(48000));
// 准备场景和舞台:
VBox vbox = new VBox(useGroupingCheckBox, tableView);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
private static class Person {
// 和其他答案中一样
}
}
英文:
The accepted answer is just fine though a bit smelly because it requires to manually refresh the table when changing the format's property.
An alternative is
- to wrap the format - which is not observable - into something that is observable
- implement a custom cell which listens to the change/s and updates itself as needed.
An example of how to implement the first (for the grouping state) is FormattingHandler in the code below. Note that
- the wrapping property itself implements the update of the contained format
- the NumberFormat is completely hidden inside the handler: that's doing the best to not allow changes of its properties under the feet of the handler (obviously it's not entirely fool-proof because outside code can still keep a reference to the format and change it at will - it's a similar isolation level as f.i. the backing list in core ObservableList implementations)
An example of how to implement the second is FormattingCell. It takes a not-null FormattingHandler, registers a listener to the grouping property and updates itself on invalidation notification. Note that this might introduce a memory leak (even though the listener is weak!) if the observable doesn't change at all (it's a known issue in the design of weak listeners that will not be changed, unfortunately) - the only way out would be to move the listening into a custom cell skin and remove the listener in the skin's dispose.
The code (boilderplate stolen from Anko's answer
public class DynamicFormattingCellBinding extends Application {
/**
* Observable wrapper around NumberFormat.
*/
public static class FormattingHandler {
/*
* Property controlling the grouping of the format.
*/
private BooleanProperty useGrouping = new SimpleBooleanProperty(this, "useGrouping", false) {
@Override
protected void invalidated() {
super.invalidated();
groupingInvalidated();
}
};
private NumberFormat formatter;
public FormattingHandler(NumberFormat formatter) {
this.formatter = formatter;
setGrouping(formatter.isGroupingUsed());
}
public BooleanProperty groupingProperty() {
return useGrouping;
}
public boolean isGrouping() {
return groupingProperty().get();
}
public void setGrouping(boolean grouping) {
groupingProperty().set(grouping);
}
public String format(Number number) {
return formatter.format(number);
}
private void groupingInvalidated() {
formatter.setGroupingUsed(isGrouping());
}
}
public static class FormattingCell<T, S extends Number> extends TableCell<T, S> {
private FormattingHandler formattingHandler;
private InvalidationListener groupingListener = o -> updateItem(getItem(), isEmpty());
public FormattingCell(FormattingHandler formattingHandler) {
this.formattingHandler = Objects.requireNonNull(formattingHandler, "formatter must not be null");
// Beware: a weak listener isn't entirely safe
// will introduce memory leaks if the observable doesn't change!
formattingHandler.groupingProperty().addListener(new WeakInvalidationListener(groupingListener));
}
@Override
protected void updateItem(S item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
setText(formattingHandler.format(item));
}
}
}
@Override
public void start(Stage primaryStage) {
TableView<Person> tableView = new TableView<>();
TableColumn<Person, Integer> salaryColumn = new TableColumn<>("Salary");
salaryColumn.setCellValueFactory(cc -> cc.getValue().salaryProperty().asObject());
tableView.getColumns().add(salaryColumn);
// instantiate the formatting support and register bidi binding with a view element
FormattingHandler formatter = new FormattingHandler(NumberFormat.getCurrencyInstance());
CheckBox useGroupingCheckBox = new CheckBox("use grouping");
useGroupingCheckBox.selectedProperty().bindBidirectional(formatter.groupingProperty());
// install custom formatting cell
salaryColumn.setCellFactory(column -> new FormattingCell<>(formatter));
// Add some test data:
tableView.getItems().add(new Person(27000));
tableView.getItems().add(new Person(48000));
// Prepare scene and stage:
VBox vbox = new VBox(useGroupingCheckBox, tableView);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
private static class Person {
// exact same as in the other answer
}
}
专注分享java语言的经验与见解,让所有开发者获益!
评论