这是学习了Java2游戏编程的Java API章节中的平铺图像这一部分后的总结。
这个Applet实现了一个ItemListener接口。这是个新东西,我们来看看它的具体作用。
ItemListener其实是个监听器接口(谁都知道)。这与javascript中我们对与鼠标单击等事件的处理类似。ItemListener主要用于像ComboBox这类的,具有明显状态的控件。而ItemListener得作用之一就是监听这类控件的状态变化。ItemState改变的时候,函数itemStateChanged(ItemEvent e)就会被调用。这里需要注意的是,改变一次下拉列表框的选择会触发2次itemStateChanged,因为上一次的选项由selected变成了unselected,而当前的选项由unselected变成了selected。
好了,知道了ItemListener的主要功能,我们就可以开始着手于代码的分析了,代码如下:
package bear.game.titleimage;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Panel;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
public class TileImage extends Applet implements ItemListener{
private Hashtable imageTable;
private Choice selections;
private int imageSize;
private final String[] filenames = {"water.GIF", "grass.gif"};
@Override
public void init()
{
int n = filenames.length;
imageTable = new Hashtable(n);
selections = new Choice();
Panel p = new Panel();
p.add(selections, BorderLayout.SOUTH);
p.setBackground(Color.RED);
setLayout(new BorderLayout());
add(p, BorderLayout.SOUTH);
selections.addItemListener(this);
for(int i = 0 ; i < n ; i++)
{
URL url;
try {
url = new URL(getCodeBase()+"\\"+filenames[i]);
//BufferedImage img = ImageIO.read(url);
Image img = getImage(getCodeBase(), filenames[i]);
MediaTracker media = new MediaTracker(this);
media.addImage(img, 1);
try {
media.waitForID(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(img.getHeight(this));
imageTable.put(filenames[i], img);
selections.add(filenames[i]);
if(i == 0)
{
imageSize = img.getHeight(this);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
int width = getSize().width;
int height = getSize().height;
AffineTransform at = new AffineTransform();
Image curImage = (Image)imageTable.get(selections.getSelectedItem());
int y = 0;
while(y < height)
{
int x = 0;
while(x < width)
{
at.setToTranslation(x, y);
g2d.drawImage(curImage, at, this);
x += imageSize;
}
y += imageSize;
}
}
public void itemStateChanged(ItemEvent arg0) {
// TODO Auto-generated method stub
repaint();
}
}
用书中给的示例总是卡住,所以这里我将书中的
while(image.getWidth(this) < 0);
...
这句改掉了,我利用MediaTracker追踪图片的加载状态。MediaTracker对象的waitForID(int index)方法在加载完由ID指定的图片以前一直等待。
此处还可以使用BufferedImage进行操作,效果更好。利用BufferedImage进行处理的代码如下:
package bear.game.titleimage;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Panel;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.Hashtable;
import javax.imageio.ImageIO;
public class TileImage extends Applet implements ItemListener{
private Hashtable imageTable;
private Choice selections;
private int imageSize;
private final String[] filenames = {"water.GIF", "grass.gif"};
@Override
public void init()
{
int n = filenames.length;
imageTable = new Hashtable(n);
selections = new Choice();
Panel p = new Panel();
p.add(selections, BorderLayout.SOUTH);
p.setBackground(Color.RED);
setLayout(new BorderLayout());
add(p, BorderLayout.SOUTH);
selections.addItemListener(this);
for(int i = 0 ; i < n ; i++)
{
URL url;
try {
url = new URL(getCodeBase()+"\\"+filenames[i]);
BufferedImage img = ImageIO.read(url);
imageTable.put(filenames[i], img);
selections.add(filenames[i]);
if(i == 0)
{
imageSize = img.getHeight(this);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//...
}
以上说的只是加载图片的部分,接下来说明的是如何让ComboBox的选择与Applet的paint方法相联系。
这里我们定义了一个Hashtable,它是java内置的,我们用它来进行图片以及名字的对应关系的存储。同时我们还定义了Choice,也即是我们所谓的ComboBox。
然后我们的主要思想是通过在paint()中将Choice中选中的字符串传递给Hashtable,然后筛选出我们需要的图片,然后绘制它的平铺图,这部分代码参见init函数。
(注意Hashtable的put和get方法的使用)
接下来我们来看平铺是怎么画的。思想比较清晰,就是从(0, 0)开始,不断绘制图片,直到下一次绘制的左上角已经超出了程序界面的边界时,我们才停止绘制。代码如下:
//...
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
int width = getSize().width;
int height = getSize().height;
AffineTransform at = new AffineTransform();
Image curImage = (Image)imageTable.get(selections.getSelectedItem());
int y = 0;
while(y < height)
{
int x = 0;
while(x < width)
{
at.setToTranslation(x, y);
g2d.drawImage(curImage, at, this);
x += imageSize;
}
y += imageSize;
}
}
//...
这里我们使用的图片是长宽相等的,所以统一用imageSize定义。
实现的效果嘛,自己实现吧,我不想贴图片啊,因为要是以后连接失效了,只剩下个叉叉很丑的。( ̄□ ̄||)