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

huangapple go评论64阅读模式
英文:

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://go.coder-hub.com/63084012.html
匿名

发表评论

匿名网友

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

确定