こんにちは。
今回は、SwingでJSpinner、JSliderを扱ってみました。
JSpinner、JSliderは連続した値から一つの値をユーザに選択してもらうときに使います。JSpinnerでは値を変更するための上下ボタンが表示されます。JSliderではスライダーが表示されます。スライダーのつまみをスライドすることでユーザーは値を選択できます。
以下のプログラムを雛形として使います。
ソース
import javax.swing.JFrame;
public class Test extends JFrame {
public static void main(String[] args) {
new Test();
}
public Test() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(350, 250);
init();
setVisible(true);
}
private void init() {
// 設置処理
}
}
目次
JSpinner
JSpinnerとSpinnerModel
JSpinnerでは以下のコンストラクタが用意されています。
- JSpinner()
- JSpinner(SpinnerModel model)
2つ目のコンストラクタでは引数がSpinnerModelインターフェースの実装クラスです。このインターフェースが値の範囲、初期値を管理しているようです。
1つ目のコンストラクタでは引数がありませんが、内部的にはSpinnerModelインターフェースの実装クラスであるSpinnerNumberModelのインスタンスが、初期値が0で最小値または最大値の制限なしで作成されます。2つ目のコンストラクタでは引数にSpinnerNumberModelクラスのインスタンスを指定するのが一般的のようです。
いくつかコンストラクタがあるのですが、ここでは以下を使ってみます。
- SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)
実際に、以下のように使ってみました。
ソース
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
public class Test2 extends JFrame {
public static void main(String[] args) {
new Test2();
}
public Test2() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(350, 250);
init();
setVisible(true);
}
private void init() {
setLayout(new FlowLayout());
JSpinner spinner1 = new JSpinner();
add(spinner1);
JSpinner spinner2 = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1));
add(spinner2);
JSpinner spinner3 = new JSpinner(new SpinnerNumberModel(0, -10000, 10, 1));
add(spinner3);
}
}
上記では、レイアウトマネージャにFlowLayoutを設置しています。
各JSpinnerの横幅は、SpinnerNumberModelで設定した最小値と最大値から決定されるようです。spinner1は値の制限はないのですが、表示される桁数が一桁になってしまってます。なのでSpinnerNumberModelのインスタンスを使って明示的に最小値・最大値を決めた方が良いかもしれません。ただし、レイアウトマネージャにBorderLayoutを設定した場合は自動的に横幅を広げてくれます。
ChangeListener
addChangeListenerメソッドでJSpinnerにリスナーを追加することができます。引数はChangeListenerインターフェースの実装クラスです。このインターフェースはstateChangedメソッドのみ持ちます。JSpinnerの値が変更されるたびに、このメソッドが呼び出されます。
以下、実装例です。
ソース
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
public class Test3 extends JFrame {
public static void main(String[] args) {
new Test3();
}
public Test3() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(350, 250);
init();
setVisible(true);
}
private void init() {
setLayout(new FlowLayout());
JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1));
add(spinner);
spinner.addChangeListener(event -> {
System.out.println("current value: " + spinner.getValue());
});
}
}
ウィンドウが表示された後、上ボタンがクリックされると、コンソールに「current value: 1」と表示されます。
JSlider
コンストラクタとBoundedRangeModel
JSliderでは内部的にはBoundedRangeModelインターフェースのが値の範囲を扱っているようです。コンストラクタJSlider(BoundedRangeModel brm)があります。他にもBoundedRangeModelを意識することなく使えるコンストラクタが用意されてます。
今回は、以下を使ってみました。
- JSlider()
- JSlider(int min, int max, int value)
これ以外にもコンストラクタが用意されてますが、今回は使いませんでした。また、使用されるBoundedRangeModelインターフェースの実装クラスはDefaultBoundedRangeModelのようです。
以下、3つのスライダーを設置してみました。
ソース
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JSlider;
public class Test4 extends JFrame {
public static void main(String[] args) {
new Test4();
}
public Test4() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(350, 250);
init();
setVisible(true);
}
private void init() {
setLayout(new FlowLayout());
JSlider slider1 = new JSlider();
add(slider1);
JSlider slider2 = new JSlider(0, 10, 2);
add(slider2);
JSlider slider3 = new JSlider(-1000, 0, -100);
add(slider3);
}
}
JSlider()の場合、範囲が0から100、初期値が50の水平スライダが作成されます。
JSlider(int min, int max, int value)ではそれぞれの引数で最小値、最大値、および初期値が設定され、水平スライダが作成されます。指定できる数値については、負の数も扱えます。
数値の組み合わせに矛盾があると、IllegalArgumentExceptionが発生します。以下のエラーメッセージが出力されます。
- java.lang.IllegalArgumentException: invalid range properties
例えば、「new JSlider(0, 10, -2)」という引数の組み合わせの場合、上記のエラーが発生します。
addChangeListenerとJLabel
スライダーの値を表示する方法については、例えばJLabelを使う方法があります。ラベルにスライダーの値を設定する方法です。スライダーの値が変更されたときのリスナーはaddChangeListenerで設定することができます。
以下では、スライダーの値が変更されたときに、JLabelに値を出力しています。
ソース
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
public class Test5 extends JFrame {
public static void main(String[] args) {
new Test5();
}
public Test5() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(350, 250);
init();
setVisible(true);
}
private void init() {
setLayout(new FlowLayout());
JSlider slider = new JSlider();
add(slider);
JLabel label = new JLabel(String.valueOf(slider.getValue()));
add(label);
slider.addChangeListener(event -> {
label.setText(String.valueOf(slider.getValue()));
});
}
}
コンストラクタJSlider()を使ってインスタンスを生成しているため、初期値は50です。ラベルには50が表示されます。マウスでスライダーのつまみをドラッグして動かすと、ラベルの表示が変わります。
目盛りとラベル
スライダーに目盛りを追加することができます。以下のメソッドを使います。
- void setPaintTicks(boolean b)
- void setMinorTickSpacing(int n)
- void setMajorTickSpacing(int n)
setPaintTicksの引数をtrueにして、setMinorTickSpacing、setMajorTickSpacingで小目盛、大目盛の設定をします。setPaintTicks(true)を実行しただけでは目盛りは表示されません。
更にラベルを追加したい場合、setPaintLabels(true)を実行します。初期設定ではラベルは大目盛に合わせて表示されます。ラベルを指定する場合、void setLabelTable(Dictionary labels)メソッドを使います。Dictionaryクラスは抽象メソッドを含むため、実際にはそのサブクラスであるHashtableを使います。ここで、キーと値の組み合わせはIntegerとjava.swing.JComponentです。
以下、設定例です。
ソース
import java.awt.FlowLayout;
import java.util.Dictionary;
import java.util.Hashtable;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
public class Test6 extends JFrame {
public static void main(String[] args) {
new Test6();
}
public Test6() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(350, 250);
init();
setVisible(true);
}
private void init() {
setLayout(new FlowLayout());
JSlider slider1 = new JSlider();
add(slider1);
slider1.setPaintTicks(true);
slider1.setMinorTickSpacing(10);
slider1.setMajorTickSpacing(20);
JSlider slider2 = new JSlider();
add(slider2);
slider2.setPaintTicks(true);
slider2.setMinorTickSpacing(10);
slider2.setMajorTickSpacing(20);
slider2.setPaintLabels(true);
JSlider slider3 = new JSlider();
add(slider3);
slider3.setPaintTicks(true);
slider3.setMinorTickSpacing(10);
slider3.setMajorTickSpacing(20);
Dictionary<Integer, JComponent> dictionary = new Hashtable<>();
dictionary.put(0, new JLabel("min"));
dictionary.put(50, new JLabel("middle"));
dictionary.put(100, new JLabel("max"));
slider3.setPaintLabels(true);
slider3.setLabelTable(dictionary);
}
}
slider1では目盛りの設定をしています。小目盛10、大目盛20で設定しています。slider2ではさらにラベルを表示しています。初期設定なので、大目盛に合わせてラベルが表示されています。slider3では目盛り0、50、100に対してラベルを付けています。JComponentのサブクラスであるJLabelを使っています。ここではコンストラクタJLabel(String text)を使っていますが、JLabel(Icon image)を使えば画像がつけられるようです。
以上、参考になれば幸いです。