Java圖形界面開發:高級Swing容器(二)
11.3 JTabbedPane類
JTabbedPane類表示曾經流行的屬性頁來支持在一個窗口中多個容器的輸入或輸出,其中每次只顯示一個面板。使用JTabbedPane類似于使用CardLayout管理器,所不同的是添加到修改內建卡片的支持。然而CardLayout是一個LayoutManager,而JTabbedPane是一個完全功能的Container。如果我們不熟悉屬性頁,標簽對話框或是標簽面板(所有都是相同的事物的不同名字),圖11-10顯示了一個JDK 1.2版本所帶的原始SwingSet Demo中的標簽集合。


為了有助于JTabbedPane管理哪一個Component被選中,容器的模型是一個SingleSelectionModel接口的實現,或者更確切的說,是一個DefaultSingleSelectionModel實例。(SingleSelectionModel與DefaultSingleSelectionModel在第6章中進行了描述。)
11.3.1 創建JTabbedPane
JTabbedPane只有三個構造函數:
- public JTabbedPane()
- JTabbedPane tabbedPane = new JTabbedPane();
- public JTabbedPane(int tabPlacement)
- JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.RIGHT);
- public JTabbedPane(int tabPlacement, int tabLayoutPolicy)
- JTabbedPane tabbedPane =
- new JTabbedPane(JTabbedPane.RIGHT, JTabbedPane.SCROLL_TAB_LAYOUT);
可配置的選項是用來修改顯示哪一個組件的標簽位置與當在一個虛擬行中有多個標簽時的標簽布局策略。默認情況下,標簽位于容器的頂部,并且標簽數量超過容器寬度時會進行回環形成多行。然而,我們可以使用JTabbedPane的下列常量之一來顯式的指定位置:TOP, BOTTOM, LEFT或RIGHT,或者是使用SCROLL_TAB_LAYOUT或WRAP_TAB_LAYOUT來配置布局策略。圖11-11使用其他三個標簽位置顯示了圖11-10的屏幕顯示。圖11-12顯示了帶有滾動標簽布局的屏幕。
11.3.2 添加與移除標簽
一旦我們創建了基本的JTabbedPane容器,我們需要添加構成JTabbedPane頁的面板。我們可以使用兩種基本方法來添加面板。
如果我們使用JBuilder或是Eclipse的可視化工具來創建我們的界面,用戶界面構建器將會使用我們所熟悉的Container的add()方法來添加Component。所添加的面板使用component.getName()作為默認標題。然而,如果我們手動編程我們不應使用各種add()方法。
添加組件或是面板來創建標簽更為合適的方法是使用下面列出的addTab()或是insertTab()方法。insertTab()方法中除了組件與位置索引以外,其他的參數可以為空。(傳遞null作為Component參數會在運行時拋出NullPointerException。)顯示的圖標與工具提示設置并沒有默認值。
- • public void addTab(String title, Component component)
- • public void addTab(String title, Icon icon, Component component)
- • public void addTab(String title, Icon icon, Component component, String tip)
- • public void insertTab(String title, Icon icon, Component component, String tip,
- int index)
當使用addTab()時,標簽被添加到末尾,也就是對于頂部或是底部標簽集合來說是最右邊的位置,或是對于在左邊或右邊放置的標簽時位于底部,依據組件的方向,也可以是相反的一邊。
在創建面板之后,我們可以通過setXXXAt()方法修改一個特定標簽的標題,圖標,熱鍵,工具提示或是組件:
- • public void setTitleAt(int index, String title)
- • public void setIconAt(int index, Icon icon)
- • public void setMnemonicAt(int index, int mnemonic)
- • public void setDisplayedMnemonicIndexAt(int index, int mnemonicIndex)
- • public void setToolTipTextAt(int index, String text)
- • public void setComponentAt(int index, Component component)
提示,顯示的熱鍵索引指向標題中哪一個字符應高亮。例如,如果我們希望title中第二t高亮顯示熱鍵,我們可以使用setMnemonicAt()方法將熱鍵字符設置為KeyEvent.VK_T,并使用setDisplayedMnemonicIndexAt()將熱鍵索引設置為2。
另外,我們可以修改一個特定標簽的背景色或前景色,允許或是禁止一個特定的標簽,或是使用setXXXAt()方法設置不同的禁止圖標:
- • public void setBackgroundAt(int index, Color background)
- • public void setForegroundAt(int index, Color foreground)
- • public void setEnabledAt(int index, boolean enabled)
- • public void setDisabledIconAt(int index, Icon disabledIcon)
要移除一個標簽,我們可以使用removeTabAt(int index), remove(int index)或是remove(Component component)來移除一個特定的標簽。另外,我們可以使用removeAll()移除所有的標簽。
13.3.3 JTabbedPane屬性
表11-4顯示了JTabbedPane的11個屬性。因為JTabbedPane的許多setter/getter方法都指定了一個索引參數,事實上他們并不是真正的屬性。
我們可以通過selectedComponent或是selectedIndex屬性來編程修改顯示的標簽。
tabRunCount屬性表示顯示所有的標簽所必須的行數(對于頂部或底部標簽位置)或是列數(對于左邊或是右邊位置)。
注意,當要顯示容器時修改JTabbedPane的LayoutManager將會拋出異常。換句話說,不要那樣做。
11.3.4 監聽修改標簽選中
如果我們對確定何時選中的標簽變化感興趣,我們需要監聽選中模型的變化。這是通過我們將一個ChangeListener關聯到JTabbedPane(或是直接關聯到SingleSelectionModel)來實現的。注冊的ChangeListener報告何時選中模型發生變化,以及選中的面板變化時模型的變化。
顯示在列表11-3中的程序演示了監聽選中標簽的變化并且顯示了新選中標簽的標題。
- package swingstudy.ch11;
- import java.awt.BorderLayout;
- import java.awt.Color;
- import java.awt.EventQueue;
- import java.awt.event.KeyEvent;
- import javax.swing.JButton;
- import javax.swing.JFrame;
- import javax.swing.JTabbedPane;
- import javax.swing.event.ChangeEvent;
- import javax.swing.event.ChangeListener;
- import swingstudy.ch04.DiamondIcon;
- public class TabSample {
- static Color colors[] = {Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.MAGENTA};
- static void add(JTabbedPane tabbedPane, String label, int mnemonic) {
- int count = tabbedPane.getTabCount();
- JButton button = new JButton(label);
- button.setBackground(colors[count]);
- tabbedPane.addTab(label, new DiamondIcon(colors[count]), button, label);
- tabbedPane.setMnemonicAt(count, mnemonic);
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Runnable runner = new Runnable() {
- public void run() {
- JFrame frame = new JFrame("Tabbed Pane Sample");
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- JTabbedPane tabbedPane = new JTabbedPane();
- tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
- String titles[] = {"General", "Security", "Content", "Connection", "Programs", "Advanced"};
- int mnemonics[] = {KeyEvent.VK_G, KeyEvent.VK_S, KeyEvent.VK_C, KeyEvent.VK_0, KeyEvent.VK_P, KeyEvent.VK_A};
- for(int i=0, n=titles.length; i<n; i++) {
- add(tabbedPane, titles[i], mnemonics[i]);
- }
- ChangeListener changeListener = new ChangeListener() {
- public void stateChanged(ChangeEvent event) {
- JTabbedPane sourceTabbedPane = (JTabbedPane)event.getSource();
- int index = sourceTabbedPane.getSelectedIndex();
- System.out.println("Tab changed to: "+sourceTabbedPane.getTitleAt(index));
- }
- };
- tabbedPane.addChangeListener(changeListener);
- frame.add(tabbedPane, BorderLayout.CENTER);
- frame.setSize(400, 150);
- frame.setVisible(true);
- }
- };
- EventQueue.invokeLater(runner);
- }
- }
11.3.5 自定義JTabbedPane觀感
每一個可安裝的Swing觀感都提供了不同的JTabbedPane外觀以及JTabbedPane組件的默認UIResource值集合。圖11-13顯示了JTabbedPane容器在預安裝的觀感類型Motif,Windows以及Ocean下的外觀。某些項目是特定觀感的:當可用的標簽集合對于顯示過度時JTabbedPane如何顯示,當用戶在后一行選擇標簽時如何響應,如何顯示工具提示,以及如何顯示滾動標簽布局。
JTabbedPane可用的UIResource相關的屬性集合顯示在表11-5中。對于JTabbedPane組件,有34個不同的屬性。
#p#
11.4 JScrollPane類
Swing的JScrollPane容器通過滾動支持(如果需要)來使得當前部分不可見從而為在較小的顯示區域內顯示大組件提供支持。圖11-4顯示了一個實現,其中大組件是一個具有ImageIcon的JLabel。
可以使用兩種方示來標識要滾動的組件。我們不需要將要滾動的組件直接添加到JScrollPane容器中,我們可以將組件添加到已經包含在滾動面板中的另一個組件,JViewport。相對應的,我們可以通過將其傳遞給構造函數,在構造時標識組件。
- Icon icon = new ImageIcon("dog.jpg");
- JLabel label = new JLabel(icon);
- JScrollPane jScrollPane = new JScrollPane();
- jScrollPane.setViewportView(label);
- // or
- JScrollPane jScrollPane2 = new JScrollPane(label);
一旦我們將組件添加到JScrollPane中,用戶可以使用滾動條來查看在JScrollPane的內部區域不可見的大組件部分。
除了為我們提供了設置JScrollPane可滾動組件的方法,顯示策略可以決定是否以及何時在JScrollPane周圍顯示滾動條。Swing的JScrollPane為水平以及垂直滾動條維度了單獨的顯示策略。
除了使得我們為滾動添加JViewport以及兩個JScrollBar組件以外,JScrollPane同時允許我們提供另外兩個JViewport對象用于行與列頭以及在滾動面板四個角中顯示的四個Component對象。這些組件的放置是通過在第10章介紹進行全面描述的ScrollPaneLayout管理器來管理的。JScrollPane實現所用的JScrollBar組件是一個名為JScrollPane.ScrollBar的JScrollBar子類。他們被用來替換通常的JScrollBar,從而在組件實現了Scrollable接口時正確處理JViewport中的滾動組件。
為了幫助我們理解這些組件如何放置在JScrollPane中,圖11-15演示了ScrollPaneLayout如何放置各種對象。
注意,JScrollPane組件只支持輕量級組件的滾動。我們不應該向容器添加通常的,重量級AWT組件。
11.4.1 創建JScrollPane
JScrollPane有四個構造函數:
- public JScrollPane()
- JScrollPane scrollPane = new JScrollPane();
- public JScrollPane(Component view)
- Icon icon = new ImageIcon("largeImage.jpg");
- JLabel imageLabel = new JLabel(icon);
- JScrollPane scrollPane = new JScrollPane(imageLabel);
- public JScrollPane(int verticalScrollBarPolicy, int horizontalScrollBarPolicy)
- JScrollPane scrollPane = new
- JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
- JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
- public JScrollPane(Component view, int verticalScrollBarPolicy,
- int horizontalScrollBarPolicy)
- JScrollPane scrollPane = new JScrollPane(imageLabel,
- JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
- JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
這些構造函數提供了預安裝滾動組件以及配置單獨滾動條滾動策略的選項。默認情況下,滾動條只在需要的時候顯示。表11-16顯示了用來為每一個滾動條顯示設置策略的JScrollPane常量。使用其他不正確的設置會導致拋出IllegalArgumentException。
下面的章節將會解釋如何在創建JScrollPane之后添加或修改組件。
11.4.2 修改Viewport View
如果我們使用合適的組件創建JScrollPane,我們只需要添加JScrollPane來顯示。然而,如果我們并沒有在創建時關聯組件,或者是希望在稍后進行修改,有兩種方法可以為滾動關聯一個新的組件。首先,我們可以通過設置viewportView屬性直接修改組件:
- scrollPane.setViewportView(dogLabel);
修改滾動組件另一種方法就是將JViewport放在JScrollPane的中間,然后修改其view屬性:
- scrollPane.getViewport().setView(dogLabel);
我們將會在本章稍后的“JViewport類”一節中了解到更多關于JViewport組件的內容。
11.4.3 Scrollable接口
不同于AWT組件,例如List會在一次顯示的選項過多時自動提供可滾動區域,Swing組件JList,JTable,JTextComponent,以及JTree并不會自動提供滾動支持。我們必須創建組件,將其添加到JScrollPane,然后將滾動面板添加到屏幕。
- JList list = new JList(...);
- JScrollPane scrollPane = new JScrollPane(list);
- aFrame.add(scrollPane, BorderLayout.CENTER);
將組件添加到JScrollPane起作用的原因在于每一個也許對于屏幕過大的Swing組件(并且需要滾動支持)實現了Scrollable接口。通過實現這個接口,當我們移動與JScrollPane相關聯的滾動條時,JScrollPane會查詢容器內Scrollable組件的尺寸信息從而基于當前的滾動條位置正確的定位組件。
我們唯一需要擔心Scrollable接口的時機就是當我們創建一個需要滾動支持的自定義組件的時候。下面是Scrollable接口的定義。
- public interface Scrollable {
- public Dimension getPreferredScrollableViewportSize();
- public boolean getScrollableTracksViewportHeight();
- public boolean getScrollableTracksViewportWidth();
- public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
- int direction);
- public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
- int direction);
- }
如果我們創建一個自定義的Scrollable組件,然后將放在JScrollPane中,當JScrollPane的滾動條或是鼠標滾輪移動時,他就會正確的響應。
11.4.4 JScrollPane屬性
表11-7顯示了JScrollPane的19個屬性。
嘗試著JScrollPane的布局屬性修改為除了ScrollPaneLayout以外的值或是null將會在運行時拋出ClassCastException,因為JScrollPane所用的布局管理器必須為ScrollPaneLayout。
使用ScrollPaneLayout JScrollPane依賴ScrollPaneLayout管理器對容器內的組件進行放置。然而大多數的布局管理器被設置用來布局所有的組件類型,但是ScrollPaneLayout的四個區域只接受特定類型的組件。表11-8顯示了可以放置在圖11-15中所示區域中顯示的組件類型。
注意,區域角有兩個常量集合。對于國際化支持,我們可以使用LOWER_LEADING_CORNER, LOWER_TRAILING_CORNER, UPPER_LEADING_CORNER與UPPER_TRAILING_CORNER,這些常量可以為我們處理組件方向。對于由左到右的組件方向,起始是左邊,而結束是右邊。
正如設計要求,布局管理器描述支持對于可用空間過大的主內容區域(VIEWPORT)所必須的屏幕布局。用于在區域中瀏覽的滾動條可以被設置在內容區域的右邊(VERTICAL_SCROLLABAR)或是下邊(HORIZONTAL_SCROLLBAR)。不滾動的固定頭可以被放置在內容區域的上部(COLUMN_HEADER)或是其左邊(ROW_HEADER)。四個角(*_CORNER)可以配置來顯示任意的組件類型,通常是帶有圖片的標簽;然則 ,在其中可以放置任意的組件。
注意,一些開發者會認為ScrollPaneLayout是一個帶有自定義約束的GridBagLayout。在通常情況下,大多數開發者并不會在JScrollPane之外使用ScrollPaneLayout。
使用JScrollPane頭與角
如圖11-15與表11-8所示,在JScrollPane存在多個不同的區域。通常,我們只使用中間的視圖,并使用兩個滾動條完成相應的任務。另外,當使用JTable組件時,當放置在JScrollPane中時,表會自動將列頭放置在列頭區域。
我們也可以手動添加或是修改JScrollPane的列頭或是行頭。盡管我們可以在這里區域完全替換JViewport,但是為此區域中的Component設置columnHeaderView或是rowHeaderView屬性更為簡單。這一操作可以為我們將組件放置在JViewport中。
要將組件放置在JScrollPane的一個角中,我們需要調用setCorner(String key, Component corner)方法,其中key是JScrollPane中的下列常量之一:LOWER_LEFT_CORNER, LOWER_RIGHT_CORNER, UPPER_LEFT_CORNER,或是UPPER_RIGHT_CORNER。
角區域的使用比較有技巧。只有當兩個位于角落右邊角的組件是當前顯示時,角落組件才會被顯示。例如,假如我們要在右下角落放置一個具有公司logo的標簽,而兩個滾動條的滾動策略只有在必需的時才會顯示。在這種情況下,如果一個滾動條不需要,角落中的logo也不會被顯示。作為另一個盒子,如果一個JScrollPane具有一個列頭顯示,但是并沒有行頭,左上角中的組件也不會被顯示。
所以,僅僅因為我們將角落設置為一個組件(例如scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, logLabel)),不要期望組件總是或是自動顯示。而且,如圖11-16所示,相鄰的區域控制角落的尺寸。不要認為角落組件會按需要大小顯示。這是因為其最小尺寸,最優尺寸與最大尺寸被完全被忽略了。在圖11-16中,用來創建角落組件的實際圖片要大于所用的空間。
注意,修改JScrollPane的一個角落類似于邊界屬性,其中屬性名是表11-8中所列的角落鍵值。
重設視圖域位置
有時,我們也許會希望將內部視圖的內容向JScrollPane的左上角移動。這種變化也許是需要的,因為視圖發生了變化,或者是某些事情的發生要求視圖域組件返回到JScrollPane的原始位置。移動視圖最簡單的方法就是JScrollPane的滾動條位置。將每一個滾動條設置為其最小值就有效的將組件視圖移動到了組件的左上角區域。列表11-4中所顯示的ActionListener可以關聯到屏幕中的按鈕或是JScrollPane的角落,從而使得JScrollPane的內容返回到原始位置。
- package swingstudy.ch11;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import javax.swing.JScrollBar;
- import javax.swing.JScrollPane;
- public class JScrollPaneToTopAction implements ActionListener {
- JScrollPane scrollPane;
- public JScrollPaneToTopAction(JScrollPane scrollPane) {
- if(scrollPane == null) {
- throw new IllegalArgumentException("JScrollPaneToTopAction: null JScrollPane");
- }
- this.scrollPane = scrollPane;
- }
- @Override
- public void actionPerformed(ActionEvent event) {
- // TODO Auto-generated method stub
- JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
- JScrollBar horizontalScrollBar = scrollPane.getHorizontalScrollBar();
- verticalScrollBar.setValue(verticalScrollBar.getMinimum());
- horizontalScrollBar.setValue(horizontalScrollBar.getMinimum());
- }
- }
11.4.5 自定義JScrollPane觀感
每一個可安裝的觀感都提供了不同的JScrollPane外觀以及默認的組件UIResource值集合。圖11-17顯示了JScrollPane組件在預安裝的觀感類型集合下的外觀顯示。對于JScrollPane,觀感類型之間的主要區別與滾動條的外觀以及視圖周圍的邊框有關。
表11-9顯示了JScrollPane可用的UIResource相關屬性集合。對于JScrollPane組件,有十個不同的屬性。當滾動條在JScrollPane內可見時,修改與JScrollBar的相關屬性會影響其外觀。