WisdomSoft - for your serial experiences.

6.7 スライダー

JSlider クラスによるスライダーは、ある一定の最小値と最大値からなる範囲から 1 点を選択します。

6.7.1 つまみによる入力

ユーザーに音量を設定させたり、画面のコントラストや明るさを設定させるコンポーネントを実装する場合、スピナーを使って数値を入力させるという方法が考えられます。確かに、数値モデルのスピナーを使えば安全に数値を得られますが、これはデジタルナ方法で優しいユーザーインタフェースとは言えません。ユーザーにとっては、アナログ的なスライダーを使ったほうが直観的に操作できるでしょう。

スライダーは進捗バーのように最小値と最大値を持ち、その範囲で値を設定するコンポーネントですが、現在の値をユーザーに入力させるためのコンポーネントである点で役割が異なります。スライダーは、一定範囲の数値をユーザーに入力してもらうためのコンポーネントと考えることができます。

スライダーコンポーネントを実装するのは javax.swing.JSlider クラスです。

javax.swing.JSlider クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.JSlider
public class JSlider extends JComponent implements SwingConstants, Accessible

このクラスの基本的な制御方法は進捗バーに良く似ています。何よりも、このクラスのデータモデルは進捗バーと同じ BoundedRangeModel インタフェースを使っています。

表1 JSlider クラスのコンストラクタとメソッド(抜粋)
コンストラクタ 解説
public JSlider() 範囲が 0 ~ 100、初期値が 50 の水平スライダを作成する。
public JSlider(int orientation) 方向を指定して、範囲が 0 ~ 100、初期値が 50 のスライダを作成する。
public JSlider(int min, int max) 指定された最小値および最大値を使って、水平スライダを作成する。
public JSlider(int min, int max, int value) 最小値、最大値、および初期値を指定して、水平スライダを作成する。
public JSlider(int orientation, int min, int max, int value) 方向、最小値、最大値、および初期値を指定して、スライダを作成する。
public JSlider(BoundedRangeModel brm) モデルを使って水平スライダを作成する。
メソッド
public void addChangeListener(ChangeListener l) スライダに ChangeListener を追加する。
public void removeChangeListener(ChangeListener l) スライダに設定されている指定した ChangeListener を削除する。
public ChangeListener[] getChangeListeners() 設定されているすべての ChangeListener の配列を返す。
public void setValue(int n) 現在値を設定する。
public int getValue() 現在値を返す。
public void setExtent(int extent) ノブによって「カバー」される範囲のサイズを設定する。
public int getExtent() ノブによって「カバー」される値の範囲を返す。
public void setMinimum(int minimum) 最小値を設定する。
public int getMinimum() 最小値を返す。
public void setMaximum(int maximum) 最大値を設定する。
public int getMaximum() 最大値を返す。
public void setModel(BoundedRangeModel newModel) データモデルを設定する。
public BoundedRangeModel getModel() データモデルを返す。
public void setOrientation(int orientation ) スクロールバーの方向を設定する。
public int getOrientation() スライダの方向を返す。

表1は JSlider クラスのコンストラクタと、データモデルやコンストラクタに関連する基本的なプロパティに関係するメソッドです。その多くのメソッドが JProgressBar クラスのメソッドと同じであることがわかります。

スライダーのつまみをユーザーによって移動させられると、コンポーネントの現在の値が変更されます。その結果 addChangeListener() メソッドから追加された ChangeListener オブジェクトのメソッドが呼び出されるので、そのタイミングで必要な処理を行います。

setExtent() メソッドgetExtent() メソッドは進捗バーにはなかった概念ですが、BoundedRangeModel インタフェースによるデータモデルで内側の幅として定義されていたプロパティです。

setOrientation() メソッドや、コンストラクタの orientation 引数から SwingConstants インタフェースで定められている VERTICAL または HORIZONTAL 定数のいずれかを指定することで、スライダーの方向を設定することができます。

コード1
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;

