Swing組件提供了對組件周圍的邊框地區進行定製的功能。為了簡單,我們可以使用預定義的八個邊框,或者是我們可以建立自己的邊框。在本章中,我們將會瞭解如何最好的使用已存在邊框以及如何建立我們自己的邊框。
7.1 Some Basics on Woring with Borders
邊框是帶有標準的setBorder()與getBorder()屬性方法的JComponent屬性。所以,所有的JComponent子類的Swing組件都具有邊框。預設情況下,一個組件並沒有與其相關聯的自訂邊框。(JComponent的getBorder()方法返回null。)相反,組件顯示的預設邊框是依據當前的觀感對於其狀態最為合適的邊框。例如,對於JButton,對於每一個觀感特定不同的邊框,邊框可以表現為按下,未按下或是禁止。
儘管對於所有的組件初始的邊框屬性設定為null,我們可以通過調用JComponent的setBorder(Border newValue)方法來修改組件的邊框。一旦設定,修改的值就會覆蓋當前觀感的邊框,並且在組件的地區內繪製新邊框。如果在稍後的時候,我們希望將邊框重新設定為對於狀態與觀感合適的邊框,我們可以將邊框屬性修改為null,使用setBorder(null)並且調用組件的updateUI()方法。updateUI()方法會通知觀感重新設定邊框。如果我們沒有調用updateUI()方法,則組件將沒有邊框。
圖7-1顯示了一個JLabel周圍的各種邊框設定,通過文字標籤來標明邊框類型。如何建立不同的邊框將會在本章的稍後部分進行討論。
7.1.1 Exploring the Border Inteface
我們可以在javax.swing.border包中找到Border介面。這個介面構成了所有邊框類的基礎。這個介面直接由AbstractBorder類實現,這個類是所有預定義的Swing邊框類的父類:BevelBorder,CompoundBorder,EmptyBorder,EtchedBorder,LineBorder,MatteBorder,SoftBevelBorder以及TitledBorder。另外一個有趣的類就是BorderFactory類,我們可以在javax.swing包中找到這個類。這個類使用工廠設計模式來建立邊框,隱藏了實現細節,並且可以緩衝各種選項來最佳化共同使用。
在這裡顯示的Border介面由三個方法構成:paintBorder(),getBordernsets()以及isBorderOpaque()。這些方法會在後面的章節中進行描述。
paintBorder()
paintBorder()方法是這個介面的關鍵方法。其定義如下:
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
邊框實現的繪製是由這個方法完成的。通常,Border實現首先會詢問Insets維度,然後在外層地區的四個邊進行繪製,7-2所示。如果邊框是不透明的,paintBorder()實現必須填充整個內部地區。如果一個邊框是不透明的,並沒有填充地區,那麼這是一個bug並且需要修正。
列表7-1顯示了一個簡單的paintBorder()實現,這個實現使用比上部與下部略淺的顏色填充左邊與右邊。
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { Insets insets = getBorderInsets(c); Color color = c.getForeground(); Color brighterColor = color.brighter();// Translate coordinate space g.translate(x, y);// Top g.setColor(color); g.fillRect(0, 0, width, insets.top);// Left g.setColor(brighterColor); g.fillRect(0, insets.top, insets.left, height-insets.top-insets.bottom);// Bottom g.setColor(color); g.fillRect(0, height-insets.bottom, width, insets.bottom);// Right g.setColor(brighterColor); g.fillRect(width-insets.right, insets.top, insets.right, height-insets.top-insets.bottom);// Translate coordinate space back g.translate(-x, -y);}
當建立我們自己的邊框時,我們將會經常發現我們自己在填充相同的非重疊矩形地區。Graphics的translate()方法簡化了繪製座標的指定。無需轉換座標,我們需要通過原始的(x,y)來位移繪製。
注意:我們不能通過插入g.fillRect(x,y,width,height)來簡化,因為這會填充整個組件地區,而不是邊框地區。
getBorderInsets()
getBorderInsets()方法會返回在指定的組件c作為Insets對象的周圍繪製邊框所必須的空間。其定義如下:
public Insets getBorderInsets(Component c)
7-2所示,這些內部地區定義了可以繪製邊框的合法地區。Component參數可以使得我們使用他的一些屬性來決定內部地區的尺寸。
isBorderOpaque()
邊框可以是不透明的或是透明的。isBorderOpaque()方法可以返回true或是false來表明邊框是哪種形式。其定義如下:
public boolean isBorderOpaque()
當這個方法返回true時,邊框需要是非透明的,填充其整個內部地區。當其返回false時,沒有繪製的地區將會保持邊框所在的組件的背景顏色。
7.1.2 Introducing BorderFactory
現在我們已經基本瞭解了Border介面是如何工作的,現在我們來瞭解一下作為簡單建立邊框方法的BorderFactory類。我們可以在javax.swing包中找到這個類,BorderFactory類提供了一系列的static方法來建立預定義的邊框。無需調用不同的邊框類的特定建構函式,通過這個工廠類我們幾乎可以建立所有的邊框。這個工廠類同時可以緩衝一些邊框的建立從而避免多次重新建立經常使用的邊框。這個類的定義如下:
public class BorderFactory { public static Border createBevelBorder(int type); public static Border createBevelBorder(int type, Color highlight, Color shadow); public static Border createBevelBorder(int type, Color highlightOuter, Color highlightInner, Color shadowOuter, Color shadowInner); public static CompoundBorder createCompoundBorder(); public static CompoundBorder createCompoundBorder(Border outside, Border inside); public static Border createEmptyBorder(); public static Border createEmptyBorder(int top, int left, int bottom, int right); public static Border createEtchedBorder(); public static Border createEtchedBorder(Color highlight, Color shadow); public static Border createEtchedBorder(int type); public static Border createEtchedBorder(int type, Color highlight, Color shadow); public static Border createLineBorder(Color color); public static Border createLineBorder(Color color, int thickness); public static Border createLoweredBevelBorder(); public static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Color color); public static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Icon icon); public static Border createRaisedBevelBorder(); public static TitledBorder createTitledBorder(Border border); public static TitledBorder createTitledBorder(Border border, String title); public static TitledBorder createTitledBorder(Border border, String title, int justification, int position); public static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font); public static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font, Color color); public static TitledBorder createTitledBorder(String title);}
我們將會在描述特定的邊框類型的過程中描述這個類的不同方法。例如,要建立一個具有紅線的邊框,我們可以使用下面的語句,然後將這個邊框關聯到一個組件。
Border lineBorder = BorderFactory.createLineBorder(Color.RED);
7.1.3 Starting with AbstractBorder
在我們瞭解javax.swing.border包中單個的邊框之前,一個系統邊框需要獲得特別的關註:AbstractBorder。正如前面所提到的,AbstractBorder類是其他的預定義邊框的父類。
建立AbstractBorder
AbstractBorder有一個建構函式:
public AbstractBorder()
因為AbstractBorder是其他標準邊框的父類,這個建構函式實際是為其他邊框類自動調用的。
檢測AbstractBorder方法
AbstractBorder類提供了Border介面的三個方法實現。
public Insets getBorderInsets(Component c)
AbstractBorder的內部地區是零。每一個預定義的子類要重寫getBorderInsets()方法。
public boolean isBorderOpaque()
AbstractBorder的預設非透明屬性設定為false。這就意味著如果我們需要繪製類似點劃線的邊框,組件的背景將會是透明的。許多預定義的子類重寫了isBorderOpaque()方法。
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
AbstractBorder的繪製邊框是空的。所有的子類應該重寫這個方法來實際繪製一個邊框,也許除了EmptyBorder。
除了提供了Border方法的預設實現以外,AbstractBorder提供了我們可以利用的其他兩個功能,或者僅是允許系統使用。首先,還有另外一個需要兩個參數Component與Insets的getBorderInsets()方法:
public Insets getBorderInsets(Component c, Insets insets)
在這個方法版本中,並沒有建立並返回一個新的Insets對象,所傳遞的Insets對象首先被修改然後返回。使用這個方法可以避免每次需要查詢邊框內部地區時建立然後銷毀額外的Insets對象。
第二個可用的新方法是getInteriorRectangle(),這個方法有靜態與非靜態版本。指定了Component,Border,以及四個整數參數,這個方法將會返回一個內部的Rectangle,從而組件可以在邊框內部地區內繪製其自身。