仿xp画板、画图重绘、五子棋总结

学了画板、画图重绘已经有些时日的,一直没写好总结。前几天过去蓝杰补了一节课,学习了五子棋的人人对战,自己在考试周期间用了写琐碎的时间把那些代码都捉摸了一遍,今天写些自己的体会。也顺便帮自己复习一下。
在学习仿xp画板的时候,我最大的困惑就是一个类中为什么要声明这样或那样的属性或者是顶这样或那样的方法,以及参数传递方面的一些问题。而关于方法的方法体还是能比较容易接受的。这次总结我也这要谈谈自己对这个困惑关于自己的一些见解。
我们就拿画板来说。仿xp画板,一共用了五个类:
1.画板界面类(DrawingFrame)(拥有主函数的类)
2.图形形状选择工具类(ToolsPanel)
3.颜色选择类(ColorsPanel)
4.画图区域类(DrawingPanel)
5.画板鼠标监听器类(DrawingPanel)
1.画板界面类;该类继承了JFrame类,本身就是一个界面,该类中除了主函数外还要定义一个初始界面的方法,在该方法中根据xp画板的一些属性来设置面板的属性(大小、布局等)。

2.图形形状面板类;该类继承JPanel类,该类能实现的功能就是提供图形形状的选择,所以该类必须声明形状属性(value),而且必须定义有获取形状的方法(getValue()),根据xp画板我们知道图形形状必须在鼠标点击相应的图形形状按钮时获得,所以就必须有动作监听器,在该类中以匿名内部类的形式创建。而关于按钮的添加可以利用数组,而为了动作监听器能起作用,就必须给按钮相应形状的动作命令值,关于按钮上图形的设置,可参考按钮JButton的构造方法(jdk1.6中可查)。(别忘了给按钮添加上动作监听器对象)
以下代码可供参考:


/*
* 图形形状选择工具类
*/
public class ToolsPanel extends JPanel {
// 声明一个形状属性
private String value = "Line";

// 定义一个获取形状的方法
public String getValue() {
return value;
}

// 在内部创建一个动作监听器
ActionListener a1 = new ActionListener() {

public void actionPerformed(ActionEvent e) {
value = e.getActionCommand();
System.out.println("你点击的是" + value);
}
};

// 创建一个构造方法
public ToolsPanel() {
// 实例化一个新的面板
JPanel jp = new JPanel();
// 设置该面板布局为网格布局
jp.setLayout(new GridLayout(5, 2, 5, 5));
// 用数组实例化五个按钮添加到该面板上
String[] array = { "images/LineTool.jpg", "images/RectTool.jpg",
"images/PolygonTool.jpg", "images/OvalTool.jpg",
"images/RoundRectTool.jpg", "images/SprayGunTool.jpg","images/EraserTool.jpg","images/ColorPickedTool.jpg","images/BrushTool.jpg","images/PencilTool.jpg"};
for(int i=0;i<array.length;i++){
// 实例化图标,并添加到按钮上

ImageIcon iamge = new ImageIcon(array[i]);
JButton btn = new JButton(iamge);
// 设置按钮大小
btn.setPreferredSize(new Dimension(20, 20));
// 截取字符串
String item = array[i].substring(array[i].indexOf("/") + 1,
array[i].lastIndexOf("Tool.jpg"));
// 给按钮设置动作命令值
btn.setActionCommand(item);
// 给按扭添加动作监听器
btn.addActionListener(a1);
jp.add(btn);

}
// 把该面板添加到整个面板上
this.add(jp);
}

}


3。颜色选择类:该类同样继承了JPanel类,同理该类提供颜色选择功能,固必须声明颜色属性(color),和定义获取颜色的方法(getColor),同理颜色也是在点击按钮时获得,固也应该有动作监听器,该监听器也是用匿名内部类的形式创建,而按钮上的颜色使用按钮的背景颜色,所以在想要获取该颜色必须将鼠标点击获取的事件源强制转化成按钮对象,用按钮对象获取背景颜色的方法(getBackground)即可获得。
以下的代码可参考:



