目次


Merlin の魔術

フォーカス、フォーカス、フォーカス

フォーカス管理システムが改訂されました

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: Merlin の魔術

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:Merlin の魔術

このシリーズの続きに乞うご期待。

Swing ベースの GUI にいつまでも残っている問題点には、(どのコンポーネントが優先的にキーボード入力を受け取るかの) フォーカス管理方法や、どのコンポーネントがフォーカスを得ているかの判別法、およびあるコンポーネントから次のコンポーネントへトラバースする方法などがあります。Swing は Abstract Window Toolkit (AWT) 上に構築されるため、コンポーネントのフォーカス管理は AWT の基本フォーカス管理機能に依存しています。しかし Java プラットフォームの過去のリリースでは、フォーカス管理はネイティブ・ウィンドウ・マネージャーに依存していました。したがって、フォーカス・コントロールは Java アプリケーション内で行っているものと開発者は思っていたかもしれませんが、必ずしもそうではありませんでした。基本ネイティブ・フォーカス・システムへの依存は、数多くのプラットフォームに「不整合」を生じさせました。

Merlin を使えば、AWT レベルでの全く新しいフォーカス・サブシステムを手に入れることができます。そのサブシステムには長所・短所があります。新しいモデルは、現在のフォーカス・オーナーおよびアクティブにフォーカスされた Window を一元管理できる KeyboardFocusManager を使用して、実用的なクロス・プラットフォーム・システムが作成できるようになっています。マイナス面は、下位互換性がないものがあり、新しいリリース版ではうまく動作しないプログラムがあるということです。開発者としては、新たにプログラムを作成する際は、新しいフォーカス・トラバースの振る舞いを知っておく必要があります。

新しいフォーカス・サブシステムは非常に大規模であるので、このヒントにおいては、新機能のうちの 1 つ (FocusTraversalPolicy) に焦点をあて、一つのコンテナー内でフォーカス・トラバースを管理する方法について説明いたします。残りの機能については、参考文献のリンクより、Sun のドキュメンテーションやその他の重要なガイドを参照してください。

インタフェースではない?

まずは FocusTraversalPolicy クラスを見ていきましょう。FocusTraversalPolicy クラスはインタフェースではなく、クラスです。しかし、抽象クラスであるので、サブクラス化されなければなりません。FocusTraversalPolicy クラスは、特定のフォーカス・サイクル・ルート内でトラバース順序を制御しています。フォーカス・サイクル・ルートとは focusCycleRoot プロパティーが true に設定されているコンテナーです。デフォルトでは、ウィンドウとフレームが true に設定されおり、残りのコンテナーは false に設定されていますが、これを true に設定することも可能です。プロパティーを true に設定すると、フォーカスを前後に移動しても、フォーカスは常にそのフォーカス・サイクル・ルートのサイクルのコンポーネント内にとどまることになります。

FocusTraversalPolicy クラスには 6 つのメソッドがあります。

  • getDefaultComponent(Container focusCycleRoot)
  • getInitialComponent(Window window)
  • getComponentBefore(Container focusCycleRoot, Component aComponent)
  • getComponentAfter(Container focusCycleRoot, Component aComponent)
  • getFirstComponent(Container focusCycleRoot)
  • getLastComponent(Container focusCycleRoot)

6 つのメソッドはすべて Componet オブジェクトを返します。6 つのメソッドのうち 5 つは抽象メソッドであり、唯一 getInitialComponent() が具象メソッドです。

名前が示唆しているように、getDefaultComponent() メソッドは関連するフォーカス・サイクル・ルートがフォーカスを得る際に、フォーカス対象となるデフォルト・コンポーネントを返します。コンテナー内でのコンテナー周囲のタブ移動およびコンテナーへのタブ移動を考えてみてください。これはダウン・フォーカス・サイクル と呼ばれます。フォーカスがサブコンテナーへ移動する際、getDefaultComponent() は最初にフォーカスを取得するコンポーネントを返す必要があります。

getInitialComponent() メソッドは、ウィンドウが最初に表示された時にフォーカスが設定されるコンポーネントを返します。デフォルトでは、このメソッドはウィンドウのデフォルト・コンポーネント 、つまり getDefaultComponent() 呼び出しの結果を返します。

getComponentBefore() および getComponentAfter() メソッドはペアで機能します。特定のフォーカス・サイクル・ルート (コンテナーを考えてください) のあるコンポーネントが与えられると、メソッドはこのコンポーネントの前後にフォーカスを取得するコンポーネントを返します。通常、前のコンポーネントへ移るにはShift+Tabを押し、次のコンポーネントへ移動するためにはTabを押しますが、場合によっては異なるキー・シーケンスでフォーカスを移動させることが可能です。通常、これらのメソッドは大きな 1 つの if-else ブロックあるいは Map 検索で書かれます。

getFirstComponent() および getLastComponent() メソッドもペアで機能します。論理上、getComponentBefore() および getComponentAfter() は最初/最後のコンポーネントを考慮して書かれるべきですが、これらのメソッドで最初のコンポーネントおよび最後のコンポーネントをはっきりと確定することができます。

組み込みポリシー

