jjzjj

javafx:如何使 TableCell Edit 返回 double 而不是字符串,并且字体会根据条件更改颜色?

coder 2024-03-03 原文

我有 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);
    }
}

在我的 Controller 类中,我有以下 TableViewTableColumn

问题有两个:

  1. 价格属性和价格列只接受double。但是下面的 EditingDoubleCell 代码只返回 String。 我怎样才能让它返回 double 值,并且用户输入的所有 String 都将被忽略?
  2. 我想要的第二个功能是:Price 列单元格内的字体(谈论相同的价格单元格)会将其颜色更改为蓝色当 caution 属性为 true 时变为红色 当 caution 属性为 false 时?

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() );
                }
            }
        } );

    }
}

最佳答案

对于问题的第一部分,您应该创建自己的 TextFormatter作为TextFormatter<Double> .这使得 valuePropertyTextFormatter进入Property<Double> , 因此您可以通过调用 getValue() 来提交您的编辑在格式化板上。您需要指定一个 StringConverter<Double>这样它就知道如何从文本到 Double ,反之亦然。所以这看起来像:

        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 ;
            }
        }) ;

我在这里更改了过滤器,因为您的过滤器只匹配“完整”输入。由于过滤器应用于每个单独的编辑,因此您必须允许“部分”输入,例如 "100," .您拥有的过滤器不允许这样做(例如)。此处版本中的过滤器使用正则表达式:您可以修改它以使其正确,但我使用

Pattern partialInputPattern = Pattern.compile(""[-+]?[,0-9]*(\\.[0-9]*)?");

这对它允许的内容相当宽松。

现在,不是在用户按下回车时直接提交编辑,而是在文本格式化程序的值更改时提交编辑:

    // commit on Enter
    textFormatter.valueProperty().addListener((obs, oldValue, newValue) -> {
        commitEdit(newValue);
    });

整个细胞类现在看起来像

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);
        });
    }
}

(我添加了构造函数参数,因此它将与您第二个问题的解决方案一起使用。)

第二部分在别处得到了回答,但我只想创建一个 rowFactory对于根据警告属性的状态设置 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 ;
});

然后只需在要更改样式的单元格上设置样式类(例如,将样式类 "price-cell" 添加到您定义的 EditingDoubleCell 中)。然后,您可以只使用 CSS 样式表根据需要更改样式,例如

.table-row-cell .price-cell {
    -fx-text-fill: red ;
}

.table-row-cell:caution .price-cell {
    -fx-text-fill: blue ;
}

将使行中没有 caution 的价格单元格的文本变为红色设置,并在需要的行中将其设置为蓝色。

这是完整的 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);
    }
}

使用上面 trade-table.css 中的 CSS 代码。

关于javafx:如何使 TableCell Edit 返回 double 而不是字符串,并且字体会根据条件更改颜色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32188676/

有关javafx:如何使 TableCell Edit 返回 double 而不是字符串,并且字体会根据条件更改颜色?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  4. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  5. ruby-on-rails - unicode 字符串的长度 - 2

    在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)

  6. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  7. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  10. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

随机推荐