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。纵轴亦是如此,明白了吗?为了容易理解,我们来看《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);
}
}