用JIcon裝飾你的界面

如果說Swing Icon接口很簡單,那麼其強大的功能就更令人驚訝了。通過它,你可以用程序創建圖標或在圖標上進行各種操作、可以用不同的方式合併圖標、或在現有組件中很容易地顯示圖標。讓我們來開發一個組件,它可以讓你顯示一組圖標,並可以在圖標列表的各個狀態中循環。在需要管理有很多元素的用戶界面時(出於編輯或提供信息的目的,這些元素需要可視化地反映狀態),該組件尤其有用。我們可以提供很多的例子,包括:一個日誌文件條目列表,我們可以用一個彩色圖標來反映每個條目的重要程度;或一個表,我們可以用一個列來反映每行的狀態,包括可編輯狀態、正常狀態、包含在內(included )或不包含在內(excluded)的狀態。

我們就不花時間來講述如何將JIcon用做一個表、列表、樹狀單元繪製器(cell renderer)或編輯器了。可以說實現該功能並不難。如果我們要實現很多功能,本文的篇幅可能並不允許我們講述太多。因此,我們將重點講述如何構建一個可以在Icon元素中循環的簡單的組件,如何運用一個你可以很容易偵聽的模式來查看事件的變化或處理單元編輯器和繪製器組件。

在開發這個項目的過程中,我實現了幾個Icon工具類(utility class),你可以下載它們。CompondIcon類可以讓你合併任何兩個圖標,形式可以是並排的,或者一個圖標在另一個圖標的上面。DecoratorIcon可以讓你用一個圖標覆蓋另一個圖標,就像我們在快捷裝飾的例子中所做的那樣(見圖1)。FilterIcon可以讓你在繪製圖標前,將一個圖像過濾器(image filter)用於圖標。在繼續講述前,讓我們先來簡要地看看每個類的功能是怎樣的。Icon接口(Swing集合的一部分)是非常簡單的:public interface Icon
{
   public int getIconHeight(); 
   public int getIconWidth();
   public void paintIcon(
      Component c, Graphics g, 
      int x, int y);
}

只要調用組件知道圖標的寬度和高度,它就可以很好地管理版面佈局了。在繪製圖標時,我們調用了paintIcon()方法。大多數情況下,你需要做的就是運用繪圖環境(graphic context),在指定的x、y座標上畫圖,將圖像控制在由getIconWidth()和getIconHeight()方法返回的寬度和高度範圍內。有時侯,可以訪問繪製圖標所位於的組件是很有用的;例如,我們可以用Component的getBackground()方法來取得背景顏色。

通過這個接口,我們可以很容易地從圖像構建圖標。實際上,Swing爲此提供了ImageIcon。我們也可以很容易地在瞬間繪製圖標。我提供了一個CheckBoxIcon對此加以說明,它可以繪製一個方框,該方框可以是空的、可以包含一個複選標記、或裏面畫有一個X標記。我們將用這些圖標來說明多個狀態。你可以在圖1左邊底部的方框中看到它的實際應用情況。如果你點擊這個圖標,它就會轉換到每個可能出現的狀態。在每個狀態中,都運用了CheckBoxIcon的一個單獨的實例。JIcon組件可以管理狀態。

裝飾圖標
讓我們很快地來看看很像容器的這三種圖標的實現情況吧。它們可以讓你組合、覆蓋或調整圖標,並將它們看做是單獨的Icon實例。例如,你可以用CompoundIcon將多個圖標放在一個通常只處理一個圖標(如Jlabel或Jbutton)的Swing組件中。

DecoratedIcon類可以讓你用一個圖標覆蓋另一個圖標。該功能可以讓你用一個較小的圖標在九個可能的位置上來裝飾一個大的圖標:垂直方向上的TOP、BOTTOM或CENTER,以及水平方向上的LEFT、RIGHT或CENTER。構造器需要兩個圖標實例,並需要垂直對齊和水平對齊。我們會查看對齊是否有效,如果對齊不好,或裝飾圖標比被裝飾圖標大,你就會得到IllegalArgumentException異常。我們用GetIconWidth()和getIconHeight()來返回被裝飾圖標(兩個圖標中較大的那個)的寬度和高度。然後用paintIcon()方法先繪製被裝飾的圖標,再在指定的位置上繪製裝飾圖標。

