OverlayLayout是用于排列重疊組件的布局管理器。它的用途是以一些對齊的點為基準將一些組件層疊的放置在布局容器中。
組件的橫軸和縱軸的對齊點介于0.0和1.0之間。橫軸(X軸)上0.0代表組件的左側面,1.0代表組件的右側面;縱軸(Y軸)上0.0和1.0分別代表組件的頂部和底部。
構造函數
public OverlayLayout(Container target)
因為構造函數不會為target對象安裝結果布局管理器,所以我們還必須調用setLayout()來完成此功能。
JPanel p1 = new JPanel();
OverlayLayout overlay = new OverlayLayout(p1);
p1.setLayout(overlay); 在OverlayLayout布局管理器中,每個組件都有一對橫縱坐標值,每個組件的位置只和它本身的橫縱坐標值有關,換句話說就是組件以他自己的位置作為基準,橫軸上0.0和1.0分別代表組件的左側面和右側面;縱軸上0.0和1.0分別代表組件的頂部和底部,和容器位置無關。如果一個組件的alignmentX屬性設置為0.5,原本左側面的位置對應0.0,現在變成了0.5,那么,現在組件的位置就要向左移動width/2的距離,使左側面的位置對應現在的0.0??v軸亦是如此,明白了嗎?為了容易理解,我們來看《Java Swing》中關于OverlayLayout的一段樣例程序,它可以編譯運行。如圖,你可以在輸入框中調節容器中3個按鈕的X,Y軸的值來看他們在容器中的位置是怎樣改變的,多試幾次,你就可以完全理解OverlayLayout。
// OverlayTest.java
// A test of the OverlayLayout manager allowing experimentation.
//

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;


public class OverlayTest extends JFrame
{


public OverlayTest()
{
super("OverlayLayout Test");
setSize(500, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);

final Container c = getContentPane();
c.setLayout(new GridBagLayout());

final JPanel p1 = new GridPanel();
final OverlayLayout overlay = new OverlayLayout(p1);
p1.setLayout(overlay);

final JButton jb1 = new JButton("B1");
final JButton jb2 = new JButton("B2");
final JButton jb3 = new JButton("B3");

Dimension b1 = new Dimension(60, 50);
Dimension b2 = new Dimension(80, 40);
Dimension b3 = new Dimension(100, 60);

jb1.setMinimumSize(b1);
jb1.setMaximumSize(b1);
jb1.setPreferredSize(b1);
jb2.setMinimumSize(b2);
jb2.setMaximumSize(b2);
jb2.setPreferredSize(b2);
jb3.setMinimumSize(b3);
jb3.setMaximumSize(b3);
jb3.setPreferredSize(b3);

SimpleReporter reporter = new SimpleReporter();
jb1.addActionListener(reporter);
jb2.addActionListener(reporter);
jb3.addActionListener(reporter);

p1.add(jb1);
p1.add(jb2);
p1.add(jb3);

JPanel p2 = new JPanel();
p2.setLayout(new GridLayout(2,6));
p2.add(new JLabel("B1 X", JLabel.CENTER));
p2.add(new JLabel("B1 Y", JLabel.CENTER));
p2.add(new JLabel("B2 X", JLabel.CENTER));
p2.add(new JLabel("B2 Y", JLabel.CENTER));
p2.add(new JLabel("B3 X", JLabel.CENTER));
p2.add(new JLabel("B3 Y", JLabel.CENTER));
p2.add(new JLabel(""));

final JTextField x1 = new JTextField("0.0", 4); // Button1 x alignment
final JTextField y1 = new JTextField("0.0", 4); // Button1 y alignment
final JTextField x2 = new JTextField("0.0", 4);
final JTextField y2 = new JTextField("0.0", 4);
final JTextField x3 = new JTextField("0.0", 4);
final JTextField y3 = new JTextField("0.0", 4);

p2.add(x1);
p2.add(y1);
p2.add(x2);
p2.add(y2);
p2.add(x3);
p2.add(y3);


GridBagConstraints constraints = new GridBagConstraints();
c.add(p1, constraints);

constraints.gridx = 1;
JButton updateButton = new JButton("Update");

updateButton.addActionListener(new ActionListener()
{

public void actionPerformed(ActionEvent ae)
{
jb1.setAlignmentX(
Float.valueOf(x1.getText().trim()).floatValue());
jb1.setAlignmentY(
Float.valueOf(y1.getText().trim()).floatValue());
jb2.setAlignmentX(
Float.valueOf(x2.getText().trim()).floatValue());
jb2.setAlignmentY(
Float.valueOf(y2.getText().trim()).floatValue());
jb3.setAlignmentX(
Float.valueOf(x3.getText().trim()).floatValue());
jb3.setAlignmentY(
Float.valueOf(y3.getText().trim()).floatValue());

p1.revalidate();
}
});
c.add(updateButton, constraints);

constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 2;
c.add(p2, constraints);
}


public static void main(String args[])
{
OverlayTest ot = new OverlayTest();
ot.setVisible(true);
}


public class SimpleReporter implements ActionListener
{

public void actionPerformed(ActionEvent ae)
{
System.out.println(ae.getActionCommand());
}
}


public class GridPanel extends JPanel
{

public void paint(Graphics g)
{
super.paint(g);
int w = getSize().width;
int h = getSize().height;

g.setColor(Color.red);
g.drawRect(0,0,w-1,h-1);
g.drawLine(w/2,0,w/2,h);
g.drawLine(0,h/2,w,h/2);
}
}
}