/*
* 颜色选择类
*/
public class ColorsPanel extends JPanel{

//声明颜色属性
private Color color=Color.BLACK;
//定义一个获取颜色的方法
public Color getColor(){
return color;
}
//定义一个设置颜色的方法
public void setColor(Color color){
this.color = color;
}

//在内部创建一个动作监听器
ActionListener a1=new ActionListener(){

public void actionPerformed(ActionEvent e) {
JButton btn=(JButton)e.getSource();
color=btn.getBackground();
System.out.println("你选择的颜色是"+color);

}};
//创建一个构造方法
public ColorsPanel(){
//设置整个面板为流式布局,并从左到右
this.setLayout(new FlowLayout(FlowLayout.LEFT));
//实例化一个新的面板
JPanel jp=new JPanel();
//设置该面板布局为网格布局
jp.setLayout(new GridLayout(2,7,4,4));
//用数组实例化一些按钮
Color[] array={Color.BLACK,Color.BLUE,Color.DARK_GRAY,
Color.GREEN,Color.ORANGE,Color.PINK,Color.RED,Color.YELLOW,
Color.cyan,Color.MAGENTA,Color.WHITE,Color.LIGHT_GRAY,Color.CYAN,Color.darkGray};
for(int i=0;i<array.length;i++){
JButton btn=new JButton();
//设置按钮大小
btn.setPreferredSize(new Dimension(20,20));
//设置按钮的背景颜色
btn.setBackground(array[i]);
//给按钮添加动作监听器
btn.addActionListener(a1);
//把按钮添加到该面板上
jp.add(btn);
}
//把该面板添加到整个面板上
this.add(jp);

}

}


4.画图区域类:该类同样继承了JPanel,我们知道xp画板可画图区域并不是整个界面区域,而是中部的一小块区域,为类能达到同样的效果,在画图区域类中,我们要在实例化一个小面板作为画图区域添加到整个大面板上(因为该类继承了JPanel所以本身就是一个大面板,用this.add()方法添加),为了能在这个小面板上画图,我们知道肯定要在它上面获得画布对象,所以在该类中首先必须定义有获取面板的方法(getPanel()),既然该方法要去作用,哪么就必须声明一个面板对象属性。
以下代码可参考:



/*
*画图区域类
*/
public class DrawingPanel extends JPanel {
//声明一个面板对象属性
private JPanel jp=new JPanel();
//创建一个构造方法
public DrawingPanel(){
//设置整个面板的布局为流式布局,并从左到右
this.setLayout(new FlowLayout(FlowLayout.LEFT));
jp.setPreferredSize(new Dimension(450,450));
jp.setBackground(Color.WHITE);
this.setBackground(Color.darkGray);

//把该面板添加到正给面板上
this.add(jp);
}
//定义一个获取面板对象的方法
public JPanel getG(){
return jp;
}

}


5.画板鼠标监听器类:该类实现Mouselistener和MOuseMOtionListener接口。
该类实现的功能是:能在画图区域上画图我们选择的不同形状不同颜色的图形,所以它和我们前面创建的图形形状选择类、颜色选择类、画图区域类都相关联。要使得该类能选择图形形状和图形颜色,那么就必须声明我们创建好的图形形状选择类对象属性和颜色选择类对象属性,要画图那么必须有画布,所以也要声明画布对象属性,根据不同图形的需要我们也要声明一些整形变量用来存储坐标值,或者用来做判断。有了这些我们就可以画图了。
以下代码可参考:
package 


