━━━━ ◇ ━━━━
Java/JavaFx

JavaFx 08. 계산기 만들기

JavaFX를 사용해서 간단한 계산기를 만들어 보겠습니다.


자료 구조는 다음과 같습니다.


Main.java

Main.fxml

MainController.java

Model.java


1. Main.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package application;
    
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
 
 
public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("/application/Main.fxml"));
            Scene scene = new Scene(root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}
 
cs


이 파일은 Application 을 상속 받아 구성되었습니다. 기본적으로 생성된 소스에서 14번 라인만 수정했습니다.


2. Main.fxml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<?xml version="1.0" encoding="UTF-8"?>
 
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
 
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="300.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
   <children>
      <StackPane prefHeight="50.0" prefWidth="300.0">
         <children>
            <Label fx:id="result" prefHeight="19.0" prefWidth="339.0">
               <font>
                  <Font name="System Bold" size="18.0" />
               </font>
            </Label>
         </children></StackPane>
      <HBox alignment="CENTER" prefHeight="50.0" prefWidth="300.0" spacing="10.0">
         <children>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="7">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="8">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="9">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processOperators" prefWidth="50.0" text="/">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
         </children>
      </HBox>
      <HBox alignment="CENTER" prefHeight="50.0" prefWidth="300.0" spacing="10.0">
         <children>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="4">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="5">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="6">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processOperators" prefWidth="50.0" text="*">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
         </children>
      </HBox>
      <HBox alignment="CENTER" prefHeight="50.0" prefWidth="300.0" spacing="10.0">
         <children>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="1">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="2">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="50.0" text="3">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processOperators" prefWidth="50.0" text="-">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
         </children>
      </HBox>
      <HBox alignment="CENTER" prefHeight="50.0" prefWidth="300.0" spacing="10.0">
         <children>
            <Button mnemonicParsing="false" onAction="#processNumbers" prefWidth="110.0" text="0">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processOperators" prefWidth="50.0" text="=">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            <Button mnemonicParsing="false" onAction="#processOperators" prefWidth="50.0" text="+">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
            
         </children>
      </HBox>
   </children>
</VBox>
 
cs


fxml 파일에서 핵심은 VBox 와 HBox 를 사용해서 버튼들을 규칙적으로 배치하였다는 것입니다. 이외에는 그냥 버튼을 배치하는 것과 다름이 없습니다.



Scene Builder 로 보면 위와 같이 버튼을 배치 하였습니다. 컨트롤러는 MainController 로 설정해 놓았으며, 숫자 버튼과 연산자 버튼을 따로 묶어서 On Action 을 일괄 적용했습니다. 하나씩 적용해도 상관없지만 일괄적으로 처리하니까 편해서 좋았습니다.


3. Model.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package application;
 
public class Model {
    public float calculate(long number1, long number2, String operator) {
        switch (operator) {
        case "+":
            return number1 + number2 ;
        case "-":
            return number1 - number2 ;
        case "*":
            return number1 * number2 ;
        case "/":
            if(number2 == 0){
                return 0;
            }
            return number1 / number2 ;
 
        default:
            return 0;
        }
        
        
        
    }
}
 
cs


모델 클래스에서는 연산자들의 기능을 정의하였습니다. float 타입을 반환하는 메소드 calculate 를 만들었습니다. calculate 메소드는 long 타입의 숫자 2개와 String 타입의 연산자 1개를 매개변수로 작동하는 메소드 입니다. switch 구문을 사용하여 입력된 연산자의 종류에 따라서 입력된 숫자들이 처리됩니다. 나누기의 경우 0으로 나눌수 없기 때문에 0을 리턴하도록 코드를 짰습니다.


4. MainController.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package application;
 
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
 
public class MainController {
 
    @FXML
    private Label result;
    private long number1 = 0;
    private String operator = "";
    private boolean start = true;
    private Model model = new Model();
    
    @FXML
    public void processNumbers(ActionEvent event) {
        if(start){
            result.setText("");
            start = false;
        }
        String value = ((Button)event.getSource()).getText();
        result.setText(result.getText() + value);
    }
    
    @FXML
    public void processOperators(ActionEvent event) {
        String value = ((Button)event.getSource()).getText();
        if(!value.equals("=")){
            if(!operator.isEmpty()){
                return;
            }
            operator = value;
            number1 = Long.parseLong(result.getText());
            result.setText("");
            
        }else{
            if(operator.isEmpty()){
                return;
            }
            long number2 = Long.parseLong(result.getText());
            float ouput = model.calculate(number1, number2, operator);
            result.setText(String.valueOf(ouput));
            operator = "";
            start = true;
        }
    }
    
}
 
cs


(라인 23) 숫자 버튼을 누르면 버튼의 text 값을 가져와서 value 변수에 저장 합니다.

(라인 24) 라벨에 입력되어 있는 값에 value 값을 붙여서 라벨에 입력합니다. 

(라인 30~) 연산자 버튼을 눌렀을때 해당 연산자가 '=' 이 아니면 operator 변수에 해당 연산자를 입력합니다.

(라인 38~) '=' 버튼을 눌렀다면, 라벨에 입력되어 있는 값을 number2에 저장하고, 모델클래스의 calculate 메소드를 호출합니다. 그리고 그 결과값을 라벨에 표시합니다.



COMMENT