最后提醒,使用OverlayLayout布局管理器關鍵要記住X,Y軸對應組件位置,和容器沒有關系。只要明白這一點,使用還是很簡單方便的,我用OverlayLayout布局管理器clone了一個PhotoShop的工具面板。

以前寫過2篇關于AWT中圖像加載顯示方法的文章,最近又多了一些對于 ImageProducer / ImagConsumer 模式的一些理解,嘗試著用文字總結了一下,接著還想再寫一篇介紹 AWT 中圖像過濾的原理和方法。你可能認為現在學習 AWT 中的成像方法對于開發中已經沒有太大的意義,因為已經有了 Java 2D 和 JAI ,但是我在實際工作中感到還是無法完全離開 AWT,特別是在一些基本的應用上。而且我覺得理解 AWT 的 Producer / Consumer (push) model 對于理解 Java 2D 的 Immediate mode model 和 JAI 的 Pipeline (pull) model 的都是有好處的。
因為水平有限,這方面學習資料相對也不豐富,我也不知道我的理解或想法是否完全正確或者表述清楚,感興趣的朋友可以當作學習參考,希望能夠和我聯系進行進一步的討論。
AWT 使用 ImageProducer / ImagConsumer 模式,支持加載和顯示 GIF 圖像文件格式和 JPEG 圖像文件格式。因為圖像的加載和顯示是異步方式進行的,所以有大量加載和顯示的技術。
在 AWT 中,提供了一個 java.awt.Image 類。java.awt.Image 類代表一個圖像對象被作為參數傳遞給其他用來顯示和處理圖像的其他 AWT 對象使用。例如,通過調用 Graphics.drawImage(java.awt.Image, int, int, ImageObserver) 方法,可以在組件中畫出圖像。
java.awt.Image 是一個定義方法的抽象類,它定義的方法提供的對圖像信息的訪問。而創建和處理圖像的基本結構則在 java.awt.image 包中。注意,這里不要和 java.awt.Image 發生混淆。
AWT? 加載和顯示圖像使用的是 ImageProducer / ImagConsumer 模式,我們必須了解3個術語,ImageProducer(圖像生產者),ImageConsumer(圖像消費者)和ImageObserver(圖像觀察者)。
ImageProducer 負責生產圖像的位,ImagConsumer 接受圖像的位,ImageObserver 監視 ImageProducer 的圖像生產過程。ImageProducer 生產傳遞給 ImagConsumer 與圖像相關的位。因為圖像生產過程是異步進行的,并不是一次性生產所有圖像位,所以當 ImageProducer 加載圖像時,ImageObserver 用來監視它的進展情況。因為 java.awt.Component 實現了 ImageObserver 接口,所以 AWT 中的每個組件都可以是ImageObserver,當一個給定的 ImageProducer 進行異步操作時,這個 ImageObserver 可以選擇是否被更新。java.awt.image 包為 ImageProducer,ImagConsumer 和 ImageObserver 都定義了接口。
ImageProducer
和圖像相關的位并不存儲在 java.awt.Image 中,每個圖像都維護一個和一個 ImageProducer?的關聯。這個 ImageProducer?的責任是生產圖像的位并將它們傳送給 ImagConsumer,用于過濾該圖像。
java.awt.image軟件包中,FilteredImageSource(被過濾的圖像源)和 MemoryImageSource(內存的圖像源)實現了 ImageProducer? 接口,是 ImageProducer?。
ImagConsumer
java.awt.image軟件包中,ImageFilter(圖像過濾器)和 PixelGrabber(像素抓取器)實現了 ImagConsumer 接口,是 ImagConsumer。
ImageProducer?和 ImagConsumer 的詳細介紹請閱讀 使用 ImageProducer? / ImagConsumer 進行圖像過濾
ImageObserver
ImageObserver接口中,定義了一個常數集合和一個方法:
public boolean imageUpdate(image img, int flags, int x, int y, int width, int height);
ImageObserver的常數 |
標志 |
含義 |
ABORT |
圖像加載被中斷 |
ALLBITS |
所有的位都已加載給圖像 |
ERROR |
在加載過程中發生錯誤 |
FRAMEBITS |
多幀圖像的一個幀被傳送,一般用于GIF |
HEIGHT |
圖像的高度已經可用 |
PROPERTIES |
圖像的屬性已經可用 |
SOMEBITS |
圖像的縮放變體的多個位已經可用 |
WIDTH |
圖像的寬度已經可用 |
參數 flags 的作用是通知圖像觀察者圖像生產的進展情況。這些常數代表傳遞給 ImageObserver.imageUpdate() 的 flags 參數中的位。
當 Component 作為 ImageObserver 時,一旦圖像有新的部分被加載,就會調用 Component.imageUpdate() 方法,imageUpdate() 再調用 repaint() 重新繪制圖像。repaint() 將先調用 update() 方法清除組件背景,再由 update() 方法調用 paint() 方法繪制圖像。我們可以通過重載 imageUpdate() 方法控制組件何時被更新,重載 update() 方法控制是否每次重繪都要清除背景圖像(每次重繪都清除背景圖像會造成畫面閃爍)。
為了更好的理解,下面我們來看幾個樣例程序:
例1,圖像位在需要之前不被生產
import?java.net.URL;
import?java.applet.Applet;
import?java.awt.Graphics;
import?java.awt.Image;


