Java 小练习(1) -- Java FX 编写计算器

引言:

最近在学习java ,在学习的过程中想找几个简单的小程序练手。用Java写一个计算器算一个难度非常低的练习,它将我之前学过的 Java 的知识重新地拾起来了。在这个小练习中,我选择了逻辑层和显示层分离(MVC模式)的JavaFX 技术。在javafx 中,用fxml文件来表示显示层内容,用Java 来编写程序的业务逻辑。

文章结构

|-- 显示层设计
|-- 逻辑层设计
|-- 源代码

显示层

显示层的设计中只存在一个简单的界面,界面中包含两部分 -- 按钮区和显示区。具体的界面如图:

Java 小练习(1) -- Java FX 编写计算器

具体的布局为 :

SpiltPane--|--VBox--|--HBox
           |        |--HBox
           |        |--HBox
           |
           |--GridPane

 

 在整个JavaFX程序中,显示层的界面和布局都是由fxml 文件生成的,在idea中可以直接由集成的Java Scene Builder 拖拽和配置相应的参数完成。

逻辑层

在这次的练习中计算器的设计逻辑是这样的:

1. 用一个Stack 去存储输入的数字和操作符,并将输入input1,input2分别用两个StringBuffer类存储,并实时更新到显示区域。

2. 每点击一个数字,都会在显示区域的标签中, 并分别存入输入栈和 输入字符串的StringBuffer中。

3. 用操作符来区分输入的数字是第一个还是第二个。

4. 当点击删除(DEL)时,从栈中将栈顶的元素删除,从输入的StringBuffer类中去掉这个字符。当点击Clear 时,清空输入栈(inputStack)和输入字符串(firstInputStr,secondInputStr)。并将对应的结果实时更新到显示区。

5. 当点击  ' = ' 时,开始进行计算,并把计算的结果反馈到显示区域。每次计算之后,都会对相应的参数进行清空。

源代码文件

GitHub仓库:https://github.com/XDKejin/java_files_of_my_study

 

1. Calculator.java

​
package calculator;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Calculator extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{

        Parent root = FXMLLoader.load(getClass().getResource("calculator.fxml"));
        primaryStage.setTitle("Calculator");
        primaryStage.setScene(new Scene(root, 400, 500));
        primaryStage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}

​

2. CalculatorController.java

package calculator;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

import java.net.URL;
import java.util.ResourceBundle;
import java.util.Stack;


public class CalculatorController implements Initializable{

    @FXML private Label firstInput;
    @FXML private Label secondInput;
    @FXML private Label showResult;
    @FXML private Label showOperator;


    Stack <Character>inputStack ;

    StringBuffer firstInputStr;
    StringBuffer secondInputStr;

    char operator;

    boolean isChanged = false;