FilterIcon類可以將一個ImageFilter用於一個指定的Icon實例。這是在構造器中實現的,另外,它還緩存了結果圖像,以便在繪圖時使用。我選用了ImageFilter,它是老的AWT的一部分,因爲通過它,我們也可以用Java 2D最近提供的BufferedImageFiler(BuffereredImageFilter實現了ImageFilter接口)。接下來的工作就是返回所創建的圖像的寬度和高度了,並在調用paintIcon()方法時,在x、y位置上畫圖。
我剛剛講述的三個類可以讓你在現有的Swing組件中顯示合併的、被裝飾的和被過濾的圖標。我們可以嵌套這些Icon的實現形式,從而根據你的需要來組合或調整圖標。遺憾的是,沒有一個Swing組件可以提供多個狀態視圖,因此下面就讓我們來看看如何實現這樣一個組件,它可以讓你處理多個圖標來顯示與你的應用程序相關的狀態。

我們來看看所實現的JIcon中的兩個重要的類,IconListModel和JIcon類本身IconListModel是個接口,它可以存儲多個用於JIcon的圖標。它可以以列表中當前索引的形式來管理數量不定的一列圖標實例和當前選擇的狀態。要實現一個IconListModel接口,我們也必須將事件發送到任何註冊的ChangeListener以便反映狀態或Icon列表內容的變化,從而使視圖(如JIcon)可以立即反映這些變化。注意,IconListModel擴展了Icon接口:public interface IconListModel
   extends Icon
{
   public int getIconCount();
   public int getCurrentIndex();
   public Icon getIcon(int index);
   public void setIcons(
      Icon[] icon);
   public void addIcon(Icon icon);
   public void removeIcon(
      Icon icon);
   public void setCurrentIcon(
      int index);
   public void addChangeListener(
      ChangeListener listener);
   public void removeChangeListener(
   ChangeListener listener);
}

 


通過這個接口,我們就可以很容易地看到需要什麼步驟來實現它了,即IconList的作用(見列表1)。我們運用了兩個ArrayList實例:一個用來管理ChangeListener實例的列表,另一個用來管理Icon實例的列表。我們將寬度和高度值緩存起來,當調用Icon的getIconWidth()和getIconHeight()方法時,返回這些值。寬度和高度是作爲圖標列表的最大值通過calculateSize()方法計算的,當列表內容發生改變時,就會調用該方法。

我們也保留當前圖標和索引值來反映當前選擇的狀態。當paintIcon()方法在Icon接口被調用時,就會繪製所選擇的圖標。如果有必要,我們還提供了一個setIcons()方法來設置整個列表的內容。兩個構造器中的第二個構造器在初試化時用該方法來設置列表。另一個構造器是空的,需要在一個單獨的步驟中填充列表。調用addIcon()或removeIcon()方法時,也會調用calculateSize()和fireChangeEvent()方法。我們運用了一個實用的方法(capIndex())來確保當前索引值永遠不會超過列表的大小。FireChangeEvent()、addChangeListener()和removeChangeListener()方法管理註冊的ChangeListener的列表。

突出焦點
缺省情況下,在JIcon類中,我們用一個單獨的像素EmptyBorder環繞在圖標周圍,這樣我們就可以用一個藍色的LineBorder來重點顯示圖標了。你可以調用setFocusable()方法來激活該行爲。缺省情況下,我們可以處理鼠標事件(mouse event),但不接受聚焦顯示。通過三個構造器我們可以創建一個空的JIcon、帶有一個單獨的圖標的JIcon或帶有一個初始Icon列表的JIcon。在每種情況中,我們都用initComponent()方法來設置可選的美學效果和監聽器(listener)。

JIcon實現了四個監聽器:ChangeListener、FocusListener、MouseListener和KeyListener。觸發ChangeListener事件可以重畫視圖。我們用了一個叫做resetSize()的方法,因爲模式的變化會影響由組件返回的首選的、最小的尺寸。實際的尺寸是由IconList緩存的,當圖標被添加或刪除時,IconList就會計算實際尺寸,因此首選的、最小的尺寸就會根據當前的getIconWidth()和getIconHeight()方法值被更新,並根據現有邊界進行調整。

如果有必要,可以用FocusListener的focusGained()和focusLost()方法來改變邊界。我們可以通過調用標準的isFocusable()方法來查看是否可以運用該行爲。注意,Java 1.4對setFocusable()和isFocusable()方法都有介紹。如果你需要向後兼容(backward compatible),你必須修改代碼來運用isFocusTraversible()方法。