public class Test extends JFrame implements ChangeListener {
	public static void main(String args[]) {
		JFrame win = new Test();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.show();
	}

	private JSlider sliderR = new JSlider(0 , 255 , 0);
	private JSlider sliderG = new JSlider(0 , 255 , 0);
	private JSlider sliderB = new JSlider(0 , 255 , 0);
	private JLabel label = new JLabel("つまみを動かしてください");
	public Test() {
		label.setFont(new Font("Serif" , Font.BOLD , 25));

		sliderR.addChangeListener(this);
		sliderG.addChangeListener(this);
		sliderB.addChangeListener(this);

		getContentPane().setLayout(new GridLayout(4 , 1));
		getContentPane().add(sliderR);
		getContentPane().add(sliderG);
		getContentPane().add(sliderB);
		getContentPane().add(label);
	}

	public void stateChanged(ChangeEvent e) {
		Color color = new Color(
			sliderR.getValue() ,
			sliderG.getValue() ,
			sliderB.getValue()
		);
		label.setForeground(color);
		label.setText(color.toString());
	}
}
実行結果
コード1 実行結果

コード1は、3 つのスライダーを使って色を入力するプログラムです。一般的なユーザーにとっては、3 バイトの RGB を数値で入力する方法よりも、このようにつまみを動かして値を指定するアナログな方法が好まれるでしょう。

6.7.2 目盛り付きスライダー

スライダーコンポーネントは、デフォルトではバーとつまみしか表示されませんが、外観もカスタマイズすることができます。特に重要なのは、スライダーに目盛りを付けられることです。デフォルトのスライダーでは、スライダーのつまみの位置がどの程度の値を表しているのかを判断することが難しいでしょう。この問題は、スライダーに目盛りを付けることが解決することができます。

表2 JSlider クラスの目盛りに関するメソッド
メソッド 解説
public void setPaintTicks(boolean b) 目盛りが表示されるかどうかを指定する。
public boolean getPaintTicks() 目盛りが表示されるかどうかを示す。
public void setMajorTickSpacing(int n) 大目盛りの間隔を設定する。
public int getMajorTickSpacing() 大目盛りの間隔を返す。
public void setMinorTickSpacing(int n) 小目盛りの間隔を設定する。
public int getMinorTickSpacing() 小目盛りの間隔を返す。

まず、デフォルトでは目盛りが表示されないように設定されているため、setPaintTicks() メソッドを使って true を指定します。これで目盛りが表示されるようになるのですが、デフォルトでは目盛りの間隔が設定されていないため、目盛りがスライダーの最小値と最大値に対してどの程度の間隔で表示するかを指定します。例えば、最小値が 0、最大値が 100 の設定で目盛りの間隔を 10 に設定した場合は、0、10、20、30、40 … の位置に目盛りが表示されるようになります。

目盛りには大目盛りと小目盛りに分けられています。これは、単純に大目盛りが比較的長い線で表現される目盛りで、大台として区切られる数値に割り当てます。定規で例えるならば 1 センチや 1 メートルなどが大台として考えることができます。小目盛りは大目盛りの間に配置する小さな線です。定規の 1 ミリや 5 ミリなどが小さな目盛りとなるでしょう。

コード2
import javax.swing.*;

public class Test  {
	public static void main(String args[]) {
		JSlider slider = new JSlider();
		slider.setPaintTicks(true);
		slider.setMinorTickSpacing(5);
		slider.setMajorTickSpacing(10);

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(slider);
		win.show();
	}
}
実行結果
コード2 実行結果

コード2は目盛りが表示されるスライダーを作成しています。まず、setPaintTicks() メソッドを呼び出して true を設定し、目盛りを表示するように設定しています。しかし、これだけでは目盛りの間隔が設定されていないので表示されない可能性があります。そこで、setMinorTickSpacing() メソッド setMajorTickSpacing() メソッドで、小目盛りと大目盛りを設定しています。

6.7.3 ラベル付きスライダー