/*
* 画板鼠标监听器类
*/
public class DrawingListener implements MouseListener,MouseMotionListener {
//声明一个画布属性
private Graphics g;
//声明一个工具类对象属性和颜色类对象属性,用来获取形状和颜色
private ToolsPanel tp;
private ColorsPanel cp;
private JPanel panel;//panel用来获取该对象在屏幕上的x,y坐标以及对象的宽和高
//声明四个整形数据属性用来存储坐标点
private int x1,y1,x2,y2;
//声明四个整型数据用来存储前两个点的坐标
private int tx1,tx2,ty1,ty2;
//声明一个count整形数据来计数
private int count=1;

//定义一个构造方法
public DrawingListener(Graphics g,ToolsPanel tp,ColorsPanel cp,JPanel panel){
this.g=g;
this.cp=cp;
this.tp=tp;
this.panel = panel;
}

public void mousePressed(MouseEvent e) {
x1=e.getX();
y1=e.getY();


}

public void mouseReleased(MouseEvent e) {
x2=e.getX();
y2=e.getY();
//设置画布颜色
g.setColor(cp.getColor());
//判断选择的形状
if(tp.getValue().equals("Line")){
g.drawLine(x1,y1,x2,y2);
}else if(tp.getValue().equals("Oval")){
g.drawOval(x1,y1,Math.abs(x1-x2),Math.abs(y1-y2));
}
else if(tp.getValue().equals("Rect")){
g.drawRect(x1, y1, Math.abs(x1-x2),Math.abs(y1-y2));
}else if(tp.getValue().equals("RoundRect")){
g.drawRoundRect(x1, y1, Math.abs(x1-x2), Math.abs(y1-y2), 40,40);
}else if(tp.getValue().equals("Polygon")){
//如果是第一次点击,则画出第一条直线
if(tx2!=x2||ty2!=y2)
{if(count==1){
g.drawLine(x1,y1,x2,y2);
count++;
//把前两点坐标保存下来
tx1=x1;
ty1=y1;
tx2=x2;
ty2=y2;
}else{
g.drawLine(tx2,ty2,x2,y2);
//改变结束点
tx2=x2;
ty2=y2;
}}else{
g.drawLine(tx1, ty1, x2, y2);
}

}else if(tp.getValue().equals("ColorPicked")){
try {
//实例化一个Robot类的对象
Robot robot = new Robot();
//获取到panel对象位于屏幕的位置
Point point = panel.getLocationOnScreen();
Rectangle rect = new Rectangle((int)point.getX(),(int)point.getY(),panel.getWidth(),panel.getHeight());
//开始截取一个BufferedImage的对象
BufferedImage image = robot.createScreenCapture(rect);
//获取点击的坐标点的颜色值
Color color = new Color(image.getRGB(x2, y2));
//输出
System.out.println("你用吸管获取的颜色是:"+color);
cp.setColor(color);
} catch (AWTException e1) {
e1.printStackTrace();
}
}

}

public void mouseDragged(MouseEvent e) {
//设置画布颜色
g.setColor(cp.getColor());
//判断选择的形状
if(tp.getValue().equals("Brush")){
x2=e.getX();
y2=e.getY();
//设置线条的粗细,但是Graphics类的对象是不能设置线条粗细的,要使用Graphics2D才可以实现
Graphics2D g2d = (Graphics2D)g;
//设置线条的粗细
g2d.setStroke(new BasicStroke(8));
//开始绘制粗的线条
g2d.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
//设置线条的粗细
g2d.setStroke(new BasicStroke(1));
}else if(tp.getValue().equals("SprayGun")){
x2=e.getX();
y2=e.getY();
//实例化一个随机数类的对象
Random rand = new Random();
for(int i=0;i<20;i++){
int tx = rand.nextInt(8);
int ty = rand.nextInt(8);
//开始绘制粗的线条
g.drawLine(x2+tx, y2+ty, x2+tx, y2+ty);
}
}else if(tp.getValue().equals("Pencil")){
x2=e.getX();
y2=e.getY();
//
Graphics2D g2d = (Graphics2D)g;
//设置线条的粗细
g2d.setStroke(new BasicStroke(1));
g2d.drawLine(x1,y1,x2,y2);
x1=x2;
y1=y2;
}else if(tp.getValue().equals("Eraser")){
x2=e.getX();
y2=e.getY();
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(8));
g2d.setColor(Color.WHITE);
g2d.drawLine(x1,y1,x2,y2);
x1=x2;
y1=y2;

}
}