KeyListener和MouseListener方法可以用來響應用戶的輸入。對於KeyListener來說,我們可以通過調用nextIcon()方法對空格鍵作出響應。MousePressed事件的作用是一樣的,但先要有個焦點(focus)。在這兩種情況中,我們都調用重畫方法來刷新視圖。我在實現的例子中已經提供了nextIcon()和prevIcon()方法,雖然我們只用了nextIcon()方法。

NextIcon()方法從模式中提取當前索引值,並以增值(increment)順序取下一個索引,如果當前索引位於列表的最後,則運用nextIcon()會返回到第一個值。PrevIcon()方法的功能正好相反,它以遞減(decrement)的順序取列表中的前一個值,如果當前索引是列表中第一個值,則prevIcon()會取列表的最後一個值。在我實現的這個例子中,我們沒有用prevIcon()方法,但是你可能想讓用戶通過後退鍵和鼠標右鍵在列表中進行後退(move backward)操作。

以上就是有關JIcon類的簡要內容了,另外還有accessor,它們可以讓你get和set模式、在列表中添加、刪除和設置圖標;還有setBorder方法,如果邊界不爲空,我們可以覆蓋該方法來調用resetSize方法。SetModel()方法可以在用新模式註冊前註銷以前的任何模式。實際效果是,你可以直接通過程序設置模式的索引值、通過用戶鍵入或鼠標事件,用一個組件來循環一列圖標。

JIcon組件填補了Swing的空白,我們可以用它來管理圖標列表,這些圖標可以以一種可視的和可編輯的方式來反映狀態。JIcon可以給我們開發的很多複合Icon實例帶來好處,使我們可以實現更復雜的視圖而不會增加複雜性,但這些實現方法也可以用於運用Icon接口實例的任何組件,如JLabel和JButton。JIcon可以管理狀態,但IconListModel也可以實現Icon接口,並可以用在其它組件中。你在尋找可以提高可用性的方法時,我希望這些技術和例子會對你有所幫助。


關於作者:
Claude Duguay已有20多年的軟件開發經驗了。他熱衷於Java技術,經常閱讀這方面的書,並經常撰寫這方面的文章。你可以通過[email protected]與他聯繫。



 


