`
iaiai
  • 浏览: 2203337 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

javafx : 支持使用微调(spinner)控制的数字的文本框(NemberTextField)

    博客分类:
  • J2EE
 
阅读更多
最近花了一些时间学习javaFX, 要更深入地理解新GUI包, 自定义控制器可能是一个比较好的方法.
javaFX中也有类似JFormattedTextField和JSpinner的控件. 这对我来说确实是个不错的选择.

这是我的控制器:
  • 数字文本框(NumberTextField): 可以配置任意格式的数字;
  • 微调控制器( NumberSpinner ): 可以使用键盘方向键或箭头按钮来控制数值;它也是控制器的一部分;

控制器及其示例可以在这里下载(可直接导入到netbeans,见附件).  示例中还包含一个css样式文件,  它用于控制Spinner的风格是直角或圆角.



NumberTextField

NumberTextField 的实现很容易,以致我认为这算不上自定义控制器, 而仅仅是改变一个已存在的控制器的一些行为而已.  NumberTextField  扩展自JFX中的文本框(TextField), 添加一个使用BigDecimal的NumberProperty作为模型, 并做一些格式化和解析处理. 就这样, 不复杂.
import java.math.BigDecimal;  
import java.text.NumberFormat;  
import java.text.ParseException;  
import javafx.beans.property.ObjectProperty;  
import javafx.beans.property.SimpleObjectProperty;  
import javafx.beans.value.ChangeListener;  
import javafx.beans.value.ObservableValue;  
import javafx.event.ActionEvent;  
import javafx.event.EventHandler;  
import javafx.scene.control.TextField;  
  
/** 
 * Textfield implementation that accepts formatted number and stores them in a 
 * BigDecimal property The user input is formatted when the focus is lost or the 
 * user hits RETURN. 
 * 
 * @author Thomas Bolz 
 */  
public class NumberTextField extends TextField {  
  
    private final NumberFormat nf;  
    private ObjectProperty<BigDecimal> number = new SimpleObjectProperty<>();  
  
    public final BigDecimal getNumber() {  
        return number.get();  
    }  
  
    public final void setNumber(BigDecimal value) {  
        number.set(value);  
    }  
  
    public ObjectProperty<BigDecimal> numberProperty() {  
        return number;  
    }  
  
    public NumberTextField() {  
        this(BigDecimal.ZERO);  
    }  
  
    public NumberTextField(BigDecimal value) {  
        this(value, NumberFormat.getInstance());  
    }  
  
    public NumberTextField(BigDecimal value, NumberFormat nf) {  
        super();  
        this.nf = nf;  
        initHandlers();  
        setNumber(value);  
    }  
  
    private void initHandlers() {  
  
        // try to parse when focus is lost or RETURN is hit  
        setOnAction(new EventHandler<ActionEvent>() {  
  
            @Override  
            public void handle(ActionEvent arg0) {  
                parseAndFormatInput();  
            }  
        });  
  
        focusedProperty().addListener(new ChangeListener<Boolean>() {  
  
            @Override  
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {  
                if (!newValue.booleanValue()) {  
                    parseAndFormatInput();  
                }  
            }  
        });  
  
        // Set text in field if BigDecimal property is changed from outside.  
        numberProperty().addListener(new ChangeListener<BigDecimal>() {  
  
            @Override  
            public void changed(ObservableValue<? extends BigDecimal> obserable, BigDecimal oldValue, BigDecimal newValue) {  
                setText(nf.format(newValue));  
            }  
        });  
    }  
  
    /** 
     * Tries to parse the user input to a number according to the provided 
     * NumberFormat 
     */  
    private void parseAndFormatInput() {  
        try {  
            String input = getText();  
            if (input == null || input.length() == 0) {  
                return;  
            }  
            Number parsedNumber = nf.parse(input);  
            BigDecimal newValue = new BigDecimal(parsedNumber.toString());  
            setNumber(newValue);  
            selectAll();  
        } catch (ParseException ex) {  
            // If parsing fails keep old number  
            setText(nf.format(number.get()));  
        }  
    }  
}


NumberSpinner

NumberSpinner好像复杂一点. 它构建在NumberTextField 上, 并使用递增和递减按钮来改变文本框中数字的值, 每次改变步长为stepwidth.

stepwidth和NumberFormat的初始值在构造器中指定. 文本框和按钮的大小取决于文本的大小. 文本的大小可在.css文件中设置.
import java.math.BigDecimal;  
import java.text.NumberFormat;  
import javafx.beans.binding.NumberBinding;  
import javafx.beans.property.ObjectProperty;  
import javafx.beans.property.SimpleObjectProperty;  
import javafx.event.ActionEvent;  
import javafx.event.EventHandler;  
import javafx.geometry.Pos;  
import javafx.scene.control.Button;  
import javafx.scene.input.KeyCode;  
import javafx.scene.input.KeyEvent;  
import javafx.scene.layout.HBox;  
import javafx.scene.layout.StackPane;  
import javafx.scene.layout.VBox;  
import javafx.scene.shape.LineTo;  
import javafx.scene.shape.MoveTo;  
import javafx.scene.shape.Path;  
import javax.swing.JSpinner;  
  
/** 
 * JavaFX Control that behaves like a {@link JSpinner} known in Swing. The 
 * number in the textfield can be incremented or decremented by a configurable 
 * stepWidth using the arrow buttons in the control or the up and down arrow 
 * keys. 
 * 
 * @author Thomas Bolz 
 */  
public class NumberSpinner extends HBox {  
  
    public static final String ARROW = "NumberSpinnerArrow";  
    public static final String NUMBER_FIELD = "NumberField";  
    public static final String NUMBER_SPINNER = "NumberSpinner";  
    public static final String SPINNER_BUTTON_UP = "SpinnerButtonUp";  
    public static final String SPINNER_BUTTON_DOWN = "SpinnerButtonDown";  
    private final String BUTTONS_BOX = "ButtonsBox";  
    private NumberTextField numberField;  
    private ObjectProperty<BigDecimal> stepWitdhProperty = new SimpleObjectProperty<>();  
    private final double ARROW_SIZE = 4;  
    private final Button incrementButton;  
    private final Button decrementButton;  
    private final NumberBinding buttonHeight;  
    private final NumberBinding spacing;  
  
    public NumberSpinner() {  
        this(BigDecimal.ZERO, BigDecimal.ONE);  
    }  
  
    public NumberSpinner(BigDecimal value, BigDecimal stepWidth) {  
        this(value, stepWidth, NumberFormat.getInstance());  
    }  
  
    public NumberSpinner(BigDecimal value, BigDecimal stepWidth, NumberFormat nf) {  
        super();  
        this.setId(NUMBER_SPINNER);  
        this.stepWitdhProperty.set(stepWidth);  
  
        // TextField  
        numberField = new NumberTextField(value, nf);  
        numberField.setId(NUMBER_FIELD);  
  
        // Enable arrow keys for dec/inc  
        numberField.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {  
  
            @Override  
            public void handle(KeyEvent keyEvent) {  
                if (keyEvent.getCode() == KeyCode.DOWN) {  
                    decrement();  
                    keyEvent.consume();  
                }  
                if (keyEvent.getCode() == KeyCode.UP) {  
                    increment();  
                    keyEvent.consume();  
                }  
            }  
        });  
  
        // Painting the up and down arrows  
        Path arrowUp = new Path();  
        arrowUp.setId(ARROW);  
        arrowUp.getElements().addAll(new MoveTo(-ARROW_SIZE, 0), new LineTo(ARROW_SIZE, 0),  
                new LineTo(0, -ARROW_SIZE), new LineTo(-ARROW_SIZE, 0));  
        // mouse clicks should be forwarded to the underlying button  
        arrowUp.setMouseTransparent(true);  
  
        Path arrowDown = new Path();  
        arrowDown.setId(ARROW);  
        arrowDown.getElements().addAll(new MoveTo(-ARROW_SIZE, 0), new LineTo(ARROW_SIZE, 0),  
                new LineTo(0, ARROW_SIZE), new LineTo(-ARROW_SIZE, 0));  
        arrowDown.setMouseTransparent(true);  
  
        // the spinner buttons scale with the textfield size  
        // TODO: the following approach leads to the desired result, but it is   
        // not fully understood why and obviously it is not quite elegant  
        buttonHeight = numberField.heightProperty().subtract(3).divide(2);  
        // give unused space in the buttons VBox to the incrementBUtton  
        spacing = numberField.heightProperty().subtract(2).subtract(buttonHeight.multiply(2));  
  
        // inc/dec buttons  
        VBox buttons = new VBox();  
        buttons.setId(BUTTONS_BOX);  
        incrementButton = new Button();  
        incrementButton.setId(SPINNER_BUTTON_UP);  
        incrementButton.prefWidthProperty().bind(numberField.heightProperty());  
        incrementButton.minWidthProperty().bind(numberField.heightProperty());  
        incrementButton.maxHeightProperty().bind(buttonHeight.add(spacing));  
        incrementButton.prefHeightProperty().bind(buttonHeight.add(spacing));  
        incrementButton.minHeightProperty().bind(buttonHeight.add(spacing));  
        incrementButton.setFocusTraversable(false);  
        incrementButton.setOnAction(new EventHandler<ActionEvent>() {  
            @Override  
            public void handle(ActionEvent ae) {  
                increment();  
                ae.consume();  
            }  
        });  
  
        // Paint arrow path on button using a StackPane  
        StackPane incPane = new StackPane();  
        incPane.getChildren().addAll(incrementButton, arrowUp);  
        incPane.setAlignment(Pos.CENTER);  
  
        decrementButton = new Button();  
        decrementButton.setId(SPINNER_BUTTON_DOWN);  
        decrementButton.prefWidthProperty().bind(numberField.heightProperty());  
        decrementButton.minWidthProperty().bind(numberField.heightProperty());  
        decrementButton.maxHeightProperty().bind(buttonHeight);  
        decrementButton.prefHeightProperty().bind(buttonHeight);  
        decrementButton.minHeightProperty().bind(buttonHeight);  
  
        decrementButton.setFocusTraversable(false);  
        decrementButton.setOnAction(new EventHandler<ActionEvent>() {  
  
            @Override  
            public void handle(ActionEvent ae) {  
                decrement();  
                ae.consume();  
            }  
        });  
  
        StackPane decPane = new StackPane();  
        decPane.getChildren().addAll(decrementButton, arrowDown);  
        decPane.setAlignment(Pos.CENTER);  
  
        buttons.getChildren().addAll(incPane, decPane);  
        this.getChildren().addAll(numberField, buttons);  
    }  
  
    /** 
     * increment number value by stepWidth 
     */  
    private void increment() {  
        BigDecimal value = numberField.getNumber();  
        value = value.add(stepWitdhProperty.get());  
        numberField.setNumber(value);  
    }  
  
    /** 
     * decrement number value by stepWidth 
     */  
    private void decrement() {  
        BigDecimal value = numberField.getNumber();  
        value = value.subtract(stepWitdhProperty.get());  
        numberField.setNumber(value);  
    }  
  
    public final void setNumber(BigDecimal value) {  
        numberField.setNumber(value);  
    }  
  
    public ObjectProperty<BigDecimal> numberProperty() {  
        return numberField.numberProperty();  
    }  
  
    public final BigDecimal getNumber() {  
        return numberField.getNumber();  
    }  
  
    // debugging layout bounds  
    public void dumpSizes() {  
        System.out.println("numberField (layout)=" + numberField.getLayoutBounds());  
        System.out.println("buttonInc (layout)=" + incrementButton.getLayoutBounds());  
        System.out.println("buttonDec (layout)=" + decrementButton.getLayoutBounds());  
        System.out.println("binding=" + buttonHeight.toString());  
        System.out.println("spacing=" + spacing.toString());  
    }  
}


number_spinner.css

最后, 控制器的样式可在css文件中设置. 我实现了圆角和直角两种风格(见上文截图). 你可以通过修改 #NumberField, #ButtonBox, #SpinnerButtonUp 和#SpinnerButtonDown 中的border/background-radiuses来切换不同的风格.
.root{  
    -fx-font-size: 24pt;  
    /*    -fx-base: rgb(255,0,0);*/  
    /*    -fx-background: rgb(50,50,50);*/  
}  
#NumberField {  
    -fx-border-width: 1;  
    -fx-border-color: lightgray;  
    -fx-background-insets:1;  
    -fx-border-radius:3 0 0 3;  
    /*    -fx-border-radius:0 0 0 0;*/  
}  
#NumberSpinnerArrow {  
    -fx-fill: gray;  
    -fx-stroke: gray;  
    /*        -fx-effect: innershadow( gaussian , black , 2 , 0.6 , 1 , 1 )*/  
}  
#ButtonsBox {  
    -fx-border-color:lightgray;  
    -fx-border-width: 1 1 1 0;  
    -fx-border-radius: 0 3 3 0;  
    /*    -fx-border-radius: 0 0 0 0;*/  
}  
#SpinnerButtonUp {  
    -fx-background-insets: 0;  
    -fx-background-radius:0 3 0 0;  
    /*    -fx-background-radius:0;*/  
}  
#SpinnerButtonDown {  
    -fx-background-insets: 0;  
    -fx-background-radius:0 0 3 0;  
    /*    -fx-background-radius:0;*/  
}


结论

从上面的例子可以看出在javaFx中自定义控制器并不困难.
  • 大小: 49.3 KB
  • 大小: 46.8 KB
分享到:
评论

相关推荐

    javafx2 : 支持使用微调(spinner)控制的数字的文本框(NemberTextField)

    总之,`javafx2 : 支持使用微调(spinner)控制的数字的文本框(NemberTextField)`涉及到的关键知识点包括JavaFX的`Spinner`控件、`NumberTextField`(可能来自第三方库)的使用,以及如何将两者绑定实现数值输入的同步...

    2009年新书:JavaFX: Developing Rich Internet Applications

    ### JavaFX: 开发丰富的互联网应用 #### 一、引言 随着互联网技术的飞速发展,用户对于网络应用的视觉效果与交互体验有了更高的期待。为了满足这一需求,Sun Microsystems 在 2008 年推出了 JavaFX,这是一种全新的...

    编程选择题40道:JavaFX:图形用户界面设计.Tex.docx

    编程选择题40道:JavaFX:图形用户界面设计.Tex.docx

    JavaFX:Java的现代UI革命

    ### JavaFX:Java的现代UI革命 #### JavaFX概述 JavaFX是Java生态系统中的一个重要组成部分,专注于提供现代、丰富的用户界面开发解决方案。随着用户对于应用界面的要求越来越高,JavaFX成为了开发高质量、跨平台...

    JavaFX:项目实战——贪吃蛇

    在贪吃蛇游戏中,我们需要使用JavaFX的Scene类来定义游戏区域,以及Rectangle、Circle等图形对象来表示蛇、食物和边界。 项目中的`Snake_code.zip`包含了源代码,我们可以从中学习如何使用JavaFX编写游戏逻辑。游戏...

    JavaFX:来自 jdk 1.8 的 JavaFx 测试

    2. **Rich UI 控件**:JavaFX 提供了一系列内置的UI控件,如按钮、文本框、菜单、表格、滑块、进度条等,这些控件都具有良好的可定制性和响应性。 3. **2D 和 3D 图形**:JavaFX 支持2D和3D图形渲染,允许开发者...

    JavaFx实现百度文本框的下拉提示功能

    在JavaFX中,实现百度文本框的下拉提示功能,主要涉及到的是文本输入与实时匹配的概念,这在很多用户界面中都有广泛的应用,如搜索引擎、输入法等。这个功能能够提高用户体验,帮助用户快速找到他们可能正在寻找的...

    Chess-JavaFX:JavaFX国际象棋游戏

    在这个项目中,“Chess-JavaFX”是一款使用JavaFX技术开发的国际象棋游戏,它展示了JavaFX在游戏开发中的应用。下面将详细介绍这个项目的相关知识点。 一、JavaFX基础 JavaFX提供了丰富的API,可以方便地创建出...

    Javafx 2.0: Introduction by Example

    Javafx 2.0: Introduction by Example

    javafx使用指南-目录版.pdf

    JavaFX 使用指南 JavaFX 是一个基于 Java 语言的软件平台,用于开发桌面应用程序、移动应用程序和网络应用程序。它提供了一个统一的编程模型,可以跨平台运行,具有高清晰度的图形用户界面和多媒体支持。 JavaFX ...

    JavaFX:用于练习 JavaFX 和示例实现的文件夹

    这个压缩包“JavaFX:用于练习 JavaFX 和示例实现的文件夹”显然是一个学习资源,包含了练习和示例代码,帮助开发者熟悉JavaFX的使用。 JavaFX 的核心特性包括: 1. **丰富的UI组件**:JavaFX 提供了一系列预定义的...

    JavaFx:使用javaFx gui API的todolist

    本项目“JavaFx:使用javaFx gui API的todolist”显然是一个基于JavaFX实现的任务管理应用,它利用了JavaFX的GUI API来设计和实现待办事项列表的功能。 在JavaFX中,我们首先需要导入相关的库,通常通过在`pom.xml`...

    JVx JavaFX:使用JavaFX的JVx UI实现-开源

    【JVx JavaFX:使用JavaFX的JVx UI实现】是一种开源技术,它结合了JavaFX的图形渲染能力和JVx(Java Virtual eXtension)的业务逻辑框架,为开发者提供了一种创建本机JavaFX应用程序的新途径。JVx是一个灵活且可扩展...

    akka-eventbus-javafx:使用 akka 框架的 JavaFX 的 EventBus

    使用 akka 框架的 JavaFX 的 EventBus 使用命令构建并安装到本地 maven: gradle install 然后你可以从你的本地 Maven 中包含这个依赖: repositories { mavenCentral() mavenLocal() } dependencies { ...

    FlappyBird-JavaFx:JavaFx 中的 FlappyBird 游戏

    《FlappyBird-JavaFx:使用JavaFx构建经典游戏》 在编程世界中,复现经典游戏是一种常见的学习和实践方式。"FlappyBird-JavaFx"项目正是这样一个实例,它利用JavaFx这一强大的图形用户界面(GUI)库,重新实现了广...

    example_javafx:一个简单的示例 JavaFX 程序

    1. **组件库**:JavaFX 提供了一系列预先构建的 UI 控件,如按钮、文本框、表格、菜单等,这些控件可以轻松地组合起来创建复杂的用户界面。 2. **样式和皮肤**:JavaFX 支持 CSS 样式化,允许开发者通过 CSS 文件来...

    uk-crime-javafx:结合JavaFX与JDK 11和JPMS的演示项目

    在该项目中,开发者使用了`mvn javafx:run`命令来启动应用。这是Maven的JavaFX插件提供的一个目标,它能够自动处理JavaFX的运行时环境,并且编译、打包以及运行项目。这表明项目使用了Maven作为构建工具,Maven能...

    JavaFX:JavaFX和JDK8示例

    可以使用CSS设置JavaFX组件的样式,并且可以使用FXML而不是Java代码来构成图形用户界面。 这使得在不浪费时间在Java代码上的情况下,更容易快速地将GUI放在一起,或者更改外观或布局。 JavaFX包含一组现成的图表...

Global site tag (gtag.js) - Google Analytics