JSpinner - 単純なシーケンスコンテナ

このドキュメントでは、新しい Swing コンポーネントである JSpinner について説明します。

もっとも多く作成の要求があった Swing コンポーネントの 1 つに、スピナがあります。これは、ユーザーが順序付けられた集合から数値またはオブジェクトを選択できるようにする単一行の入力フィールドです。スピナには通常、選択可能な値を段階的に表示するための一組の小さな矢印型ボタンが提供され、上向きと下向きの矢印キーでも値を循環して表示できます。ユーザーは、有効な値をスピナに直接入力することもできます。コンボボックスにも似たような機能がありますが、重要なデータがわかりにくくなる可能性のあるドロップダウンリストを必要としないスピナのほうが、ときには好まれます。

この変更に関連するバグ追跡レポート: 4290529.

スピナのカタログ

スピナは、最近の GUI で一般的なものです。次に、いくつかの一般的な Look & Feel と OpenWindows からの例を示します。
Windows スピナ CDE/Motif スピナ JLF スピナ Mac Aqua スピナ OpenWindows スピナ
Windows CDE/Motif JLF Mac Aqua OpenWindows

これらのスピナはすべて、非常に単純な動作を行います。スピナにフォーカスがあるときに矢印ボタンをクリックすると、フィールドの値が変わります。キーボードの上向き矢印キーと下向き矢印キーでも同じ結果になります。アプリケーションによっては、スピナの値が上限または下限に達すると、上向きまたは下向き矢印ボタンが無効になったり、その値が反対の極限値にリセットされたりします。

JSpinnerSpinnerModelSpinnerListModel

JSpinner クラスは、3 つの子を管理する単純な Swing コンテナです。子とは、2 つの矢印ボタンと、スピナの値を表示するための「エディタ」と呼ばれる 1 つのコンポーネントです。スピナに表示される値は、SpinnerModel と呼ばれるオブジェクトのシーケンスから構成されるモデルによってカプセル化されます。

public interface SpinnerModel {
    Object getValue();
    void setValue(Object);
    Object getNextValue();
    Object getPreviousValue();
    void addChangeListener(ChangeListener x);
    void removeChangeListener(ChangeListener x);}

SpinnerModel インタフェースは、ListModel と似ており、両方とも値のシーケンスを表現しますが、重要な相違点がいくつかあります。

JSpinner とそのモデルとの関係は単純です。エディタコンポーネントは ChangeListener 経由でモデルを監視し、SpinnerModel.getValue() によって返されたオブジェクトを常に表示します。上向きおよび下向きの矢印ボタンは、それぞれ setValue(getNextValue()) または setValue(getPreviousValue()) を呼び出すことで値を更新します。シーケンスの最後または先頭に達したときに getNextValue および getPreviousValue メソッドは null を返すので、矢印ボタンのアクションでは、モデルの値を更新する前に null をチェックする必要があります。エディタがある種の書き込み可能なフィールドの場合は、モデルに定義された制約を尊重すること、または無効な値に対して setValue によってスローされた IllegalArgumentException を処理することが、エディタの役割になります。

SpinnerListModel では、java.util.List およびオブジェクト配列という、2 つの一般的な可変のシーケンス型がサポートされます。デフォルトのロケールでユーザーが曜日を選択できるようにする JSpinner を作成する例は、次のようになります。

    String[] days = new DateFormatSymbols().getWeekdays();
    SpinnerModel model = new SpinnerListModel(days);
    JSpinner spinner = new JSpinner(model);

スピナの model プロパティーを初期化することに加えて、これらのコンストラクタが、SpinnerModelvalue を表示するエディタコンポーネントを作成します。また、コンストラクタを使用して value を変更できます。デフォルトでは、このために protected JSpinner.createEditor メソッドが使用され、このメソッドが、モデルを表示するために構成された JFormattedTextField を作成します。

スピナの現在値を検出または初期化するには、モデルの value プロパティーを使用するか、または、モデルに委譲されたばかりの、便利な JSpinner value プロパティーを使用することができます。たとえば、上の例で構成されたスピナを使用すると、次の 2 つの文は同等です。

    String selectedDay = spinner.getModel().getValue().toString();
    String selectedDay = spinner.getValue().toString();

スピナの値の設定は似ています。モデルがサポートしないオブジェクトに SpinnerModel の値を設定しようとすると、IllegalArgumentException がスローされます。

日付と数値は、スピナのコンポーネントにもっとも一般的に適用される 2つです。これらの型のスピナ化を単純にするために、SpinnerDateModelSpinnerNumberModel の、2 つの SpinnerModel 実装クラスが追加されました。

