程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Merlin的魔力: 焦點,焦點,還是焦點

Merlin的魔力: 焦點,焦點,還是焦點

編輯:關於JAVA

基於Swing的GUI還有一些遺留問題,包括如何管理焦點(哪個組件有接收鍵盤輸入的優先權),如何判斷哪個組件擁有焦點,以及如何將焦點從一個組件遍歷到下一個組件。由於Swing建立在抽象窗口工具包(AWT)之上,對組件焦點的管理便依賴於AWT中的底層焦點管理。Java平台過去的版本依賴於本地的窗口管理器來協助進行焦點管理,所以盡管有些開發者會認為焦點控制是在他們的應用程序內部進行的,而實際情況並非如此。由於對底層的本地焦點系統的依賴性,因而出現了許多平台不相容的現象。

至於Merlin,它為您提供了一個全新的、AWT級的焦點子系統。這個子系統有其優點,也有其缺點。這種新模型的出發點是創建一種能夠跨平台工作的系統,它帶有一個集中式的 KeyboardFocusManager ,用以管理活動的並且擁有焦點的窗口,以及當前焦點的屬主。缺點是,它與前面的版本之間存在一些不兼容性,從而導致有些程序在較新的版本中不能正常運行。作為一個開發者,當您創建任何新程序時,您需要清楚新的焦點遍歷方式。

新的焦點子系統相當大,在本期話題中,我們只關注其中的一項新特性―― FocusTraversalPolicy ――並向您展示如何管理單個容器中的焦點遍歷。要獲得關於其他特性的信息,參見 參考資料以鏈接到Sun的文檔以及其他一些重要的指南。

什麼,沒有接口?

我們首先來看一下 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個方法都返回一個 Component 對象。在這6個方法之中,有5個方法是抽象的,只有 getInitialComponent() 是具體的方法。

顧名思義, getDefaultComponent() 方法返回默認的組件,當相關的焦點循環根獲得焦點的時候,這個默認組件將獲得焦點。想象一下沿著某個容器進行焦點切換以及將焦點切換到那個容器裡面的一個容器中的情景。這種情況叫做 向下焦點循環(down focus cycle)。當焦點進入那個子容器時, getDefaultComponent() 需要返回應該獲得焦點的初始組件。

getInitialComponent() 方法返回當一個窗口第一次顯示時應該獲得焦點的那個初始組件。在默認情況下,該方法只返回這個窗口的默認組件――即調用 getDefaultComponent() 方法時返回的結果。

getComponentBefore() 和 getComponentAfter() 方法是成對的。根據特定的焦點循環根(比如容器)中的某個組件,這兩個方法將返回該組件之前或者之後的一個組件。通常情況下,您通過按 Shift+Tab鍵反向移動到前一個組件,或者按 Tab鍵前向移動到下一個組件,但是,不同的環境可能允許使用不同的鍵序列來利用鍵盤移動焦點。通常情況下,這些方法中的代碼都有一個大型的if-else語句塊或者一個 Map 查找。

getFirstComponent() 和 getLastComponent() 方法也是成對的。雖然在編寫 getFirstComponent() 和 getLastComponent() 方法時,您應該在腦海裡有第一/最後組件這樣一個概念,但是初始組件未必就是第一組件,這些方法允許您顯式地設置哪個組件是第一組件,哪個組件又是最後組件。

內建策略

該系統包括5種內建的焦點遍歷策略,其中後3種策略是特定於Swing的:

ContainerOrderFocusTraversalPolicy

DefaultFocusTraversalPolicy

InternalFrameFocusTraversalPolicy

SortingFocusTraversalPolicy

LayoutFocusTraversalPolicy

ContainerOrderFocusTraversalPolicy 通過使用容器固有的組件排序方式(它的 getComponents() 數組)來工作,這個順序通常就是將組件添加到容器中時的順序――除非您是用 add() 方法將組件添加到特定的位置的。除了 FocusTraversalPolicy 的這6個方法, ContainerOrderFocusTraversalPolicy 還支持隱式地將焦點向下轉移到一個容器中,並提供了一個 accept() 方法,您可以重載這個方法,以便定義對於一個要獲得焦點的組件來說什麼是可接受的選擇。

DefaultFocusTraversalPolicy 的工作方式與 ContainerOrderFocusTraversalPolicy 非常相似,只有一點不同――它依賴於AWT的對等組件來檢查一個組件是否能夠獲得焦點。對等組件的可聚焦性(focusability)是依賴於實現的,因此您又要回過頭來面對現有焦點管理子系統所存在的問題。而當您使用Swing GUI組件時,就不必擔心對等組件的可聚焦性。

InternalFrameFocusTraversalPolicy 是為 JInternalFrame 而提供的策略。假設 JInternalFrame 不是重量級的窗口,那麼當它初次顯示時需要處理一些特有的行為。

SortingFocusTraversalPolicy 允許您定義一個 Comparator 來控制焦點遍歷策略。這裡是創建一個 comparator 來處理排序方式,而不是使用大塊的 if-else語句塊或者 Map 查找 。

還有一個專門的 SortingFocusTraversalPolicy ,它提供了一個默認的Comparator: LayoutFocusTraversalPolicy 。這裡,組件是按照大小、位置和方位來排序的,而不是按照 getComponents() 數組中的順序排序的。

這裡也提供了第六個策略: LegacyGlueFocusTraversalPolicy ,但它不是公共的。當舊式的代碼使用像 JComponent.setNextFocusableComponent() 這樣的方法時,就會使用這個策略。

一個可以運行的實例

接下來,我們將創建一個簡單的實例,用這個實例來演示對我們自己的焦點遍歷策略的使用。我們將創建一個 FocusTraversalPolicy 實現,該實現帶有一個數組,並按照數組中組件的順序來決定焦點遍歷的順序。示例窗口采用的是典型的 BorderLayout 樣式,在這種樣式下,組件按照North、South、East、West和Center來定位和顯示。遍歷順序是先按順時針方向訪問外圍的組件,然後再訪問中間的組件。圖 1 展示了這個程序:

圖 1. 一個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 只是其中的一個改進。別忘了閱讀 參考資料部分中引用的Focus規范說明書,以找到該規范其他部分的內容,包括新的 KeyboardFocusManager , requestFocus() 和 requestFocusInWindow 之間的不同,以及如何調整一個組件的焦點遍歷鍵。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved