# 13.12 布局的控制
在Java里該方法是安一個組件到一個窗體中去,它不同我們使用過的其它GUI系統。首先,它是全代碼的;沒有控制安放組件的“資源”。其次,該方法的組件被安放到一個被“布局管理器”控制的窗體中,由“布局管理器”根據我們`add()`它們的決定來安放組件。大小,形狀,組件位置與其它系統的布局管理器顯著的不同。另外,布局管理器使我們的程序片或應用程序適合窗口的大小,所以,如果窗口的尺寸改變(例如,在HTML頁面的程序片指定的規格),組件的大小,形狀和位置都會改變。
程序片和幀類都是來源于包含和顯示組件的容器。(這個容器也是一個組件,所以它也能響應事件。)在容器中,調用`setLayout()`方法允許我選擇不同的布局管理器。
在這節里我們將探索不同的布局管理器,并安放按鈕在它們之上。這里沒有捕捉按鈕的事件,正好可以演示如何布置這些按鈕。
## 13.12.1 `FlowLayout`
到目前為止,所有的程序片都被建立,看起來使用一些不可思議的內部邏輯來布置它們的組件。那是因為程序使用一個默認的方式:`FlowLayout`。這個簡單的`Flow`的組件安裝在窗體中,從左到右,直到頂部的空格全部再移去一行,并繼續循環這些組件。
這里有一個例子明確地(當然也是多余地)設置一個程序片的布局管理器去`FlowLayout`,然后在窗體中安放按鈕。我們將注意到`FlowLayout`組件使用它們本來的大小。例如一個按鈕將會變得和它的字符串符一樣的大小。
```
//: FlowLayout1.java
// Demonstrating the FlowLayout
import java.awt.*;
import java.applet.*;
public class FlowLayout1 extends Applet {
public void init() {
setLayout(new FlowLayout());
for(int i = 0; i < 20; i++)
add(new Button("Button " + i));
}
} ///:~
```
所有組件將在`FlowLayout`中被壓縮為它們的最小尺寸,所以我們可能會得到一些奇怪的狀態。例如,一個標簽會合適它自已的字符串的尺寸,所以它會右對齊產生一個不變的顯示。
## 13.12.2 `BorderLayout`
布局管理器有四邊和中間區域的概念。當我們增加一些事物到使用`BorderLayout`的面板上時我們必須使用`add()`方法將一個字符串對象作為它的第一個參數,并且字符串必須指定(正確的大寫)`North`(上),`South`(下),`west`(左),`East`(右)或者`Center`。如果我們拼寫錯誤或沒有大寫,就會得到一個編譯時的錯誤,并且程序片不會像你所期望的那樣運行。幸運的是,我們會很快發現在Java 1.1中有了更多改進。
這是一個簡單的程序例子:
```
//: BorderLayout1.java
// Demonstrating the BorderLayout
import java.awt.*;
import java.applet.*;
public class BorderLayout1 extends Applet {
public void init() {
int i = 0;
setLayout(new BorderLayout());
add("North", new Button("Button " + i++));
add("South", new Button("Button " + i++));
add("East", new Button("Button " + i++));
add("West", new Button("Button " + i++));
add("Center", new Button("Button " + i++));
}
} ///:~
```
除了`Center`的每一個位置,當元素在其它空間內擴大到最大時,我們會把它壓縮到適合空間的最小尺寸。但是,`Center`擴大后只會占據中心位置。
`BorderLayout`是應用程序和對話框的默認布局管理器。
## 13.12.3 `GridLayout`
`GridLayout`允許我們建立一個組件表。添加那些組件時,它們會按從左到右、從上到下的順序在網格中排列。在構造器里,需要指定自己希望的行、列數,它們將按正比例展開。
```
//: GridLayout1.java
// Demonstrating the GridLayout
import java.awt.*;
import java.applet.*;
public class GridLayout1 extends Applet {
public void init() {
setLayout(new GridLayout(7,3));
for(int i = 0; i < 20; i++)
add(new Button("Button " + i));
}
} ///:~
```
在這個例子里共有21個空位,但卻只有20個按鈕,最后的一個位置作留空處理;注意對`GridLayout`來說,并不存在什么“均衡”處理。
## 13.12.4 `CardLayout`
`CardLayout`允許我們在更復雜的擁有真正的文件夾卡片與一條邊相遇的環境里創建大致相同于“卡片式對話框”的布局,我們必須壓下一個卡片使不同的對話框帶到前面來。在AWT里不是這樣的:`CardLayout`是簡單的空的空格,我們可以自由地把新卡片帶到前面來。(JFC/Swing庫包括卡片式的窗格看起來非常的棒,且可以我們處理所有的細節。)
(1) 聯合布局(Combining layouts)
下面的例子聯合了更多的布局類型,在最初只有一個布局管理器被程序片或應用程序操作看起來相當的困難。這是事實,但如果我們創建更多的面板對象,每個面板都能擁有一個布局管理器,并且像被集成到程序片或應用程序中一樣使用程序片或應用程序的布局管理器。這就象下面程序中的一樣給了我們更多的靈活性:
```
//: CardLayout1.java
// Demonstrating the CardLayout
import java.awt.*;
import java.applet.Applet;
class ButtonPanel extends Panel {
ButtonPanel(String id) {
setLayout(new BorderLayout());
add("Center", new Button(id));
}
}
public class CardLayout1 extends Applet {
Button
first = new Button("First"),
second = new Button("Second"),
third = new Button("Third");
Panel cards = new Panel();
CardLayout cl = new CardLayout();
public void init() {
setLayout(new BorderLayout());
Panel p = new Panel();
p.setLayout(new FlowLayout());
p.add(first);
p.add(second);
p.add(third);
add("North", p);
cards.setLayout(cl);
cards.add("First card",
new ButtonPanel("The first one"));
cards.add("Second card",
new ButtonPanel("The second one"));
cards.add("Third card",
new ButtonPanel("The third one"));
add("Center", cards);
}
public boolean action(Event evt, Object arg) {
if (evt.target.equals(first)) {
cl.first(cards);
}
else if (evt.target.equals(second)) {
cl.first(cards);
cl.next(cards);
}
else if (evt.target.equals(third)) {
cl.last(cards);
}
else
return super.action(evt, arg);
return true;
}
} ///:~
```
這個例子首先會創建一種新類型的面板:`BottonPanel`(按鈕面板)。它包括一個單獨的按鈕,安放在`BorderLayout`的中央,那意味著它將充滿整個的面板。按鈕上的標簽將讓我們知道我們在`CardLayout`上的那個面板上。
在程序片里,面板卡片上將存放卡片和布局管理器CL因為`CardLayout`必須組成類,因為當我們需要處理卡片時我們需要訪問這些引用。
這個程序片變成使用`BorderLayout`來取代它的默認`FlowLayout`,創建面板來容納三個按鈕(使用`FlowLayout`),并且這個面板安置在程序片末尾的`North`。卡片面板增加到程序片的`Center`里,有效地占據面板的其余地方。
當我們增加`BottonPanels`(或者任何其它我們想要的組件)到卡片面板時,`add()`方法的第一個參數不是`North`,`South`等等。相反的是,它是一個描述卡片的字符串。如果我們想輕擊那張卡片使用字符串,我們就可以使用,雖然這字符串不會顯示在卡片的任何地方。使用的方法不是使用`action()`;代之使用`first()`、`next()`和`last()`等方法。請查看我們有關其它方法的文件。
在Java中,使用的一些卡片式面板結構十分的重要,因為(我們將在后面看到)在程序片編程中使用的彈出式對話框是十分令人沮喪的。對于Java 1.0版的程序片而言,`CardLayout`是唯一有效的取得很多不同的“彈出式”的窗體。
## 13.12.5 `GridBagLayout`
很早以前,人們相信所有的恒星、行星、太陽及月亮都圍繞地球公轉。這是直觀的觀察。但后來天文學家變得更加的精明,他們開始跟蹤個別星體的移動,它們中的一些似乎有時在軌道上緩慢運行。因為天文學家知道所有的天體都圍繞地球公轉,天文學家花費了大量的時間來討論相關的方程式和理論去解釋天體對象的運行。當我們試圖用`GridBagLayout`來工作時,我們可以想像自己為一個早期的天文學家。基礎的條例是(公告:有趣的是設計者居然在太陽上(這可能是在天體圖中標錯了位置所致,譯者注))所有的天體都將遵守規則來運行。哥白尼日新說(又一次不顧嘲諷,發現太陽系內的所有的行星圍繞太陽公轉。)是使用網絡圖來判斷布局,這種方法使得程序員的工作變得簡單。直到這些增加到Java里,我們忍耐(持續的冷嘲熱諷)西班牙的`GridBagLayout`和`GridBagConstraints`狂熱宗教。我們建議廢止`GridBagLayout`。取代它的是,使用其它的布局管理器和特殊的在單個程序里聯合幾個面板使用不同的布局管理器的技術。我們的程序片看起來不會有什么不同;至少不足以調整`GridBagLayout`限制的麻煩。對我而言,通過一個例子來討論它實在是令人頭痛(并且我不鼓勵這種庫設計)。相反,我建議您從閱讀Cornell和Horstmann撰寫的《核心Java》(第二版,Prentice-Hall出版社,1997年)開始。
在這范圍內還有其它的:在JFC/Swing庫里有一個新的使用Smalltalk的受人歡迎的“Spring and Struts”布局管理器并且它能顯著地減少`GridBagLayout`的需要。
- Java 編程思想
- 寫在前面的話
- 引言
- 第1章 對象入門
- 1.1 抽象的進步
- 1.2 對象的接口
- 1.3 實現方案的隱藏
- 1.4 方案的重復使用
- 1.5 繼承:重新使用接口
- 1.6 多態對象的互換使用
- 1.7 對象的創建和存在時間
- 1.8 異常控制:解決錯誤
- 1.9 多線程
- 1.10 永久性
- 1.11 Java和因特網
- 1.12 分析和設計
- 1.13 Java還是C++
- 第2章 一切都是對象
- 2.1 用引用操縱對象
- 2.2 所有對象都必須創建
- 2.3 絕對不要清除對象
- 2.4 新建數據類型:類
- 2.5 方法、參數和返回值
- 2.6 構建Java程序
- 2.7 我們的第一個Java程序
- 2.8 注釋和嵌入文檔
- 2.9 編碼樣式
- 2.10 總結
- 2.11 練習
- 第3章 控制程序流程
- 3.1 使用Java運算符
- 3.2 執行控制
- 3.3 總結
- 3.4 練習
- 第4章 初始化和清除
- 4.1 用構造器自動初始化
- 4.2 方法重載
- 4.3 清除:收尾和垃圾收集
- 4.4 成員初始化
- 4.5 數組初始化
- 4.6 總結
- 4.7 練習
- 第5章 隱藏實現過程
- 5.1 包:庫單元
- 5.2 Java訪問指示符
- 5.3 接口與實現
- 5.4 類訪問
- 5.5 總結
- 5.6 練習
- 第6章 類復用
- 6.1 組合的語法
- 6.2 繼承的語法
- 6.3 組合與繼承的結合
- 6.4 到底選擇組合還是繼承
- 6.5 protected
- 6.6 累積開發
- 6.7 向上轉換
- 6.8 final關鍵字
- 6.9 初始化和類裝載
- 6.10 總結
- 6.11 練習
- 第7章 多態性
- 7.1 向上轉換
- 7.2 深入理解
- 7.3 覆蓋與重載
- 7.4 抽象類和方法
- 7.5 接口
- 7.6 內部類
- 7.7 構造器和多態性
- 7.8 通過繼承進行設計
- 7.9 總結
- 7.10 練習
- 第8章 對象的容納
- 8.1 數組
- 8.2 集合
- 8.3 枚舉器(迭代器)
- 8.4 集合的類型
- 8.5 排序
- 8.6 通用集合庫
- 8.7 新集合
- 8.8 總結
- 8.9 練習
- 第9章 異常差錯控制
- 9.1 基本異常
- 9.2 異常的捕獲
- 9.3 標準Java異常
- 9.4 創建自己的異常
- 9.5 異常的限制
- 9.6 用finally清除
- 9.7 構造器
- 9.8 異常匹配
- 9.9 總結
- 9.10 練習
- 第10章 Java IO系統
- 10.1 輸入和輸出
- 10.2 增添屬性和有用的接口
- 10.3 本身的缺陷:RandomAccessFile
- 10.4 File類
- 10.5 IO流的典型應用
- 10.6 StreamTokenizer
- 10.7 Java 1.1的IO流
- 10.8 壓縮
- 10.9 對象序列化
- 10.10 總結
- 10.11 練習
- 第11章 運行期類型識別
- 11.1 對RTTI的需要
- 11.2 RTTI語法
- 11.3 反射:運行期類信息
- 11.4 總結
- 11.5 練習
- 第12章 傳遞和返回對象
- 12.1 傳遞引用
- 12.2 制作本地副本
- 12.3 克隆的控制
- 12.4 只讀類
- 12.5 總結
- 12.6 練習
- 第13章 創建窗口和程序片
- 13.1 為何要用AWT?
- 13.2 基本程序片
- 13.3 制作按鈕
- 13.4 捕獲事件
- 13.5 文本字段
- 13.6 文本區域
- 13.7 標簽
- 13.8 復選框
- 13.9 單選鈕
- 13.10 下拉列表
- 13.11 列表框
- 13.12 布局的控制
- 13.13 action的替代品
- 13.14 程序片的局限
- 13.15 視窗化應用
- 13.16 新型AWT
- 13.17 Java 1.1用戶接口API
- 13.18 可視編程和Beans
- 13.19 Swing入門
- 13.20 總結
- 13.21 練習
- 第14章 多線程
- 14.1 反應靈敏的用戶界面
- 14.2 共享有限的資源
- 14.3 堵塞
- 14.4 優先級
- 14.5 回顧runnable
- 14.6 總結
- 14.7 練習
- 第15章 網絡編程
- 15.1 機器的標識
- 15.2 套接字
- 15.3 服務多個客戶
- 15.4 數據報
- 15.5 一個Web應用
- 15.6 Java與CGI的溝通
- 15.7 用JDBC連接數據庫
- 15.8 遠程方法
- 15.9 總結
- 15.10 練習
- 第16章 設計模式
- 16.1 模式的概念
- 16.2 觀察器模式
- 16.3 模擬垃圾回收站
- 16.4 改進設計
- 16.5 抽象的應用
- 16.6 多重分發
- 16.7 訪問器模式
- 16.8 RTTI真的有害嗎
- 16.9 總結
- 16.10 練習
- 第17章 項目
- 17.1 文字處理
- 17.2 方法查找工具
- 17.3 復雜性理論
- 17.4 總結
- 17.5 練習
- 附錄A 使用非JAVA代碼
- 附錄B 對比C++和Java
- 附錄C Java編程規則
- 附錄D 性能
- 附錄E 關于垃圾收集的一些話
- 附錄F 推薦讀物