public void mouseClicked(MouseEvent e) {

}

public void mouseEntered(MouseEvent e) {

}

public void mouseExited(MouseEvent e) {

}

public void mouseMoved(MouseEvent e) {

}

}



重点分析参数传递:(参数传递的作用在五子棋中体现得更明显)
在画板鼠标监听器中,我们声明了图形形状选择类对象属性和颜色选择对象属性,我们并不能在这个类中实例化这些对象,我们知道最终程序的运行是在拥有主函数的类中所以这些对像都必须在面板界面类中实例化。然后通过参数传递,这就必须我们定义相应的构造方法,如画板鼠标监听器类中的构造方法该方法的形式参数有ToolsPanel tp、ColorsPanel cp、Graphics g;这样在界面类中实例化画板鼠标监听器类对象时便可将在界面类中实例化好的相应对象作为实参传递给鼠标监听器类了。
以下界面类的代码可参考:

package 仿xp画板;


/*
*画板界面类
*/
public class DrawingFrame extends JFrame{
//主函数
public static void main(String[] args){
//实例化一个DrawingJRrame类的对象并调用显示界面的方法
DrawingFrame df=new DrawingFrame();
df.init();


}
//定义一个显示主界面的方法
public void init(){
//设置窗体title
this.setTitle("仿xp画板");
//设置窗体大小
this.setSize(new Dimension(600,600));
//设置窗体居中显示
this.setLocationRelativeTo(null);
//设置窗体为边框布局
this.setLayout(new BorderLayout());
//设置窗体关闭时,程序停止运行
this.setDefaultCloseOperation(3);

//实例化一个工具面板对象,并添加到窗体的西边
ToolsPanel tp=new ToolsPanel();
this.add(tp,BorderLayout.WEST);

//实例化一个颜色面板对象,并添加到窗体的南边
ColorsPanel cp=new ColorsPanel();
this.add(cp,BorderLayout.SOUTH);

//实例化一个画图区域面板对象,并添加到窗体的中部
DrawingPanel dp=new DrawingPanel();
this.add(dp,BorderLayout.CENTER);


//设置窗体可见
this.setVisible(true);


//调用dp中获取面板对象的方法(用这样获取画图区域中的小面板)
JPanel panel=dp.getG();

Graphics g=panel.getGraphics();//(并在该小面板中获取画布)


//实例化一个画图鼠标监听器
DrawingListener dl=new DrawingListener(g, tp, cp,panel);//参数传递

//给画布添加画图鼠标监听器
panel.addMouseListener(dl);
//给画布添加鼠标移动监听器方法,绑定事件处理类对象dl
panel.addMouseMotionListener(dl);
}
}


以上是关于仿xp画板的总结,在代码中有些在面板中在实例化面板并将实例化好的面板添加到整个面板上,这样经过一些处理(布局调整等)能使整个界面效果更接近于xp画板。


五子棋(只实现人人对战功能)总结

五子棋实现步骤:
一,创建五子棋界面类class ChessUi{}该类继承JFrame类,在主函数中实例化该类对象并调用初始界面的方法。那么这样就要定义相应的初始界面的方法ChessBorder(),在该方法中设置界面的一些基本属性(大小,居中等)。有了界面开始要画棋盘,在该类中定义一个画棋盘的方法,该方法要在重写该类父类paint()方法中调用以实现重绘。
1,该步骤定义画棋盘的方法就需要一些关于棋盘的信息,所以我们就创建一个五子棋信息接口interface Config{},来存储所有关于棋盘和棋子的信息(棋盘水平线条数、垂直线条数、棋盘方格大小、棋子大小、棋盘的起始坐标),这些信息全部用定义成常量Public static final 数据类型 常量名;这样我们就可以用接口名直接调用到这些信息了;关于画棋盘的方法只用用两层循环和利用棋盘信息便可画出,这边就不做介绍。

