[英]javafx: How can I make TableCell Edit return double instead of string and the font changes color based on a condition?
I have the Trade
object class with a 我有一个带有的
Trade
对象类
public class Trade {
private DoubleProperty price;
private ReadOnlyBooleanWrapper caution;
public Trade(double price){
this.price = new SimpleDoubleProperty(price);
this.caution = new ReadOnlyBooleanWrapper();
this.caution.bind(this.volume.greaterThan(0));
}
public double getPrice(){
return this.price.get();
}
public DoubleProperty priceProperty(){
return this.price;
}
public void setPrice(double price){
this.price.set(price);
}
}
In my Controller class, I have the following TableView
and TableColumn
在我的Controller类中,我有以下
TableView
和TableColumn
Problem is two-fold: 问题是双重的:
double
. double
。 But the EditingDoubleCell code below only return String. String
s the user typed in will be ignored? String
都将被忽略? Price
column ( talking about the same price cell ) will change its color to blue when the caution
property is true and to red when the caution
property is false? Price
栏的单元格内的字体( 谈论相同的价格单元格 )将在caution
属性为真时将其颜色更改为蓝色,在caution
属性为假时将其颜色更改为红色? public class EditingDoubleCell extends TableCell<Trade,String>{
private TextField textField;
public EditingDoubleCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.requestFocus();
//textField.selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
private void createTextField(){
Locale locale = new Locale("en", "UK");
String pattern = "###,###.###";
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(locale);
df.applyPattern(pattern);
//String format = df.format(123456789.123);
//System.out.println(format);
//NumberFormat nf = NumberFormat.getIntegerInstance();
textField = new TextField();
// add filter to allow for typing only integer
textField.setTextFormatter( new TextFormatter<>( c ->
{
if (c.getControlNewText().isEmpty()) {
return c;
}
ParsePosition parsePosition = new ParsePosition( 0 );
Object object = df.parse( c.getControlNewText(), parsePosition );
if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
{
return null;
}
else
{
return c;
}
} ) );
textField.setText( getString() );
textField.setMinWidth( this.getWidth() - this.getGraphicTextGap() * 2 );
// commit on Enter
textField.setOnAction( new EventHandler<ActionEvent>()
{
@Override
public void handle( ActionEvent event )
{
commitEdit( textField.getText() );
}
} );
textField.focusedProperty().addListener( new ChangeListener<Boolean>()
{
@Override
public void changed( ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2 )
{
if ( !arg2 )
{
commitEdit( textField.getText() );
}
}
} );
}
}
the first part of the question : You can try the following class (It worked for me): 问题的第一部分 :你可以尝试以下课程(它对我有用):
public class EditingDoubleCell extends TableCell<Trade, Double> {
private TextField textField;
public EditingDoubleCell() {
textField = new TextField();
textField.setOnAction(e -> commitEdit(Double.valueOf(textField.getText())));
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
setText(null);
setGraphic(textField);
textField.requestFocus();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getString());
setGraphic(null);
}
@Override
public void commitEdit(Double newValue) {
super.commitEdit(newValue);
}
@Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
Locale locale = new Locale("en", "UK");
String pattern = "###,###.###";
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(locale);
df.applyPattern(pattern);
String s = df.format(getItem());
setText(s);
setGraphic(null);
// set font of Price cell to a color
TableRow<Trade> row = getTableRow();
if (row.getItem().getCaution()) {
setStyle("-fx-background-color:blue;");
} else {
setStyle("-fx-background-color: red;");
}
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
the second part of the question : Just call setcellfactory(...)
for caution column and you have to override the method updateItem(...)
: 问题的第二部分 :只需调用
setcellfactory(...)
以获取警告列,您必须覆盖方法updateItem(...)
:
cautionCol.setCellFactory(column -> new TableCell<Trade, Boolean>() {
@Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(String.valueOf(item));
//TableRow<Trade> row = getTableRow();
if (item) {
setStyle("-fx-background-color:blue;");
} else {
setStyle("-fx-background-color: red;");
}
}
}
});
For the first part of the problem, you should create your TextFormatter
as a TextFormatter<Double>
. 对于问题的第一部分,您应该将
TextFormatter
创建为TextFormatter<Double>
。 This makes the valueProperty
of the TextFormatter
into a Property<Double>
, so you can commit your edits by calling getValue()
on the formatter. 这使
TextFormatter
的valueProperty
变为Property<Double>
,因此您可以通过在格式化程序上调用getValue()
来提交编辑。 You need to specify a StringConverter<Double>
so that it knows how to go from text to a Double
, and vice-versa. 您需要指定
StringConverter<Double>
以便它知道如何从文本转到Double
,反之亦然。 So this looks like: 所以这看起来像:
StringConverter<Double> converter = new StringConverter<Double>() {
@Override
public String toString(Double number) {
return df.format(number);
}
@Override
public Double fromString(String string) {
try {
double value = df.parse(string).doubleValue() ;
return value;
} catch (ParseException e) {
e.printStackTrace();
return 0.0 ;
}
}
};
textFormatter = new TextFormatter<>(converter, 0.0, c -> {
if (partialInputPattern.matcher(c.getControlNewText()).matches()) {
return c ;
} else {
return null ;
}
}) ;
I changed the filter here, because your filter was only matching a "complete" input. 我在这里更改了过滤器,因为您的过滤器只匹配“完整”输入。 Since the filter is applied to every individual edit, you must allow "partial" input, such as
"100,"
. 由于过滤器应用于每个单独的编辑,因此必须允许“部分”输入,例如
"100,"
。 The filter you had would not allow this (for example). 您拥有的过滤器不允许这样(例如)。 The filter in the version here uses a regular expression: you can tinker with this to get it right but I use
这里版本中的过滤器使用正则表达式:您可以修改它以使其正确但我使用
Pattern partialInputPattern = Pattern.compile(""[-+]?[,0-9]*(\\.[0-9]*)?");
which is pretty lenient with what it allows. 它允许的东西相当宽松。
Now, instead of committing the edit directly when the user hits enter, just commit the edit when the value of the text formatter changes: 现在,当用户点击进入时,不是直接提交编辑,只需在文本格式化程序的值更改时提交编辑:
// commit on Enter
textFormatter.valueProperty().addListener((obs, oldValue, newValue) -> {
commitEdit(newValue);
});
The whole cell class now looks like 整个细胞类现在看起来像
public static class EditingDoubleCell extends TableCell<Trade,Double>{
private TextField textField;
private TextFormatter<Double> textFormatter ;
private DecimalFormat df ;
public EditingDoubleCell(String...styleClasses) {
Locale locale = new Locale("en", "UK");
String pattern = "###,###.###";
df = (DecimalFormat) NumberFormat.getNumberInstance(locale);
df.applyPattern(pattern);
getStyleClass().addAll(styleClasses);
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.requestFocus();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(df.format(getItem()));
setGraphic(null);
}
@Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private String getString() {
return getItem() == null ? "" : df.format(getItem());
}
private void createTextField(){
textField = new TextField();
StringConverter<Double> converter = new StringConverter<Double>() {
@Override
public String toString(Double number) {
return df.format(number);
}
@Override
public Double fromString(String string) {
try {
double value = df.parse(string).doubleValue() ;
return value;
} catch (ParseException e) {
e.printStackTrace();
return 0.0 ;
}
}
};
textFormatter = new TextFormatter<>(converter, 0.0, c ->
{
if (c.getControlNewText().isEmpty()) {
return c;
}
ParsePosition parsePosition = new ParsePosition( 0 );
Object object = df.parse( c.getControlNewText(), parsePosition );
if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
{
return null;
}
else
{
return c;
}
} ) ;
// add filter to allow for typing only integer
textField.setTextFormatter( textFormatter);
textField.setText( getString() );
textField.setMinWidth( this.getWidth() - this.getGraphicTextGap() * 2 );
// commit on Enter
textFormatter.valueProperty().addListener((obs, oldValue, newValue) -> {
commitEdit(newValue);
});
}
}
(I added the constructor parameter so it will work with the solution to your second question.) (我添加了构造函数参数,因此它将与第二个问题的解决方案一起使用。)
The second part is answered elsewhere, but I would just create a rowFactory
for your table that sets a CSS pseudoclass based on the state of the caution property: 第二部分在别处回答,但我只是为你的表创建一个
rowFactory
,它根据caution属性的状态设置一个CSS伪类:
PseudoClass caution = PseudoClass.getPseudoClass("caution");
table.setRowFactory(tv -> {
TableRow<Trade> row = new TableRow<>();
ChangeListener<Boolean> cautionListener = (obs, wasCaution, isNowCaution) ->
row.pseudoClassStateChanged(caution, isNowCaution);
row.itemProperty().addListener((obs, oldTrade, newTrade) -> {
if (oldTrade != null) {
oldTrade.cautionProperty().removeListener(cautionListener);
}
if (newTrade == null) {
row.pseudoClassStateChanged(caution, false);
} else {
row.pseudoClassStateChanged(caution, newTrade.isCaution());
newTrade.cautionProperty().addListener(cautionListener);
}
});
return row ;
});
Then just set a style class on the cell you want the style to change on (eg add the style class "price-cell"
to the EditingDoubleCell
you defined). 然后在要更改样式的单元格上设置样式类(例如,将样式类
"price-cell"
到您定义的EditingDoubleCell
)。 Then you can just use a CSS stylesheet to change the style as you need, eg 然后你可以根据需要使用CSS样式表来改变样式,例如
.table-row-cell .price-cell {
-fx-text-fill: red ;
}
.table-row-cell:caution .price-cell {
-fx-text-fill: blue ;
}
will make the text red for price cells in rows that do not have caution
set, and make it blue in rows that do. 将为没有
caution
设置的行中的价格单元格设置红色文本,并将其设置为蓝色。
Here is the complete SSCCE: 这是完整的SSCCE:
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.function.Function;
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class TradeTable extends Application {
private final Random rng = new Random();
@Override
public void start(Stage primaryStage) {
TableView<Trade> table = new TableView<>();
table.setEditable(true);
TableColumn<Trade, Integer> volumeCol = column("Volume", trade -> trade.volumeProperty().asObject());
TableColumn<Trade, Double> priceCol = column("Price", trade -> trade.priceProperty().asObject());
priceCol.setCellFactory(col -> new EditingDoubleCell("price-cell"));
table.getColumns().add(volumeCol);
table.getColumns().add(priceCol);
PseudoClass caution = PseudoClass.getPseudoClass("caution");
table.setRowFactory(tv -> {
TableRow<Trade> row = new TableRow<>();
ChangeListener<Boolean> cautionListener = (obs, wasCaution, isNowCaution) ->
row.pseudoClassStateChanged(caution, isNowCaution);
row.itemProperty().addListener((obs, oldTrade, newTrade) -> {
if (oldTrade != null) {
oldTrade.cautionProperty().removeListener(cautionListener);
}
if (newTrade == null) {
row.pseudoClassStateChanged(caution, false);
} else {
row.pseudoClassStateChanged(caution, newTrade.isCaution());
newTrade.cautionProperty().addListener(cautionListener);
}
});
return row ;
});
table.getItems().addAll(createRandomData());
Button button = new Button("Change Data");
button.setOnAction(e -> table.getItems().forEach(trade -> {
if (rng.nextDouble() < 0.5) {
trade.setVolume(0);
} else {
trade.setVolume(rng.nextInt(10000));
}
trade.setPrice(rng.nextDouble() * 1000);
}));
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(10));
BorderPane root = new BorderPane(table, null, null, button, null);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add("trade-table.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private List<Trade> createRandomData() {
List<Trade> trades = new ArrayList<>(50);
for (int i = 0 ; i < 50; i++) {
int volume = rng.nextDouble() < 0.5 ? 0 : rng.nextInt(10000) ;
double price = rng.nextDouble() * 10000 ;
trades.add(new Trade(price, volume));
}
return trades ;
}
private static <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
public static class Trade {
private DoubleProperty price;
private IntegerProperty volume ;
private ReadOnlyBooleanWrapper caution;
public Trade(double price, int volume){
this.price = new SimpleDoubleProperty(price);
this.volume = new SimpleIntegerProperty(volume);
this.caution = new ReadOnlyBooleanWrapper();
this.caution.bind(this.volume.greaterThan(0));
}
public double getPrice(){
return this.price.get();
}
public DoubleProperty priceProperty(){
return this.price;
}
public void setPrice(double price){
this.price.set(price);
}
public final IntegerProperty volumeProperty() {
return this.volume;
}
public final int getVolume() {
return this.volumeProperty().get();
}
public final void setVolume(final int volume) {
this.volumeProperty().set(volume);
}
public final ReadOnlyBooleanProperty cautionProperty() {
return this.caution.getReadOnlyProperty();
}
public final boolean isCaution() {
return this.cautionProperty().get();
}
}
public static class EditingDoubleCell extends TableCell<Trade,Double>{
private TextField textField;
private TextFormatter<Double> textFormatter ;
private Pattern partialInputPattern = Pattern.compile(
"[-+]?[,0-9]*(\\.[0-9]*)?");
private DecimalFormat df ;
public EditingDoubleCell(String...styleClasses) {
Locale locale = new Locale("en", "UK");
String pattern = "###,###.###";
df = (DecimalFormat) NumberFormat.getNumberInstance(locale);
df.applyPattern(pattern);
getStyleClass().addAll(styleClasses);
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.requestFocus();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(df.format(getItem()));
setGraphic(null);
}
@Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private String getString() {
return getItem() == null ? "" : df.format(getItem());
}
private void createTextField(){
textField = new TextField();
StringConverter<Double> converter = new StringConverter<Double>() {
@Override
public String toString(Double number) {
return df.format(number);
}
@Override
public Double fromString(String string) {
try {
double value = df.parse(string).doubleValue() ;
return value;
} catch (ParseException e) {
e.printStackTrace();
return 0.0 ;
}
}
};
textFormatter = new TextFormatter<>(converter, 0.0, c -> {
if (partialInputPattern.matcher(c.getControlNewText()).matches()) {
return c ;
} else {
return null ;
}
}) ;
// add filter to allow for typing only integer
textField.setTextFormatter( textFormatter);
textField.setText( getString() );
textField.setMinWidth( this.getWidth() - this.getGraphicTextGap() * 2 );
// commit on Enter
textFormatter.valueProperty().addListener((obs, oldValue, newValue) -> {
commitEdit(newValue);
});
}
}
public static void main(String[] args) {
launch(args);
}
}
With the CSS code above in trade-table.css. 使用trade-table.css中的CSS代码。
I had similar problem, I did as follows: 我有类似的问题,我做了如下:
SimpleDoubleProperty price = new SimpleDoubleProperty();
price.setValue(Double.parseDouble(EditingDoubleCell().getString()));
ObservableValue<Double> g = price.asObject();
return g;
This method anticipates that you can parse your String into double. 此方法预计您可以将String解析为double。 Works for me, tell me if it helped :)
适合我,告诉我它是否有帮助:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.