public?class?ImageTestAppletSimple?extends?Applet
{
????private?Image?im;
????

????public?void?init()
{
????????URL?codebase?=?getCodeBase();
????????System.out.println(codebase);
????????
????????im?=?getImage(codebase,"flower.jpg");
????????
????????System.out.print("Image?width?=?"?+?im.getWidth(this));
????????System.out.println(" hight?=?"?+?im.getHeight(this));
????}
????

????public?void?paint(Graphics?g)
{
????????g.drawImage(im,0,0,this);
????}
}
輸出結果:
image width = -1 height = -1
圖像的高度和寬度只有在圖像被完全加載后才是有效的,輸出結果說明 java.awt.image 相關的圖像位在需要之前不被生產。
例2,圖像異步生產
import?java.net.URL;
import?java.applet.Applet;
import?java.awt.Graphics;
import?java.awt.Image;


public?class?ImageTestAppletSimple2?extends?Applet
{
????private?Image?im;
????

????public?void?init()
{
????????im?=?getImage(getCodeBase(),"flower.jpg");
????}
????

????public?void?paint(Graphics?g)
{
????????System.out.println("drawing?image...");
????????System.out.println(g.drawImage(im,0,0,this));
????}
}
輸出結果:
drawing?image...
false
drawing?image...
false
drawing?image...
false
drawing?image...
true
輸出結果說明組件作為 ImageObserver ,監視 ImageProducer 異步生產圖像,一旦有新的圖像位被生產時就重繪圖像,圖像完全加載后 drawImage() 方法返回 true。
例3,重載 ImageObserver 的 imageUpdate() 方法,在圖像完全加載前不調用 repaint()
import?java.applet.Applet;
import?java.awt.Graphics;
import?java.awt.Image;


public?class?ImageTestAppletWithUpdate?extends?Applet
{
????private?Image?im;
????

????public?void?init()
{
????????im?=?getImage(getCodeBase(),"flower.jpg");
????????
????????System.out.print("Image?width?=?"?+?im.getWidth(this));
????????System.out.println("hight?=?"?+?im.getHeight(this));
????}
????

????public?void?paint(Graphics?g)
{
????????g.drawImage(im,0,0,this);
????}
????

????public?boolean?imageUpdate(Image?image,int?flags,int?x,int?y,int?w,int?h)
{
????????System.out.println("imageUpdate():x="?+?x?+?",y="?+?y?+?",w="?+?w?+?",h="?+?h);
????????
????????if((flags?&?ALLBITS)?==?0)
????????????return?true;
????????else

????????
{
????????????repaint();
????????????return?false;
????????}
????}
}
例4,重載? Component.update() 方法,被調用時不清除背景圖像,直接調用 paint() 方法繪制圖像,消除閃爍
import?java.applet.Applet;
import?java.awt.Graphics;
import?java.awt.Image;


public?class?ImageTestAppletWithSmoothDynamicUpdate?extends?Applet
{
????private?Image?im;
????

????public?void?init()
{
????????im?=?getImage(getCodeBase(),"hl.jpg");
????????
????????System.out.print("Image?width?=?"?+?im.getWidth(this));
????????System.out.println("hight?=?"?+?im.getHeight(this));
????}
????

????public?void?paint(Graphics?g)
{
????????g.drawImage(im,0,0,this);
????}
????

????public?boolean?imageUpdate(Image?image,int?flags,int?x,int?y,int?w,int?h)
{
????????System.out.println("imageUpdate():x="?+?x?+?",y="?+?y?+?",w="?+?w?+?",h="?+?h);
????????
????????repaint();
????????
????????if((flags?&?ALLBITS)?==?0)
????????????return?true;
????????else
????????????return?false;
????}
????

????public?void?update(Graphics?g)
{
????????paint(g);
????}
}
?