AWT允许在组件上绘制位图,Graphics提供了drawImage方法用于绘制位图,该方法需要一个Image参数——代表位图,通过该方法就可以绘制出指定的位图。

(一)Image抽象类和BufferedImage实现类

Image类代表位图,但它是一个抽象类,无法直接创建Image对象,为此Java为它提供了一个BufferedImage子类,这个子类是一个可访问图像数据缓冲区的Image实现类。

BufferedImage类提供了一个简单的构造器,用于创建一个BufferedImage对象:

(1)BufferedImage(int width, int height, int imageType):创建指定大小、指定图像类型的BufferedImage对象、其中ImageType可以是BufferedImage.TYPE_INT_RGB、BufferedImage.TYPE_BYTE_GRAY等值

除此之外,BufferedImage还提供了一个getGraphics()方法返回该对象的Graphics对象,从而允许通过该Graphics对象向Image中添加图形

借助于BufferedImage的帮助,我们可以在AWT中实现缓冲技术——当需要向GUI组件上绘制图形时,不要直接绘制到该GUI组件上,而是先将图形绘制到BufferedImage对象中,然后再调用组件的drawImage方法一次性地将BufferedImage对象绘制到特定组件上

/**
 * 程序通过BufferedImage类实现了图形缓冲,并实现了一个简单的手绘程序
 */
package codes11;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

public class HandDraw {
	// 画图区的宽度
	private final int AREA_WIDTH = 500;
	// 画图区的高度
	private final int AREA_HEIGHT = 400;
	// 下面preX、preY保存了上一次鼠标拖动事件的鼠标坐标
	private int preX = -1;
	private int preY = -1;
	// 定义一个右键菜单用于设置画笔颜色
	PopupMenu pop = new PopupMenu();
	MenuItem redItem = new MenuItem("红色");
	MenuItem greenItem = new MenuItem("绿色");
	MenuItem blueItem = new MenuItem("蓝色");
	// 定义一个BufferedImage对象
	BufferedImage image = new BufferedImage(AREA_WIDTH, AREA_HEIGHT,
			BufferedImage.TYPE_INT_RGB);
	// 获取image对象的Graphics
	Graphics g = image.getGraphics();
	private Frame f = new Frame("简单手绘程序");
	private DrawCanvas drawArea = new DrawCanvas();
	// 用于保存画笔颜色
	private Color foreColor = new Color(255, 0, 0);

	public void init() {
		// 定义右键菜单的事件监听器
		ActionListener menuListener = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (e.getActionCommand().equals("红色")) {
					foreColor = new Color(255, 0, 0);
				}
				if (e.getActionCommand().equals("绿色")) {
					foreColor = new Color(0, 255, 0);
				}
				if (e.getActionCommand().equals("蓝色")) {
					foreColor = new Color(0, 0, 255);
				}
			}
		};
		// 为三个菜单添加事件监听器
		redItem.addActionListener(menuListener);
		greenItem.addActionListener(menuListener);
		blueItem.addActionListener(menuListener);
		// 将菜单项组合成右键菜单
		pop.add(redItem);
		pop.add(greenItem);
		pop.add(blueItem);
		// 将右键菜单添加到drawArea对象中
		drawArea.add(pop);
		// 将image对象的背景色填充成白色
		g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT);
		drawArea.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));
		// 监听鼠标移动动作
		drawArea.addMouseMotionListener(new MouseMotionAdapter() {

			// 实现按下鼠标键并拖动的事件处理器
			@Override
			public void mouseDragged(MouseEvent e) {
				// 如果preX和preY大于0
				if (preX > 0 && preY > 0) {
					// 设置当前颜色
					g.setColor(foreColor);
					// 绘制从上一次鼠标拖动事件点到本次鼠标拖动事件点的线段
					g.drawLine(preX, preY, e.getX(), e.getY());
				}
				// 将当前鼠标事件点的X、Y坐标保存起来
				preX = e.getX();
				preY = e.getY();
				// 重绘drawArea对象
				drawArea.repaint();
			}
		});
		// 监听鼠标事件
		drawArea.addMouseListener(new MouseAdapter() {
			// 实现鼠标键松开的事件处理器
			public void mouseReleased(MouseEvent e) {
				// 弹出右键菜单
				if (e.isPopupTrigger()) {
					pop.show(drawArea, e.getX(), e.getY());
				}
				// 松开鼠标时,把上一次鼠标拖动时间的X、Y坐标设为-1
				preX = -1;
				preY = -1;
			}
		});
		f.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		f.add(drawArea);
		f.pack();
		f.setVisible(true);
	}

	class DrawCanvas extends Canvas {
		// 重写Canvas的paint方法,实现绘图
		public void paint(Graphics g) {
			// 将image绘制到该组件上
			g.drawImage(image, 0, 0, null);
		}
	}

	public static void main(String[] args) {
		new HandDraw().init();
	}
}

(二)使用ImageIO输入/输出位图

如果希望可以访问磁盘上的位图文件,例如GIF、JPG等格式的位图,则需要利用ImageIO工具类。ImageIO利用ImageReader和Imagewriter读写图形文件,通常程序无须关心该类底层的细节,只需要利用该工具类来读写图形文件即可。

ImageIO类并不支持读写全部格式的图形文件,程序可以通过ImageIO类的如下几个静态方法来访问该类所支持读写的图形文件格式:

(1)static String[] getReaderFileSuffixes():返回一个String数组,该数组列出ImageIO所有能读的图形文件的后缀

(2)static String[] getReaderFormatNames():返回一个String数组,该数组列出ImageIO所有能读的图形文件的非正式格式名称

(3)static String[] getWriterFileSuffixes():返回一个String数组,该数组列出ImageIO所有能写的图形文件的后缀

(4)static String[] getWriterFormatNames():返回一个String数组,该数组列出ImageIO所有能写的图形文件的非正式格式名称

/**
 * 程序测试了ImageIO所支持读写的全部文件格式
 */
package codes11;

import javax.imageio.ImageIO;

public class ImageIOTest {

	public static void main(String[] args) {
		String[] readFormat = ImageIO.getReaderFormatNames();
		System.out.println("-----Image 能读的所有图形文件格式-----");
		for (String tmp : readFormat) {
			System.out.println(tmp);
		}
		String[] writeFormat = ImageIO.getWriterFormatNames();
		System.out.println("-----Image 能写的所有图形文件格式-----");
		for (String tmp : writeFormat) {
			System.out.println(tmp);
		}
	}

}

运行程序,输入结果如下:

-----Image 能读的所有图形文件格式-----
JPG
jpg
bmp
BMP
gif
GIF
WBMP
png
PNG
jpeg
wbmp
JPEG
-----Image 能写的所有图形文件格式-----
JPG
jpg
bmp
BMP
gif
GIF
WBMP
png
PNG
wbmp
jpeg
JPEG


ImageIO类包含两个静态方法:read()和writer(),通过这两个方法即可完成对位图文件的读写,调用write()方法输出图形文件时需要指定输出的图形格式,例如GIF、JPEG等