二、有了棋盘,我们就可以开始下棋了。下棋其实是在界面中画上一个填充的圆形,和画图是一样的。这样我们就必须要有鼠标监听器。
创建一个五子棋鼠标监听器类class ChessListener{},该类继承MouseAdapt抽象类在下棋时有这样几个问题要考虑:
1,必须是一次下黑棋一次下白棋,这样我们就要声明一个整形计数量来判断,实现代码如下:
  	 int count=1;
if(count==1){
画黑棋;
count++;
}else{
画白棋;
count--;
}


2,每次下棋的时候原来有下过棋子的地方不能再下,这就要求我们去判断某位置是有已经有棋子。按着这是思路,我们可以定义一个二维数组用来存储已经在棋盘上的棋子(数组元素的值为1表示黑子,-1表示白子,0表示没有),这就必须我们在棋子的坐标和数组行和列的索引数之间做一个转换,这个比较好懂,有了这个数组那么这个问题就绝解决了。其中要注意的是,当用棋子和棋盘的边界宽度除以格子宽度余数大于1时,相应的行和咧的索引要加1。
下面的代码是棋子坐标和数组行和咧之间的转换
	 int r=(x-Config.X)/Config.Chess_Size;
if((x-Config.X)%Config.Chess_Size>Config.Chess_Size/2){
r++;
}

int c=(y-Config.Y)/Config.Chess_Size;
if((y-Config.Y)%Config.Chess_Size>Config.Chess_Size/2){
c++;
}



三,有了棋盘,也能下棋子了,那么剩下的步骤是判断输赢。我们就创建一个输赢类class Win{}.
我们知道在五子棋中判断输赢的方法共有四个情况。右斜、左斜、水平、竖直五子相连就赢了。所以要顶一个相应的四个方法right()、left()、h()、v();在判断时是用当前下的棋子和原来已经有的棋子判断是否有五子相连,所以这些方法中都带有两个整形参数表示当前要下的棋子位置,而原有的棋子就要用到我们第二部用来存储的已下棋子的数组了。
下面列举右斜的方法代码:
	//定义一个判断右斜输赢的方法
public boolean right(int r,int c){
int count=1;
//往下找相同棋子
for(int i=r+1,j=c+1;i<array.length&&j<array[i].length;i++,j++){
if(array[i][j]==array[r][c]){
count++;
}else{
break;
}
}
//往上找相同棋子
for(int i=r-1,j=c-1;i<=0&&j<=0;i--,j--){
if(array[i][j]==array[r][c]){
count++;
}else{
break;
}
}
return icount(count);
}
//定义一个判断count是否大于等于5的方法
private boolean icount(int count){
if(count>=5){
return true;
}else{
return false;
}
}

注意我们得让棋子当我们最小化界面时仍然存在,这就要实现棋子的重绘,我们有了存储棋子的数组,这个就很容易实现了,只要在五子棋界面类中定义一个画棋子的方法,利用数组用个两层循环就行了。然后让这个方法在方法paint(),中调用即可。


这样一个简单的人人对战五子棋就完成了。


其中的重点还有各个类的联系和参数传递问题。我用这样几句话概括一下:
在没有主函数的类中需要用对象大多都只需声明而不用具体实例化,大多都可以创建构造方法,利用参数从界面类中传值过来就行。(有些需要实例化但必须在正确的位置上实例化,比如在ChessListener类,构造方法中实例化Win对象,这个也只是要把界面类(拥有主函数的类)中的数组传给它而已)。代码如下:
public  ChessListener(Graphics g,int [][] array,ChessUi cu){
this.g=g;
this.array=array;
win=new Win(array);//利用ChessListener的构造方法,在ChessUi类中实例化画ChessListener对象时把数组传给win对象。。
this.cu=cu;
}

所以我觉得在一般类中要用到的对象声明即可,而实例化这些对象全部在拥有主函数的类中,然后用参数传递。但这也不是绝对的,具体还要看某一个类或者某一个对象要实现的功能。(这是我从仿xp画板和五子棋中体会到的一些经验)、
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值