如何在JavaFX的列中显示/隐藏千位逗号?

huangapple 未分类评论43阅读模式
英文:

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(&quot;-fx-text-fill: green&quot;), 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&lt;Integer, Person&gt; column1 = new TableColumn&lt;&gt;(&quot;Salary&quot;);
    column1.setCellValueFactory(new PropertyValueFactory(&quot;salary&quot;));

    tableView.getColumns().add(column1);
    tableView.getItems().add(new Person(27000));
    tableView.getItems().add(new Person(48000));
    column1.setStyle(&quot;-fx-text-fill: green&quot;);	

    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&lt;Person&gt; tableView = new TableView&lt;&gt;();
        TableColumn&lt;Person, Integer&gt; salaryColumn = new TableColumn&lt;&gt;(&quot;Salary&quot;);
        salaryColumn.setCellValueFactory(new PropertyValueFactory&lt;&gt;(&quot;salary&quot;));
        tableView.getColumns().add(salaryColumn);

        // Using a check box in this example to change between formats::
        CheckBox useGroupingCheckBox = new CheckBox(&quot;use grouping&quot;);

        // 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 -&gt; new TableCell&lt;&gt;() {
            @Override
            protected void updateItem(Integer item, boolean empty) {
                super.updateItem(item, empty);
                if (item == null || empty) {
                    setText(&quot;&quot;);
                } else {
                    // Use grouping when check box selected, don&#39;t when not selected:
                    formatter.setGroupingUsed(useGroupingCheckBox.isSelected());
                    setText(formatter.format(item));
                }
            }
        });

        // Refresh table on check box action:
        useGroupingCheckBox.setOnAction(event -&gt; 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:

如何在JavaFX的列中显示/隐藏千位逗号?

答案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 如何在JavaFX的列中显示/隐藏千位逗号?

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, &quot;useGrouping&quot;, 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&lt;T, S extends Number&gt; extends TableCell&lt;T, S&gt; {
        
        private FormattingHandler formattingHandler;
        private InvalidationListener groupingListener = o -&gt; updateItem(getItem(), isEmpty());

        public FormattingCell(FormattingHandler formattingHandler) {
            this.formattingHandler = Objects.requireNonNull(formattingHandler, &quot;formatter must not be null&quot;);
            // Beware: a weak listener isn&#39;t entirely safe 
            // will introduce memory leaks if the observable doesn&#39;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(&quot;&quot;);
            } else {
                setText(formattingHandler.format(item));
            }
        }
        
    }
    
    @Override
    public void start(Stage primaryStage) {

        TableView&lt;Person&gt; tableView = new TableView&lt;&gt;();
        TableColumn&lt;Person, Integer&gt; salaryColumn = new TableColumn&lt;&gt;(&quot;Salary&quot;);
        salaryColumn.setCellValueFactory(cc -&gt; 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(&quot;use grouping&quot;);
        useGroupingCheckBox.selectedProperty().bindBidirectional(formatter.groupingProperty());
        // install custom formatting cell
        salaryColumn.setCellFactory(column -&gt; new FormattingCell&lt;&gt;(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
    }
}

huangapple
  • 本文由 发表于 2020年7月25日 11:27:43
  • 转载请务必保留本文链接:https://java.coder-hub.com/63084012.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定