回答編集履歴

3

コード追加

2025/01/05 10:25

投稿

jimbe
jimbe

スコア13235

test CHANGED
@@ -28,3 +28,212 @@
28
28
 
29
29
  全く関係ありませんが、 ボタンの処理を Action で書くのは本件ではほぼ意味がありませんので ActionListener でコンストラクタ内に書いたほうがコードは短くなると思います。
30
30
  Action は、ある機能を実行する方法がメニューにあったりツールバーに(ボタンで)あったりコンテキストメニューにあったりする場合に 1 つの定義を流用することで、表示の統一や Enable/Disable を簡単にする為に用います。
31
+
32
+ 以下は各ボタンの ActionListener で処理を行い、また input を editable=false とした上で KeyListener でボタン操作に変換します。数字以外のキーも( '%' 以外)テキトウに割り振りました。
33
+ ```java
34
+ import java.awt.*;
35
+ import java.awt.event.*;
36
+ import java.text.DecimalFormat;
37
+ import java.util.HashMap;
38
+ import java.util.Map;
39
+
40
+ import javax.swing.*;
41
+
42
+ public class Calculator extends JFrame {
43
+ public static void main(String[] args) {
44
+ new Calculator().setVisible(true);
45
+ }
46
+ //キー入力をボタン操作に変換
47
+ private static class InputKeyAdapter extends KeyAdapter {
48
+ private Map<Character,JButton> charMap = new HashMap<>();
49
+ private Map<Integer,JButton> codeMap = new HashMap<>();
50
+
51
+ void put(JButton button, char c) {
52
+ charMap.put(c, button);
53
+ }
54
+ void put(JButton button, int code) {
55
+ codeMap.put(code, button);
56
+ }
57
+ @Override
58
+ public void keyTyped(KeyEvent e) {
59
+ doClick(charMap.get(e.getKeyChar()));
60
+ }
61
+ @Override
62
+ public void keyReleased(KeyEvent e) {
63
+ doClick(codeMap.get(e.getKeyCode()));
64
+ }
65
+ private void doClick(JButton button) {
66
+ if(button != null) button.doClick();
67
+ }
68
+ };
69
+
70
+ private JTextField input1, input2, input, way, answer;
71
+ private InputKeyAdapter keyAdapter = new InputKeyAdapter();
72
+
73
+ private Calculator() {
74
+ super("Calculator");
75
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
76
+
77
+ //生成
78
+ input1 = createInput("数1");
79
+ way = createTitledField("計算方法", 3);
80
+ input2 = createInput("数2");
81
+ answer = createTitledField("結果", 5);
82
+
83
+ ActionListener numActionListener = v -> { //lamda 式によりコード短縮
84
+ String num = ((JButton)v.getSource()).getText();
85
+ String text = input.getText();
86
+ input.setText((text.equals("0")?"":text) + num);
87
+ };
88
+ JButton[] numButtons = new JButton[10];
89
+ for(int i=0; i<10; i++) numButtons[i] = createButton(""+i, numActionListener);
90
+
91
+ JButton dotButton = createButton(".", v -> {
92
+ String text = input.getText();
93
+ if(!text.contains(".")) input.setText(text + ".");
94
+ });
95
+
96
+ JButton acButton = createButton("AC", v -> {
97
+ input1.setText("0");
98
+ way.setText("");
99
+ input2.setText("0");
100
+ answer.setText("");
101
+ }, KeyEvent.VK_ESCAPE);
102
+
103
+ JButton delButton = createButton("Del", v -> {
104
+ String text = input.getText();
105
+ if(text.length() > (text.startsWith("-")?2:1)) {
106
+ input.setText(text.substring(0, text.length()-1));
107
+ } else {
108
+ input.setText("0");
109
+ }
110
+ }, KeyEvent.VK_DELETE, KeyEvent.VK_BACK_SPACE);
111
+
112
+ JButton pmButton = createButton("+/-", v -> {
113
+ String text = input.getText();
114
+ if(text.startsWith("-")) {
115
+ input.setText(text.substring(1));
116
+ } else if(!text.equals("0")) {
117
+ input.setText("-" + text);
118
+ }
119
+ }, KeyEvent.VK_SHIFT);
120
+
121
+ JButton percentButton = createButton("%", v -> {
122
+ way.setText("%");
123
+ setAnswer(Double.parseDouble(input.getText()) / 100);
124
+ });
125
+
126
+ JButton addButton = createButton('+', BinaryOperation.ADD);
127
+ JButton subButton = createButton('-', BinaryOperation.SUB);
128
+ JButton mulButton = createButton('*', BinaryOperation.MUL);
129
+ JButton divButton = createButton('/', BinaryOperation.DIV);
130
+
131
+ JButton eqButton = createButton("=", v -> {
132
+ double a = Double.parseDouble(input1.getText());
133
+ double b = Double.parseDouble(input2.getText());
134
+ BinaryOperation op = BinaryOperation.of(way.getText());
135
+ setAnswer(op == null ? 0 : op.calc(a,b));
136
+ }, KeyEvent.VK_ENTER);
137
+
138
+ //配置
139
+ JPanel northPanel = new JPanel(new GridBagLayout());
140
+ GridBagConstraints gbc = new GridBagConstraints();
141
+ gbc.gridy = 0;
142
+ gbc.fill = GridBagConstraints.HORIZONTAL;
143
+ gbc.weightx = 1;
144
+ northPanel.add(input1, gbc);//左側で伸縮
145
+ gbc.weightx = 0;
146
+ northPanel.add(way, gbc);//中央固定サイズ
147
+ gbc.weightx = 1;
148
+ northPanel.add(input2, gbc); //右側で伸縮
149
+ add(northPanel, BorderLayout.NORTH);
150
+
151
+ add(answer, BorderLayout.SOUTH);
152
+
153
+ JPanel centerPanel = createGridPanel(5,3, acButton,pmButton,percentButton,delButton,numButtons[0],dotButton);
154
+ for(int i : new int[]{3,2,1, 6,5,4, 9,8,7}) centerPanel.add(numButtons[i], 3); //インデックス固定の為, 逆順で挿入
155
+ centerPanel.setPreferredSize(new Dimension(270, 300));
156
+ add(centerPanel, BorderLayout.CENTER);
157
+
158
+ add(createGridPanel(5,1,addButton,subButton,mulButton,divButton,eqButton), BorderLayout.EAST);
159
+
160
+ pack();
161
+ setMinimumSize(getSize());
162
+ setLocationRelativeTo(null); //画面中央へ
163
+
164
+ //初期化
165
+ acButton.doClick();
166
+ input1.requestFocus();
167
+ }
168
+
169
+ private static JPanel createGridPanel(int rows, int cols, Component... components) {
170
+ JPanel panel = new JPanel(new GridLayout(rows, cols));
171
+ for(Component c : components) panel.add(c);
172
+ return panel;
173
+ }
174
+
175
+ private static JTextField createTitledField(String title, int columns) {
176
+ JTextField textField = new JTextField(columns);
177
+ textField.setEditable(false);
178
+ textField.setFocusable(false);
179
+ textField.setFont(textField.getFont().deriveFont(30f));
180
+ textField.setBorder(BorderFactory.createTitledBorder(title));
181
+ return textField;
182
+ }
183
+
184
+ private JTextField createInput(String title) {
185
+ JTextField textField = createTitledField(title, 5);
186
+ textField.setFocusable(true);
187
+ textField.addKeyListener(keyAdapter);
188
+ textField.addFocusListener(new FocusAdapter() {
189
+ @Override
190
+ public void focusGained(FocusEvent e) {
191
+ if(input != null) input.setBackground(way.getBackground());
192
+ input = textField;
193
+ input.setBackground(Color.WHITE);
194
+ }
195
+ });
196
+ return textField;
197
+ }
198
+
199
+ private JButton createButton(String text, ActionListener l, int... keyCodes) {
200
+ return createButton(text, text.length()==1?text.charAt(0):0, l, keyCodes);
201
+ }
202
+ private JButton createButton(char keyChar, BinaryOperation op) {
203
+ return createButton(op.text, keyChar, v -> way.setText(op.text));
204
+ }
205
+ private JButton createButton(String text, char keyChar, ActionListener l, int... keyCodes) {
206
+ JButton button = new JButton(text);
207
+ button.setFocusable(false);
208
+ button.setFont(button.getFont().deriveFont(20f));
209
+ button.addActionListener(l);
210
+ if(keyChar != 0) keyAdapter.put(button, keyChar);
211
+ for(int keyCode : keyCodes) keyAdapter.put(button, keyCode);
212
+ return button;
213
+ }
214
+
215
+ private final DecimalFormat ANSWER_FORMAT = new DecimalFormat("0.######");
216
+ public void setAnswer(Double v) {
217
+ answer.setText(ANSWER_FORMAT.format(v));
218
+ }
219
+
220
+ //2項演算
221
+ private enum BinaryOperation {
222
+ ADD("+") { @Override double calc(double a, double b) { return a+b; } },
223
+ SUB("-") { @Override double calc(double a, double b) { return a-b; } },
224
+ MUL("×") { @Override double calc(double a, double b) { return a*b; } },
225
+ DIV("÷") { @Override double calc(double a, double b) { return a/b; } };
226
+
227
+ static BinaryOperation of(String text) {
228
+ for(BinaryOperation op : values()) if(op.text.equals(text)) return op;
229
+ return null;
230
+ }
231
+
232
+ final String text;
233
+ BinaryOperation(String text) {
234
+ this.text = text;
235
+ }
236
+ abstract double calc(double a, double b);
237
+ }
238
+ }
239
+ ```