    int result;     //store result


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        firstInputStr = new StringBuffer();
        secondInputStr = new StringBuffer();
        inputStack = new <Character>Stack();
    }



    /*
    *   number button action code  is below here:
    * */

    @FXML protected void pressNumZero(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('0');
        else
            secondInputStr.append('0');

        inputStack.push('0');
        putStrToLabel();


    }
    @FXML protected void pressNumOne(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('1');
        else
            secondInputStr.append('1');

        inputStack.push('1');
        putStrToLabel();

    }
    @FXML protected void pressNumTwo(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('2');
        else
            secondInputStr.append('2');

        inputStack.push('2');
        putStrToLabel();
    }

    @FXML protected void pressNumThree(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('3');
        else
            secondInputStr.append('3');

        inputStack.push('3');
        putStrToLabel();

    }
    @FXML protected void pressNumFour(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('4');
        else
            secondInputStr.append('4');

        inputStack.push('4');
        putStrToLabel();
    }
    @FXML protected void pressNumFive(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('5');
        else
            secondInputStr.append('5');

        inputStack.push('5');
        putStrToLabel();
    }
    @FXML protected void pressNumSix(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('6');
        else
            secondInputStr.append('6');

        inputStack.push('6');
        putStrToLabel();
    }
    @FXML protected void pressNumSeven(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('7');
        else
            secondInputStr.append('7');

        inputStack.push('7');
        putStrToLabel();
    }
    @FXML protected void pressNumEight(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('8');
        else
            secondInputStr.append('8');

        inputStack.push('8');
        putStrToLabel();
    }
    @FXML protected void pressNumNine(ActionEvent event){
        if (!isChanged)
            firstInputStr.append('9');
        else
            secondInputStr.append('9');

        inputStack.push('9');
        putStrToLabel();
    }

    /*
    *       operator button action code is below here;
    *
    * */

    @FXML protected void pressOperatorAdd(ActionEvent event){
        operator = '+';
        inputStack.push('+');
        showOperator.setText("+");
        isChanged = true;

    }
    @FXML protected void pressOperatorMinus(ActionEvent event){
        operator = '-';
        inputStack.push('-');
        showOperator.setText("-");
        isChanged = true;
    }
    @FXML protected void pressOperatorMultiply(ActionEvent event){
        operator = '*';
        inputStack.push('*');
        showOperator.setText("*");
        isChanged = true;
    }
    @FXML protected void pressOperatorDivide(ActionEvent event){
        operator = '/';
        inputStack.push('/');
        showOperator.setText("/");
        isChanged = true;
    }
    @FXML protected void pressOperatorMod(ActionEvent event){
        operator = '%';
        inputStack.push('%');
        showOperator.setText("%");
        isChanged = true;
    }


    /*
    *       function key action code is below here
    * */
    @FXML protected void pressFunckeyDel(ActionEvent event){
        if (inputStack.empty()){
            clearCache();
            updateDisplay();
            return;
        }


        if ( (int)(inputStack.peek()) >= (int)('0')  && (int)(inputStack.peek()) <= (int)('9')){
            if (!isChanged)
                firstInputStr.deleteCharAt( firstInputStr.length() - 1 );
            else
                secondInputStr.deleteCharAt( secondInputStr.length() - 1 );
        }else {
            showOperator.setText("");
            isChanged = false;
        }

        inputStack.pop();
        updateDisplay();

    }

    @FXML protected void pressFunckeyClear(ActionEvent event){
        clearCache();
        updateDisplay();
    }


    /*
    *       press '=' button get RESULT
    * */
    @FXML protected void getResult(ActionEvent event){
        int first = Integer.parseInt(firstInputStr.toString());
        int second = Integer.parseInt(secondInputStr.toString());

        switch (operator){
            case '+' :
                result = first + second;
                break;
            case '-' :
                result = first - second;
                break;
            case '*' :
                result = first * second;
                break;
            case '/' :
                if (0 == second)
                    break;
                result = first / second;
                break;
            case '%' :
                result = first % second;
                break;
        }
        showResult.setText(result+"");

        //清空各种参数的缓存
        clearCache();
    }



    /*
    *       extra method
    *
    * */

    void putStrToLabel(){
        firstInput.setText(firstInputStr.toString());
        secondInput.setText(secondInputStr.toString());
    }

    void updateDisplay(){
        if (inputStack.empty()){
            setDisplayNull();
            return;
        }

        firstInput.setText(firstInputStr.toString());
        secondInput.setText(secondInputStr.toString());
        showOperator.setText(operator+"");
        if ( 0 == result )
            showResult.setText("");
        else
            showResult.setText(result+"");

    }


    void clearCache(){
        isChanged = false;
        firstInputStr.delete( 0,(firstInputStr.length()) );
        secondInputStr.delete( 0, (secondInputStr.length()));
        result = 0;

        while(!inputStack.empty()){
            inputStack.pop();
            System.out.println(inputStack.toString());
        }

    }

    void setDisplayNull(){
        firstInput.setText("");
        secondInput.setText("");
        showOperator.setText("");
        showResult.setText("");

    }





}