SpinnerDateModel

スピナのもっとも一般的な使用法の 1 つが、編集可能な日付をコンパクトに提示することです。次に、ユーザーが完全にローカライズされた日付を入力できる JSpinner の作成例を示します。

    SpinnerDateModel model = new SpinnerDateModel();
    JSpinner spinner = new JSpinner(model);
    Date value = model.getDate();

この例で、JSpinner コンストラクタは、日付を編集する JFormattedTextField エディタを作成し、ChangeListenerSpinnerDateModel に追加して、エディタとモデルの同期を保ちます。

ここで、SpinnerDateModel API を示します。この API には、startend、および stepSize の 3 つの新しい読み取り/書き込み両用プロパティーと、Date にキャストする値を返す読み取り専用の date プロパティーが追加されました。

public class SpinnerDateModel extends AbstractSpinnerModel {
    public SpinnerDateModel(Date value, Comparable start, Comparable end, int stepSize)
    public SpinnerDateModel()
    public void setStart(Comparable start)
    public Comparable getStart()
    public void setEnd(Comparable end)
    public Comparable getEnd()
    public Object getNextValue()
    public Object getPreviousValue() 
    public Date getDate()
    public Object getValue()
    public void setValue(Object value)}

startDate および endDate プロパティーは、上限または下限がないことを示すために、null になる可能性もあります。引数を指定しない SpinnerDateModel コンストラクタの場合は、開始日と終了日の両方を null に初期化し、モデルの最初の日付は現在の日付です。

stepSize プロパティーの値は、Calendar 内でフィールドを指定する java.util.Calendar 定数の 1 つにする必要があります。getNextValue および getPreviousValue メソッドは、stepSize プロパティーの値の分だけ日付を前方または後方に変更します。たとえば、stepSizeCalendar.DAY_OF_WEEK の場合は、nextValue で現在の value より 24 時間後の Date を作成し、previousValue で 24 時間前の Date を作成します。

stepSize に有効な値は、次のとおりです。

デフォルトの SpinnerDateModel editor は、テキストのカーソル位置に基づいて stepSize プロパティーを調節します。たとえば、カーソルがエディタの month サブフィールドに移動した場合は、incrementSizeCalendar.DAY_OF_MONTH に変更されます。

SpinnerNumberModel

スピナはしばしば、気温から株価までのあらゆる数値を表す、編集可能な整数および実数を提示するために使用されます。SpinnerNumberModel では、Byte から Double までの基本的なすべての Java の Number 型を基本的にサポートします。

初期値を 500.0 にして、0.0 から 1000.0 の間で 1/8 の実数の倍数をユーザーが選択できるようにするスピナを作成するには、次のようにコーディングします。

    SpinnerNumberModel model = new SpinnerNumberModel(500.0, 0.0, 1000.0, 0.625);
    JSpinner spinner = new JSpinner(model);
    double value = model.getNumber().doubleValue();

この例で、JSpinner コンストラクタは、実数を編集するための JFormattedTextField エディタを作成し、ChangeListenerSpinnerDateModel に追加して、エディタとモデルの同期を保っています。

ここで、SpinnerNumberModel API について簡単にまとめます。この API には、minimummaximum、および stepSize の 3 つの新しい読み取り/書き込み両用プロパティーと、Number にキャストする value を返す読み取り専用の number プロパティーが追加されました。

public class SpinnerNumberModel extends AbstractSpinnerModel {
    public SpinnerNumberModel(Number value, Comparable minimum, Comparable maximum, Number stepSize)
    public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)
    public SpinnerNumberModel(double value, double minimum, double maximum, double stepSize)
    public SpinnerNumberModel()
    public void setMinimum(Comparable minimum)
    public Comparable getMinimum()
    public void setMaximum(Comparable maximum)
    public Comparable getMaximum()
    public void setStepSize(Number stepSize)
    public Number getStepSize()
    public Object getNextValue()
    public Object getPreviousValue()
    public Number getNumber()
    public Object getValue()
    public void setValue(Object value)}

SpinnerDateModel に関しては、minimum および maximum プロパティーが、それより大きな値または小さな値がないことを示すために、null になる可能性もあります。stepSize プロパティーには、nextValue または previousValue を計算するために value と加算または減算する値を指定します。

スピナ API のまとめ

スピナのサポートにあたって、次の 6 つのクラスと 1 つのインタフェース (SpinnerModel) が javax.swing パッケージに追加されました。

また、SpinnerUIjavax.swing.plaf パッケージに、BasicSpinnerUIjavax.swing.plaf.basic パッケージに追加されました。