2

追記

2024/12/31 17:35

投稿

jimbe
jimbe

スコア13235

test CHANGED
@@ -21,3 +21,10 @@
21
21
 
22
22
  プラスマイナスを変更した時の setText でも NumericDocument は反応しますので、原因は同じです。
23
23
 
24
+ ---
25
+ insertString メソッドの str に(1文字ずつで無く)全文字列が入ってくるのは、(NumAction で) JTextField に対して setText しているからです。
26
+ ご存じの通り setText は既存の文字列を消して指定した文字列にするというものですので、 JTextField は AbstractDocument#remove 等で既存を全て消してから PlainDocument#insertString で新たな文字列を設定しようとします。しかし insertString を override して super.insertString せずに return してしまうと remove の効果だけが残り、「勝手に空にされ」た状態になるのです。
27
+ なおこの動作は setText に因るものですので、 JTextField に直接キーボードから順次入力した場合は insertString の str は1文字ずつになります。
28
+
29
+ 全く関係ありませんが、 ボタンの処理を Action で書くのは本件ではほぼ意味がありませんので ActionListener でコンストラクタ内に書いたほうがコードは短くなると思います。
30
+ Action は、ある機能を実行する方法がメニューにあったりツールバーに(ボタンで)あったりコンテキストメニューにあったりする場合に 1 つの定義を流用することで、表示の統一や Enable/Disable を簡単にする為に用います。

1

追記

2024/12/29 16:00

投稿

jimbe
jimbe

スコア13235

test CHANGED
@@ -18,3 +18,6 @@
18
18
  ```
19
19
  としており、 validValues は `"0123456789.+-%"` ですので最後の 1235 は if が成立し return してしまいます。
20
20
  恐らく insertString の str には一文字ずつ入ってくるつもりでこのようなコードを書かれたのでしょうが、そうでは無かったためのバグと思われます。ちゃんと動作を確認してください。
21
+
22
+ プラスマイナスを変更した時の setText でも NumericDocument は反応しますので、原因は同じです。
23
+