3. calculator.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<GridPane alignment="center" hgap="10" prefHeight="500.0" prefWidth="400.0" vgap="10" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="calculator.CalculatorController">
   <columnConstraints>
      <ColumnConstraints />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
   </rowConstraints>
   <children>
      <SplitPane dividerPositions="0.3273092369477912" orientation="VERTICAL" prefHeight="500.0" prefWidth="400.0">
        <items>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="398.0">
               <children>
                  <VBox prefHeight="150.0" prefWidth="400.0">
                     <children>
                        <HBox prefHeight="50.0" prefWidth="400.0">
                           <children>
                              <Label prefHeight="50.0" prefWidth="70.0" style="-fx-background-color: #CDC9C9;" text="Input 1:">
                                 <font>
                                    <Font size="16.0" />
                                 </font>
                                 <HBox.margin>
                                    <Insets />
                                 </HBox.margin></Label>
                              <Label fx:id="firstInput" prefHeight="50.0" prefWidth="280.0">
                                  <font>
                                      <Font size="16.0" />
                                  </font></Label>
                              <Label fx:id="showOperator" alignment="CENTER" contentDisplay="CENTER" prefHeight="50.0" prefWidth="50.0" style="-fx-background-color: #F0FFF0;">
                                 <font>
                                    <Font size="20.0" />
                                 </font></Label>
                           </children></HBox>
                        <HBox layoutY="50.0" prefHeight="50.0" prefWidth="400.0">
                           <children>
                              <Label prefHeight="50.0" prefWidth="70.0" style="-fx-background-color: #CDC9C9;" text="Input 2:">
                                  <font>
                                      <Font size="16.0" />
                                  </font></Label>
                              <Label fx:id="secondInput" prefHeight="50.0" prefWidth="330.0">
                                  <font>
                                      <Font size="16.0" />
                                  </font></Label>
                           </children></HBox>
                        <HBox layoutY="100.0" prefHeight="50.0" prefWidth="400.0">
                           <children>
                              <Label prefHeight="50.0" prefWidth="70.0" style="-fx-background-color: #CDC9C9;" text="Result :">
                                  <font>
                                      <Font size="16.0" />
                                  </font></Label>
                              <Label fx:id="showResult" prefHeight="50.0" prefWidth="330.0">
                                  <font>
                                      <Font size="16.0" />
                                  </font></Label>
                           </children></HBox>
                     </children>
                  </VBox>
               </children></AnchorPane>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="350.0" prefWidth="400.0">
               <children>
                  <GridPane layoutY="10.0" prefHeight="320.0" prefWidth="400.0">
                    <columnConstraints>
                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />

                    </columnConstraints>
                    <rowConstraints>
                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                      

                    </rowConstraints>
                     <children>
                        <Button fx:id="numOne" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumOne" prefHeight="80.0" prefWidth="80.0" text="1" GridPane.columnIndex="0" GridPane.rowIndex="0">
                           <font>
                              <Font size="20.0" />
                           </font></Button>
                         <Button fx:id="numTwo" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumTwo" prefHeight="80.0" prefWidth="80.0" text="2" GridPane.columnIndex="1" GridPane.rowIndex="0">
                             <font>
                                 <Font size="20" />
                             </font>
                         </Button>
                         <Button fx:id="numThree" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumThree" prefHeight="80.0" prefWidth="80.0" text="3" GridPane.columnIndex="2" GridPane.rowIndex="0">
                             <font>
                                 <Font size="20" />
                             </font>
                         </Button>
                        <Button fx:id="numFour" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumFour" prefHeight="80.0" prefWidth="80.0" text="4" GridPane.columnIndex="0" GridPane.rowIndex="1">
                             <font>
                                 <Font size="20" />
                             </font>
                        </Button>
                         <Button fx:id="numFive" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumFive" prefHeight="80.0" prefWidth="80.0" text="5" GridPane.columnIndex="1" GridPane.rowIndex="1">
                             <font>
                                 <Font size="20" />
                             </font>
                         </Button>
                         <Button fx:id="numSix" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumSix" prefHeight="80.0" prefWidth="80.0" text="6" GridPane.columnIndex="2" GridPane.rowIndex="1">
                             <font>
                                 <Font size="20" />
                             </font>
                         </Button>
                        <Button fx:id="numSeven" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumSeven" prefHeight="80.0" prefWidth="80.0" text="7" GridPane.columnIndex="0" GridPane.rowIndex="2">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="numEight" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumEight" prefHeight="80.0" prefWidth="80.0" text="8" GridPane.columnIndex="1" GridPane.rowIndex="2">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="numNine" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumNine" prefHeight="80.0" prefWidth="80.0" text="9" GridPane.columnIndex="2" GridPane.rowIndex="2">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="operatorMod" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressOperatorMod" prefHeight="80.0" prefWidth="80.0" text="mod" GridPane.columnIndex="0" GridPane.rowIndex="3">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="numZero" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressNumZero" prefHeight="80.0" prefWidth="80.0" text="0" GridPane.columnIndex="1" GridPane.rowIndex="3">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="calculatorResult" contentDisplay="CENTER" mnemonicParsing="false" onAction="#getResult" prefHeight="80.0" prefWidth="80.0" text="=" GridPane.columnIndex="2" GridPane.rowIndex="3">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="funckeyDel" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressFunckeyDel" prefHeight="80.0" prefWidth="80.0" text="DEL" GridPane.columnIndex="3">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="funcKeyClear" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressFunckeyClear" prefHeight="80.0" prefWidth="80.0" text="CLEAR" GridPane.columnIndex="3" GridPane.rowIndex="1">
                            <font>
                                <Font size="16" />
                            </font>
                        </Button>
                        <Button fx:id="operatorAdd" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressOperatorAdd" prefHeight="80.0" prefWidth="80.0" text="+" GridPane.columnIndex="3" GridPane.rowIndex="2">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="operatorMinus" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressOperatorMinus" prefHeight="80.0" prefWidth="80.0" text="-" GridPane.columnIndex="4" GridPane.rowIndex="2">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="operatorMultiply" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressOperatorMultiply" prefHeight="80.0" prefWidth="80.0" text="*" GridPane.columnIndex="3" GridPane.rowIndex="3">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button fx:id="operatorDivide" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressOperatorDivide" prefHeight="80.0" prefWidth="80.0" text="/" GridPane.columnIndex="4" GridPane.rowIndex="3">
                            <font>
                                <Font size="20" />
                            </font>
                        </Button>
                        <Button contentDisplay="CENTER" mnemonicParsing="false" prefHeight="80.0" prefWidth="80.0" text="spare1" GridPane.columnIndex="4" GridPane.rowIndex="0">
                            <font>
                                <Font size="16" />
                            </font>
                        </Button>
                        <Button contentDisplay="CENTER" mnemonicParsing="false" prefHeight="80.0" prefWidth="80.0" text="spare2" GridPane.columnIndex="4" GridPane.rowIndex="1">
                            <font>
                                <Font size="16" />
                            </font>
                        </Button>
                     </children>
                  </GridPane>
               </children></AnchorPane>
        </items>
      </SplitPane>
   </children>
</GridPane>

Java 小练习(1) -- Java FX 编写计算器