目盛りをつけることで、つまみの位置と間隔をより正確にユーザーに知らせることができましたが、これだけでもユーザーは数値が見えません。数値を隠蔽しても良い入力であればこれでも問題はありませんが、最小値や最大値、そして現在の値を具体的に入力してもらうことが目的であれば、現在の値をユーザーにより効率的に知らせる必要があります。

そこで、スライダーにラベルを付属することができます。ラベルを表示するように設定すると、スライダーは一定間隔ごとに実際の数値を表示するようになります。ラベルが表示される間隔は、デフォルトで大目盛りに合わせられます。

スライダーのデフォルトではラベルは表示されないように設定されているため、ラベルを表示するように setPaintLabels() メソッドで設定する必要があります。現在、ラベルを表示しているかどうかは getPaintLabels() メソッドを使います。 

JSlider クラス setPaintLabels() メソッド
public void setPaintLabels(boolean b)
JSlider クラス getPaintLabels() メソッド
public boolean getPaintLabels()

setPaintLabels() メソッドに true を設定すれば、スライダーにラベルが表示されるようになります。目盛りと併用して表示すれば、スライダーのどちら側の方向が値の増加を意味するのかをユーザーが確実に認識することができます。

図1 ラベル付きのスライダー
図1 ラベル付きのスライダー

図1は、コード2のスライダーに setPaintLabels(ture) を設定した実行結果です。図を見てわかるように、大目盛りの下部に数値が表示されています。

スライダーのラベルはカスタマイズ可能な設計になっているため、Swing コンポーネントであれば特定の目盛り位置に自由に表示することができます。スライダーのラベルを変更するには setLabelTable() メソッドを使って、表示するコンポーネントと、ラベルを表示するスライダーの位置を関連付けた Dictionary オブジェクトを設定します。取得には getLabelTable() メソッドを使います。

JSlider クラス setLabelTable() メソッド
public void setLabelTable(Dictionary labels)
JSlider クラス getLabelTable() メソッド
public Dictionary getLabelTable()

最新の仕様では Dictionary クラスを使うことは推奨されておらず、その代わりに Map インタフェースを使ってキーと値の関連付けを抽象化するように支持されています。そのため、直接 Dictionary クラスを使うのではなく、Dictionary を継承し Map インタフェースを実装する Hashtable クラスを使う方法が賢い判断だと考えられます。

Hashtable を使ってスライダーのラベルを設定するには、put() メソッドでコンポーネントを表示する位置を表す Integer オブジェクトと JComponent をマップに追加します。このマップを Dictionary オブジェクトとして setLabelTable() メソッドに追加すると、Integer が表す値と等しいスライダーの位置に、関連付けられているコンポーネントを表示します。

コード3
import java.util.*;
import javax.swing.*;

public class Test  {
	public static void main(String args[]) {
		Icon icon1 = new ImageIcon("icon1.jpg");
		Icon icon2 = new ImageIcon("icon2.jpg");
		Icon icon3 = new ImageIcon("icon3.jpg");

		Hashtable labels = new Hashtable();
		labels.put(new Integer(0) , new JLabel(icon3));
		labels.put(new Integer(50) , new JLabel(icon1));
		labels.put(new Integer(100) , new JLabel(icon2));
		
		JSlider slider = new JSlider();
		slider.setPaintTicks(true);
		slider.setMajorTickSpacing(10);

		slider.setPaintLabels(true);
		slider.setLabelTable(labels);

		JFrame win = new JFrame();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(slider);
		win.show();
	}
}
実行結果
コード3 実行結果

コード3は、標準のラベルの代わりにアイコンを 0、50、及び 100 の値に表示するプログラムです。プログラムでは、まず Hashtable オブジェクトを生成し、0、50、100 の値を表す Integer オブジェクトとコンポーネントを関連付けて保存しています。このハッシュテーブルを setLabelTable() メソッドに渡すことで、値に関連付けられたコンポーネントが表示されているというわけです。このプログラムのコンポーネントには、アイコンを表示する JLabel を採用しています。