システムには 5 つの組み込みフォーカス・トラバース・ポリシーが含まれています。最後 3 つは Swing で特有なものです。

  • ContainerOrderFocusTraversalPolicy
  • DefaultFocusTraversalPolicy
  • InternalFrameFocusTraversalPolicy
  • SortingFocusTraversalPolicy
  • LayoutFocusTraversalPolicy

ContainerOrderFocusTraversalPolicy は、コンテナーの暗黙のコンポーネント順序 (getComponents() 配列) を使用することで機能します。この順序は特定の位置で add() コンポーネントを使用していなければ、通常はコンポーネントがコンテナーに追加される順序です。FocusTraversalPolicy の 6 つのメソッドに加え、ContainerOrderFocusTraversalPolicy もコンテナーへのフォーカス・ダウン転送、およびフォーカス設定可能なコンポーネントを定義するためにオーバーライドすることができる accept() メソッドの提供をサポートしています。

DefaultFocusTraversalPolicy は、1 つの例外を除いて ContainerOrderFocusTraversalPolicy と全く同じように動作します。1 つの例外とは、コンポーネントがフォーカス可能であるかを確認する AWT のピア・コンポーネントです。ピアがフォーカス可能かどうかは実装で決まるので、既存のフォーカス管理サブシステムの問題がぶり返します。しかし、Swing GUI コンポーネントを用いれば、ピアがフォーカス可能であるかについて心配する必要はありません。

InternalFrameFocusTraversalPolicyJInternalFrame 用のポリシーです。JInternalFrame がヘビー・ウエイト・ウィンドウでないならば、最初に表示された時に処理するべき振る舞いが必要です。

SortingFocusTraversalPolicy によって、フォーカス・トラバース・ポリシーを制御する Comparator を定義することができます。順序を処理するには、深い if-else ブロックあるいは Map 検索の代わりに Comparator を作成してください。

さらにデフォルトで Comparator を備えている専門化した SortingFocusTraversalPolicyLayoutFocusTraversalPolicy があります。ここではコンポーネントは、getComponents() 配列の順序ではなく、サイズ、位置、方向に基づいてソートされます。

6 番めのポリシーのLegacyGlueFocusTraversalPolicyは利用可能ですが、public ではありません。このポリシーは、古いコードが JComponent.setNextFocusableComponent() のようなものを使用する場合に使用されます。

役に立つ例

では、フォーカス・トラバース・ポリシーの使用を実証する簡単な例を作ってみましょう。配列を取得して、コンポーネントのトラバース順序を決定する FocusTraversalPolicy を実装します。サンプルのウィンドウは、コンポーネントが東西南北および中心に位置していることを示す簡単な BorderLayout 例です。トラバース順序は、中心へ移動する前に外側を時計回りに回ります。図 1 はそのプログラムです。

図 1. BorderLayout ウィンドウ
BorderLayout ウィンドウ

リスト 1 は時計回りのトラバース・リストです。

リスト 1. 時計回りのトラバース
import java.awt.*;
import javax.swing.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class BorderFocus {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Focus Cycling");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Container contentPane = frame.getContentPane();
    JButton north = new JButton("North");
    contentPane.add(north, BorderLayout.NORTH);
    JButton south = new JButton("South");
    contentPane.add(south, BorderLayout.SOUTH);
    JButton east = new JButton("East");
    contentPane.add(east, BorderLayout.EAST);
    JButton west = new JButton("West");
    contentPane.add(west, BorderLayout.WEST);
    JButton center = new JButton("Center");
    contentPane.add(center, BorderLayout.CENTER);
    contentPane.setFocusable(false);
    final Component order[] = new Component[] {north, east, south, west, center};
    FocusTraversalPolicy policy = new FocusTraversalPolicy() {
      List list = Arrays.asList(order);
      public Component getFirstComponent(Container focusCycleRoot) {
        return order[0];
      }
      public Component getLastComponent(Container focusCycleRoot) {
        return order[order.length-1];
      }
      public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
        int index = list.indexOf(aComponent);
        return order[(index + 1) % order.length];
      }
      public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
        int index = list.indexOf(aComponent);
        return order[(index - 1 + order.length) % order.length];
      }
      public Component getDefaultComponent(Container focusCycleRoot) {
        return order[0];
      }
    };
    frame.setFocusTraversalPolicy(policy);
    frame.pack();
    frame.show();
   }
}

追加の機能試験として、SortingFocusTraversalPolicy を使用して例を書き直てみてください。コンテナーを通じて前後のトラバースを試みるようにしてください。

要約

Java プラットフォーム 1.4 リリース版のフォーカス・サブシステムは、過去のリリース版における多くのフォーカス関連の問題点を改善しました。FocusTraversalPolicy はまさにその改善点の 1 つです。新しい KeyboardFocusManager や、requestFocus()requestFocusInWindow の違い、およびコンポーネントのフォーカス・トラバース・キーの調節などを含むその他の仕様に関しては参考文献セクションにおけるフォーカスの仕様を一読するようにしてください。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=218830
ArticleTitle=Merlin の魔術: フォーカス、フォーカス、フォーカス
publish-date=07152003