如果說Swing Icon接口很簡單,那麼其強大的功能就更令人驚訝了。通過它,你可以用程序創建圖標或在圖標上進行各種操作、可以用不同的方式合併圖標、或在現有組件中很容易地顯示圖標。讓我們來開發一個組件,它可以讓你顯示一組圖標,並可以在圖標列表的各個狀態中循環。在需要管理有很多元素的用戶界面時(出於編輯或提供信息的目的,這些元素需要可視化地反映狀態),該組件尤其有用。我們可以提供很多的例子,包括:一個日誌文件條目列表,我們可以用一個彩色圖標來反映每個條目的重要程度;或一個表,我們可以用一個列來反映每行的狀態,包括可編輯狀態、正常狀態、包含在內(included )或不包含在內(excluded)的狀態。 我們就不花時間來講述如何將JIcon用做一個表、列表、樹狀單元繪製器(cell renderer)或編輯器了。可以說實現該功能並不難。如果我們要實現很多功能,本文的篇幅可能並不允許我們講述太多。因此,我們將重點講述如何構建一個可以在Icon元素中循環的簡單的組件,如何運用一個你可以很容易偵聽的模式來查看事件的變化或處理單元編輯器和繪製器組件。 在開發這個項目的過程中,我實現了幾個Icon工具類(utility class),你可以下載它們。CompondIcon類可以讓你合併任何兩個圖標,形式可以是並排的,或者一個圖標在另一個圖標的上面。DecoratorIcon可以讓你用一個圖標覆蓋另一個圖標,就像我們在快捷裝飾的例子中所做的那樣(見圖1)。FilterIcon可以讓你在繪製圖標前,將一個圖像過濾器(image filter)用於圖標。在繼續講述前,讓我們先來簡要地看看每個類的功能是怎樣的。Icon接口(Swing集合的一部分)是非常簡單的:public interface Icon { public int getIconHeight(); public int getIconWidth(); public void paintIcon( Component c, Graphics g, int x, int y); } 1212Y4025440Z1E21.gif 只要調用組件知道圖標的寬度和高度,它就可以很好地管理版面佈局了。在繪製圖標時,我們調用了paintIcon()方法。大多數情況下,你需要做的就是運用繪圖環境(graphic context),在指定的x、y座標上畫圖,將圖像控制在由getIconWidth()和getIconHeight()方法返回的寬度和高度範圍內。有時侯,可以訪問繪製圖標所位於的組件是很有用的;例如,我們可以用Component的getBackground()方法來取得背景顏色。 通過這個接口,我們可以很容易地從圖像構建圖標。實際上,Swing爲此提供了ImageIcon。我們也可以很容易地在瞬間繪製圖標。我提供了一個CheckBoxIcon對此加以說明,它可以繪製一個方框,該方框可以是空的、可以包含一個複選標記、或裏面畫有一個X標記。我們將用這些圖標來說明多個狀態。你可以在圖1左邊底部的方框中看到它的實際應用情況。如果你點擊這個圖標,它就會轉換到每個可能出現的狀態。在每個狀態中,都運用了CheckBoxIcon的一個單獨的實例。JIcon組件可以管理狀態。 裝飾圖標讓我們很快地來看看很像容器的這三種圖標的實現情況吧。它們可以讓你組合、覆蓋或調整圖標,並將它們看做是單獨的Icon實例。例如,你可以用CompoundIcon將多個圖標放在一個通常只處理一個圖標(如Jlabel或Jbutton)的Swing組件中。 DecoratedIcon類可以讓你用一個圖標覆蓋另一個圖標。該功能可以讓你用一個較小的圖標在九個可能的位置上來裝飾一個大的圖標:垂直方向上的TOP、BOTTOM或CENTER,以及水平方向上的LEFT、RIGHT或CENTER。構造器需要兩個圖標實例,並需要垂直對齊和水平對齊。我們會查看對齊是否有效,如果對齊不好,或裝飾圖標比被裝飾圖標大,你就會得到IllegalArgumentException異常。我們用GetIconWidth()和getIconHeight()來返回被裝飾圖標(兩個圖標中較大的那個)的寬度和高度。然後用paintIcon()方法先繪製被裝飾的圖標,再在指定的位置上繪製裝飾圖標。 FilterIcon類可以將一個ImageFilter用於一個指定的Icon實例。這是在構造器中實現的,另外,它還緩存了結果圖像,以便在繪圖時使用。我選用了ImageFilter,它是老的AWT的一部分,因爲通過它,我們也可以用Java 2D最近提供的BufferedImageFiler(BuffereredImageFilter實現了ImageFilter接口)。接下來的工作就是返回所創建的圖像的寬度和高度了,並在調用paintIcon()方法時,在x、y位置上畫圖。我剛剛講述的三個類可以讓你在現有的Swing組件中顯示合併的、被裝飾的和被過濾的圖標。我們可以嵌套這些Icon的實現形式,從而根據你的需要來組合或調整圖標。遺憾的是,沒有一個Swing組件可以提供多個狀態視圖,因此下面就讓我們來看看如何實現這樣一個組件,它可以讓你處理多個圖標來顯示與你的應用程序相關的狀態。 我們來看看所實現的JIcon中的兩個重要的類,IconListModel和JIcon類本身IconListModel是個接口,它可以存儲多個用於JIcon的圖標。它可以以列表中當前索引的形式來管理數量不定的一列圖標實例和當前選擇的狀態。要實現一個IconListModel接口,我們也必須將事件發送到任何註冊的ChangeListener以便反映狀態或Icon列表內容的變化,從而使視圖(如JIcon)可以立即反映這些變化。注意,IconListModel擴展了Icon接口:public interface IconListModel extends Icon { public int getIconCount(); public int getCurrentIndex(); public Icon getIcon(int index); public void setIcons( Icon[] icon); public void addIcon(Icon icon); public void removeIcon( Icon icon); public void setCurrentIcon( int index); public void addChangeListener( ChangeListener listener); public void removeChangeListener( ChangeListener listener); } 通過這個接口,我們就可以很容易地看到需要什麼步驟來實現它了,即IconList的作用(見列表1)。我們運用了兩個ArrayList實例:一個用來管理ChangeListener實例的列表,另一個用來管理Icon實例的列表。我們將寬度和高度值緩存起來,當調用Icon的getIconWidth()和getIconHeight()方法時,返回這些值。寬度和高度是作爲圖標列表的最大值通過calculateSize()方法計算的,當列表內容發生改變時,就會調用該方法。 我們也保留當前圖標和索引值來反映當前選擇的狀態。當paintIcon()方法在Icon接口被調用時,就會繪製所選擇的圖標。如果有必要,我們還提供了一個setIcons()方法來設置整個列表的內容。兩個構造器中的第二個構造器在初試化時用該方法來設置列表。另一個構造器是空的,需要在一個單獨的步驟中填充列表。調用addIcon()或removeIcon()方法時,也會調用calculateSize()和fireChangeEvent()方法。我們運用了一個實用的方法(capIndex())來確保當前索引值永遠不會超過列表的大小。FireChangeEvent()、addChangeListener()和removeChangeListener()方法管理註冊的ChangeListener的列表。 突出焦點缺省情況下,在JIcon類中,我們用一個單獨的像素EmptyBorder環繞在圖標周圍,這樣我們就可以用一個藍色的LineBorder來重點顯示圖標了。你可以調用setFocusable()方法來激活該行爲。缺省情況下,我們可以處理鼠標事件(mouse event),但不接受聚焦顯示。通過三個構造器我們可以創建一個空的JIcon、帶有一個單獨的圖標的JIcon或帶有一個初始Icon列表的JIcon。在每種情況中,我們都用initComponent()方法來設置可選的美學效果和監聽器(listener)。 JIcon實現了四個監聽器:ChangeListener、FocusListener、MouseListener和KeyListener。觸發ChangeListener事件可以重畫視圖。我們用了一個叫做resetSize()的方法,因爲模式的變化會影響由組件返回的首選的、最小的尺寸。實際的尺寸是由IconList緩存的,當圖標被添加或刪除時,IconList就會計算實際尺寸,因此首選的、最小的尺寸就會根據當前的getIconWidth()和getIconHeight()方法值被更新,並根據現有邊界進行調整。 如果有必要,可以用FocusListener的focusGained()和focusLost()方法來改變邊界。我們可以通過調用標準的isFocusable()方法來查看是否可以運用該行爲。注意,Java 1.4對setFocusable()和isFocusable()方法都有介紹。如果你需要向後兼容(backward compatible),你必須修改代碼來運用isFocusTraversible()方法。 KeyListener和MouseListener方法可以用來響應用戶的輸入。對於KeyListener來說,我們可以通過調用nextIcon()方法對空格鍵作出響應。MousePressed事件的作用是一樣的,但先要有個焦點(focus)。在這兩種情況中,我們都調用重畫方法來刷新視圖。我在實現的例子中已經提供了nextIcon()和prevIcon()方法,雖然我們只用了nextIcon()方法。 NextIcon()方法從模式中提取當前索引值,並以增值(increment)順序取下一個索引,如果當前索引位於列表的最後,則運用nextIcon()會返回到第一個值。PrevIcon()方法的功能正好相反,它以遞減(decrement)的順序取列表中的前一個值,如果當前索引是列表中第一個值,則prevIcon()會取列表的最後一個值。在我實現的這個例子中,我們沒有用prevIcon()方法,但是你可能想讓用戶通過後退鍵和鼠標右鍵在列表中進行後退(move backward)操作。 以上就是有關JIcon類的簡要內容了,另外還有accessor,它們可以讓你get和set模式、在列表中添加、刪除和設置圖標;還有setBorder方法,如果邊界不爲空,我們可以覆蓋該方法來調用resetSize方法。SetModel()方法可以在用新模式註冊前註銷以前的任何模式。實際效果是,你可以直接通過程序設置模式的索引值、通過用戶鍵入或鼠標事件,用一個組件來循環一列圖標。 JIcon組件填補了Swing的空白,我們可以用它來管理圖標列表,這些圖標可以以一種可視的和可編輯的方式來反映狀態。JIcon可以給我們開發的很多複合Icon實例帶來好處,使我們可以實現更復雜的視圖而不會增加複雜性,但這些實現方法也可以用於運用Icon接口實例的任何組件,如JLabel和JButton。JIcon可以管理狀態,但IconListModel也可以實現Icon接口,並可以用在其它組件中。你在尋找可以提高可用性的方法時,我希望這些技術和例子會對你有所幫助。 關於作者: Claude Duguay已有20多年的軟件開發經驗了。他熱衷於Java技術,經常閱讀這方面的書,並經常撰寫這方面的文章。你可以通過[email protected]與他聯繫。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章