---
category: swing
folder: TimePicker
title: JFormattedTextFieldと増減用JButtonを組み合わせて時間選択コンポーネントを作成する
title-en: Create a time selection component by combining a JFormattedTextField and increment/decrement JButtons
tags: [JFormattedTextField, JButton, JSpinner, Calendar]
author: aterai
pubdate: 2026-03-02T00:36:33+09:00
description: JFormattedTextFieldの上下に増減用JButtonを配置してJSpinner風の時間選択コンポーネントを作成します。
summary-jp: JFormattedTextFieldの上下に増減用JButtonを配置してJSpinner風の時間選択コンポーネントを作成します。
summary-en: Create a JSpinner-style time selection component by placing increment/decrement JButtons above and below a JFormattedTextField.
image: https://drive.google.com/uc?id=101CyjRPsRjX22GM-lMU-HvIAXJ5TBezd
---
* Summary [#summary]
JFormattedTextFieldの上下に増減用JButtonを配置してJSpinner風の時間選択コンポーネントを作成します。
`JFormattedTextField`の上下に増減用`JButton`を配置して`JSpinner`風の時間選択コンポーネントを作成します。
// #en{{Create a `JSpinner`-style time selection component by placing increment/decrement `JButton`s above and below a `JFormattedTextField`.}}
#download(https://drive.google.com/uc?id=101CyjRPsRjX22GM-lMU-HvIAXJ5TBezd)
* Source Code Examples [#sourcecode]
#code(link){{
class AutoRepeatHandler extends MouseAdapter implements ActionListener {
private final Timer autoRepeatTimer;
private final JTextComponent view;
private final int delta;
private final int min;
private final int max;
private JButton arrowButton;
protected AutoRepeatHandler(JTextComponent view, int delta, int min, int max) {
super();
this.view = view;
this.delta = delta;
this.min = min;
this.max = max;
autoRepeatTimer = new Timer(60, this);
autoRepeatTimer.setInitialDelay(300);
}
public static void adjust(JTextComponent field, int delta, int min, int max) {
field.requestFocusInWindow();
int range = max - min + 1;
int value = Integer.parseInt(field.getText());
value = (value - min + delta) % range;
if (value < 0) {
value += range;
}
value += min;
field.setText(String.format("%02d", value));
}
@Override public void actionPerformed(ActionEvent e) {
Object o = e.getSource();
if (o instanceof Timer) {
boolean released = Objects.nonNull(arrowButton) &&
!arrowButton.getModel().isPressed();
if (released && autoRepeatTimer.isRunning()) {
autoRepeatTimer.stop();
}
} else if (o instanceof JButton) {
arrowButton = (JButton) o;
}
adjust(view, delta, min, max);
}
@Override public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && e.getComponent().isEnabled()) {
autoRepeatTimer.start();
}
}
@Override public void mouseReleased(MouseEvent e) {
autoRepeatTimer.stop();
}
@Override public void mouseExited(MouseEvent e) {
if (autoRepeatTimer.isRunning()) {
autoRepeatTimer.stop();
}
}
}
}}
* Description [#description]
- 左: `JFormattedTextField`+`LocalTime`を使用した時間選択コンポーネント
-- `new MaskFormatter("##:##")`で作成しまマスクを`JFormattedTextField`に設定
-- キー入力での数値変更は`JFormattedTextField#setEditable(false)`不可で、マウスホイールによる時間変更のみ可能
-- 以下のように`:`で区切られた時、分フィールド上でマウスホイールが回転したかをチェックして時間を更新
#code{{
timeField.addMouseWheelListener(e -> {
boolean isUp = e.getWheelRotation() < 0;
boolean isHourSide = timeField.viewToModel(e.getPoint()) <= 2;
adjustTime(isHourSide, isUp);
});
}}
- 右: `JFormattedTextField`+`JButton`を組み合わせた時間選択コンポーネント
-- 増減ボタンの配置を上下に変更した`JSpinner`風の時間選択コンポーネント(`24`時間選択固定)を`JFormattedTextField`+`JButton`で作成
-- 時、分フィールドに別々の`JFormattedTextField`を適用し、増減ボタンもそれぞれの上下に配置して倍の`4`個使用
-- `JSpinner`とは異なり、時、分フィールドに別々の`JFormattedTextField`を適用し、増減ボタンもそれぞれの上下に配置して倍の`4`個使用
-- `MouseWheelListener`もそれぞれ設定するので、時、分フィールドのどちらでマウスホイールが回転したかをチェックする必要がない
-- 時、分フィールドは単独でループするよう設定
--- たとえば`12:59`から分フィールドで`1`増加しても`13:00`ではなく`12:00`に変化する
* Reference [#reference]
- [[JSpinnerでLocalDateTimeを使用する>Swing/SpinnerLocalDateTimeModel]]
- [[JButtonがマウスで押されている間、アクションを繰り返すTimerを設定する>Swing/AutoRepeatTimer]]
* Comment [#comment]
#comment
#comment