| 网管联盟 | 网管论坛 | 网管u家 | 网管博客 | 网管软件 | 网管求职 | 小游戏 | 网管搜索 | 网管原创 | 网管聚合 | 网管读摘 | 网管焦点 | 世界素材 | 会员投稿 | 会员中心 |
![]() |
| Windows Linux Cisco 网络技术 数据库 黑客攻防 DotNet Java PHP 认证 新闻资讯 服务器 存储资讯 网络设备 网管学堂 技术专题 焦点 网吧频道 |
网管u家u.bitscn@com
图1: KT的界面由一个棋盘, 一个选择开始方格的组合框和一个开始游历的按钮组成
在启动巡游之前, 先从组合框中选择骑士开始的角落。 程序响应会让骑士显示在正确的角落上(默认情况下骑士在最左上角)。 然后单击"Take the Tour"(开始巡游)按钮来开始整个巡游过程。 按钮和组合框在巡游过程中都将被禁止。巡游过程是怎么样的呢? 图2展现了一系列的线段(轨迹), 每一个线段都是随着骑士在棋盘的行动从上一个方格的中心到当前方格的中心。
网管联盟bitsCN_com
图2: 巡游从左上角开始
现在你已经看到了这个小程序的界面和巡游过程, 让我们开始学习它的源代码吧。
清单1. KT.java
网管联盟bitsCN_com
// KT.java
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import java.util.ArrayList;
public class KT extends Applet
{
// 线程延迟以毫秒为单位
public final static int DELAY = 500;
// 开始骑士巡游线程
private Thread thd;
// 初始化小程序
public void init ()
{
// 创建一个标签对象来标明小程序的标题
Label lblTitle = new Label ("Knight's Tour", Label.CENTER);
lblTitle.setFont (new Font ("Arial", Font.BOLD, 18));
// 把标签对象加到小程序的面板容器
add (lblTitle);
// 创建一个ChessBoard对象,它具有显示一个棋盘、移动骑士到
// 任何方格并留下骑士巡游轨迹的能力. 网管u家u.bitsCN.com
final ChessBoard cb = new ChessBoard (this);
//把ChessBoard对象加入到小程序的面板容器
add (cb);
// 创建一个Panel对象来保存Label,Choice和按钮对象.
Panel pnl = new Panel ();
//创建一个标签来标明Choice对象并把它添加到Panel中
pnl.add (new Label ("Choose starting position:"));
//创建一个Choice对象,用来选择骑士的开始位置(棋盘的四个角落)
//并把它添加到Panel中.
final Choice c = new Choice ();
c.add ("Upperleft corner");
c.add ("Upperright corner");
//创建Choice的item listener,这个监听器按选择结果来重设骑士的开始位置.中国网管论坛bbs.bitsCN.com
c.addItemListener (new ItemListener ()
{
public void itemStateChanged (ItemEvent e)
{
Choice c = (Choice) e.getSource ();
if (c.getSelectedIndex () == 0) 网管bitscn_com
cb.moveKnight (1);
else
cb.moveKnight (8);
cb.reset ();
} 网管下载dl.bitscn.com
});
pnl.add (c);
//把Panel加入到小程序的面板容器
add (pnl);
//创建一个按钮对象用来开始骑士巡游.
final Button btn = new Button ("Take the Tour");
//创建按钮的Action listener(动作监听器),用来确定骑士巡游的位置.
//按照规则将骑士从一个位置移动到另一个位置.
ActionListener al;
al = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{ 中国网管论坛bbs.bitsCN.com
Runnable r;
r = new Runnable ()
{
int [] boardPositions1 =
{
1, 18, 33, 50, 60, 54, 64, 47,
32, 15, 5, 11, 17, 34, 49, 59, 中国网管论坛bbs.bitsCN.com
53, 63, 48, 31, 16, 6, 12, 2,
19, 25, 42, 57, 51, 61, 55, 40,
23, 8, 14, 4, 10, 27, 44, 38,
21, 36, 46, 29, 35, 41, 58, 52,
62, 56, 39, 24, 7, 13, 3, 9, 网管联盟bitsCN_com
26, 43, 37, 22, 28, 45, 30, 20
};
int [] boardPositions2 =
{
8, 23, 40, 55, 61, 51, 57, 42,
25, 10, 4, 14, 24, 39, 56, 62, 网管下载dl.bitscn.com
52, 58, 41, 26, 9, 3, 13, 7,
22, 32, 47, 64, 54, 60, 50, 33,
18, 1, 11, 5, 15, 30, 45, 35,
20, 37, 43, 28, 38, 48, 63, 53,
59, 49, 34, 17, 2, 12, 6, 16, 网管联盟bitsCN@com
31, 46, 36, 19, 29, 44, 27, 21
};
public void run ()
{
cb.reset ();
// thd用来检查用户离开小程序网页 网管论坛bbs_bitsCN_com
// 以便停止小程序的运行.
for (int i = 0; i < boardPositions1.length &&
thd != null; i++)
{
if (c.getSelectedIndex () == 0)
cb.moveKnight (boardPositions1 [i]); 网管联盟bitsCN@com
else
cb.moveKnight (boardPositions2 [i]);
try
{
Thread.sleep (DELAY);网管联盟bitsCN@com
}
catch (InterruptedException e2)
{
}
}中国网管论坛bbs.bitsCN.com
c.setEnabled (true);
btn.setEnabled (true);
}
};
c.setEnabled (false);
btn.setEnabled (false);
thd = new Thread (r); 网管联盟bitsCN@com
thd.start ();
}
};
btn.addActionListener (al);
//添加按钮到小程序面板容器
add (btn);
}
//停止小程序
public void stop ()
{
//用户离开网页时必须停止”骑士巡游”线程
thd = null;
}
}
final class ChessBoard extends Canvas
{
//非白色方格的颜色
private final static Color SQUARECOLOR = new Color (195, 214, 242);
//棋盘方格的尺寸
private final static int SQUAREDIM = 40;
//棋盘方格的尺寸(包括黑边框) 网管网www.bitscn.com
private final static int BOARDDIM = 8 * SQUAREDIM + 2;
//棋盘左上角的左坐标(X坐标)
private int boardx;
//棋盘左上角的顶坐标(Y坐标)
private int boardy;
//棋盘长度
private int width;
// 棋盘宽度
private int height;
// 图像缓冲
private Image imBuffer;
// Graphics context associated with image buffer.
private Graphics imG;
// 骑士图像
private Image imKnight;
// 骑士图像的长度
private int knightWidth;
// 骑士图像的宽度
private int knightHeight;
//骑士轨迹的坐标
private ArrayList trail;
// Left coordinate of knight rectangle origin (upper-left corner).
private int ox;网管网www.bitscn.com
// Top coordinate of knight rectangle origin (upper-left corner).
private int oy;
// 创建ChessBoard的Applet--调用它的getImage()和getDocumentBase()方法,
// 并且我们将使用它作为drawImage()方法的image observer
Applet a;
// 构造棋盘
ChessBoard (Applet a)
{
// 确定部件的大小
width = BOARDDIM+1;
height = BOARDDIM+1;
// 初始化棋盘, 使它处于中央
boardx = (width - BOARDDIM) / 2 + 1;
boardy = (height - BOARDDIM) / 2 + 1;
//使用MediaTracker来确保骑士图像在我们获取它的长和宽之前被完全导入
MediaTracker mt = new MediaTracker (this);
// 导入骑士图像中国网管联盟bitsCN.com
imKnight = a.getImage (a.getDocumentBase (), "knight.gif");
mt.addImage (imKnight, 0);
try
{
mt.waitForID (0);
}
catch (InterruptedException e) {}
//获得骑士的长度和宽度, 帮助骑士定位于方格中央
knightWidth = imKnight.getWidth (a);
knightHeight = imKnight.getHeight (a);
//初始化骑士图像, 使骑士定位于左上角方格的中央
ox = boardx + (SQUAREDIM - knightWidth) / 2 + 1;
oy = boardy + (SQUAREDIM - knightHeight) / 2 + 1;
//创建一个数据结构, 用来保存骑士巡游时的轨迹 网管u家u.bitsCN.com
trail = new ArrayList ();
//保存applet引用以便后面调用drawImage()时使用.
this.a = a;
}
// This method is called when the ChessBoard component's peer is created.
// The code in this method cannot be called in the ChessBoard constructor
// because the createImage() method returns null at that point. It doesn't
// return a meaningful value until the ChessBoard component has been added
// to its container. The addNotify() method is not called until the first
// time ChessBoard is added to a container.
public void addNotify ()
{
// Given this object's Canvas "layer" a chance to create a Canvas peer.
super.addNotify ();
//创建图像缓冲 网管论坛bbs_bitsCN_com
imBuffer = createImage (width, height);
//得到图像缓冲的内容。
imG = imBuffer.getGraphics ();
}
//当小程序的布局管理器布置它的组件时,会调用这个方法。
//如果可能,组件会显示为首选大小。
public Dimension getPreferredSize ()
{
return new Dimension (width, height);
}
//移动骑士到指定的棋盘位置。如果位置小于1或大于64则抛出一个异常
public void moveKnight (int boardPosition)
{
if (boardPosition < 1 || boardPosition > 64)
throw new IllegalArgumentException ("invalid board position: " +
boardPosition); 网管bitscn_com
int rebasedBoardPosition = boardPosition-1;
int col = rebasedBoardPosition % 8;
int row = rebasedBoardPosition / 8;
ox = boardx + col * SQUAREDIM + (SQUAREDIM - knightWidth) / 2 + 1;
oy = boardy + row * SQUAREDIM + (SQUAREDIM - knightHeight) / 2 + 1;
trail.add (new Point (ox + knightWidth / 2, oy + knightHeight / 2));
repaint ();
}
//画出所有部件——先棋盘然后是骑士
public void paint (Graphics g)
{
//画出棋盘
paintChessBoard (imG, boardx, boardy);
//画出骑士
paintKnight (imG, ox, oy);
网管联盟bitsCN_com
//画出骑士的轨迹
paintTrail (imG);
//画出图像缓冲的内容
g.drawImage (imBuffer, 0, 0, this);
}
//画出棋盘——(x, y)是左上角坐标
void paintChessBoard (Graphics g, int x, int y)
{
// 画出棋盘的边框
g.setColor (Color.black);
g.drawRect (x, y, 8 * SQUAREDIM + 1, 8 * SQUAREDIM + 1);
//画出棋盘
for (int row = 0; row < 8; row++)
{
g.setColor (((row & 1) != 0) ? SQUARECOLOR : Color.white);
for (int col = 0; col < 8; col++) 网管论坛bbs_bitsCN_com
{
g.fillRect (x + 1 + col * SQUAREDIM, y + 1 + row * SQUAREDIM,
SQUAREDIM, SQUAREDIM);
g.setColor ((g.getColor () == SQUARECOLOR) ? Color.white :
SQUARECOLOR);
}
}
}
//画出骑士——(x, y)是图片左上角坐标
void paintKnight (Graphics g, int x, int y)
{网管联盟bitsCN_com
g.drawImage (imKnight, x, y, a);
}
//画出骑士的轨迹
void paintTrail (Graphics g)
{
g.setColor (Color.black);
int len = trail.size ();
if (len == 0)
return;
Point pt = (Point) trail.get (0);
int ox = pt.x;
int oy = pt.y;
for (int i = 1; i < len; i++)
{
pt = (Point) trail.get (i);
g.drawLine (ox, oy, pt.x, pt.y);
ox = pt.x;
oy = pt.y;中国网管联盟bitsCN.com
}
}
//清空ArrayList来重设棋盘
public void reset ()
{
trail.clear ();
}
// The AWT invokes the update() method in response to the repaint() method
// call that is made as a knight is moved. The default implementation of
// this method, which is inherited from the Container class, clears the
// applet's drawing area to the background color prior to calling paint().
// This clearing followed by drawing causes flicker. KT overrides
// update() to prevent the background from being cleared, which eliminates
// the flicker.
public void update (Graphics g)
{
paint (g);
}
}网管bitscn_com
中国网管联盟bitsCN.com
网管u家u.bitsCN.com
在我们学习清单1的时候,你可能已经注意到了一个遗漏:你可以从左上角或右上角来开始一次巡游,但是却不能从左下角或右下角开始。我故意漏掉这两个角落来让你去实现它。因为每个巡游过程都反映了另一个,为剩下的巡游指出开始位置应该不会太难。
我还有另外一个练习留给你:在选择组件的item监听器中:为什么指定cb.reset();跟在moveKnight()后面呢(参看init()方法)?
回顾
国际象棋提供了许多有趣的娱乐,比如说骑士巡游。它要求骑士从任何方格开始,游历剩余的63个方格而不能重复同一个位置。KT小程序选择了一个简单的实现,限制开始位置为左上角和右上角的方格。在你完成这个小程序之后,请研究一个通用的解决办法使骑士可以从任何方格开始巡游。
关于作者
Jeff Friesen是一个自由作家、软件开发者和教育者。特别擅长于C,C++和Java技术。 网管u家u.bitsCN.com
版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接
原文:http://www.javaworld.com/
译文:http://www.matrix.org.cn/
|
0
|
评论加载中…