第15章 图形编程

在Java的开发中,图形编程是一个比较重要的环节,一般的效果图及页面几乎都是通过Applet来实现,并且是一个单独的界面显示小程序,可以做成各种动画效果或是与网页有关的,如电子相册、QQ欢迎动画等。

实例137 多变的按钮

在本实例中,主要是实现多变的按钮,它是一个用Java Applet实现页面响应鼠标事件的例子。可以进行鼠标移入、移出、单击事件等操作并实现响应鼠标移入、移出、单击事件的功能,可以让图形界面更加美观。

技术要点

多变的按钮的技术要点如下:

• Applet的声音播放AudioClip类的使用。

• 鼠标事件的监听功能。

• MediaTracker类的使用。

实现步骤

(1)新建一个类名为Change_Button.java。

(2)代码如下所示:

package chp15;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
public class Change_Button extends Applet implements MouseListener {//页面响应鼠标事件
    private int width, height;                          //声明int类变量
    private Image image, img1, img2, img3;              //声明Image类型变量
    private Label la = new Label("多变的按钮");          //创建一个带初始值的Label对象
    private Graphics grap;                              //声明Graphics变量
    private MediaTracker media;                         //声明MediaTracker变量
    private AudioClip audioA, audioB;                   //声明AudioClip变量
    public void init() {                                //为Applet初始化
          audioA = getAudioClip(this.getCodeBase(), "lx.wav"); //创建audioA对象
          audioB = getAudioClip(getCodeBase(), "hh.wav");      //创建audioB对象
          width = getSize().width;                      //返回Applet的宽度
          height = getSize().height;                    //返回Applet的高度
          image = createImage(width, height);           //根据参数创建一个Image对象
        grap = image.getGraphics();                     //根据图像创建Graphics对象
        media = new MediaTracker(this);                 //MediaTracker对象实例化
        img1 = getImage(getCodeBase(), "b1.jpg");       //根据参数创建Image对象
        media.addImage(img1, 0);                        //将img1放入media对象中
        img2 = getImage(getCodeBase(), "b3.jpg");
        media.addImage(img2, 1);
        img3 = getImage(getCodeBase(), "b2.jpg");
        media.addImage(img3, 2);
        try {
              media.waitForAll();                        //等待media加载所有的图像
        } catch (InterruptedException e) {
        }
        la.setSize(100, 20);
        la.setForeground(Color.pink);                    //设置标签的前景颜色
        this.add(la, BorderLayout.NORTH);                //将标签组件加载到Applet中
        addMouseListener(this);                          //为Applet添加鼠标监听事件
    }
    public void start() {                                //开始Applet程序
        grap.drawImage(img1, 0, 0, width, height, this); //根据给定的参数绘制图像
        repaint();
    }
    public void mouseClicked(MouseEvent e) {             //鼠标单击事件
    }
    public void mousePressed(MouseEvent e) {             //鼠标按下事件
        grap.drawImage(img3, 0, 0, width, height, this);   //当鼠标被按下时所绘制的图像
        audioA.stop();                                   //audioA停止播放声音
        audioB.play();                                   //audioB开始播放声音
        la.setBackground(Color.black);                   //设置标签的背景颜色
        la.setForeground(Color.yellow);                  //设置标签的前景颜色
        la.setText("audioB is playing");                 //设置标签中要显示的内容
        this.add(la, BorderLayout.NORTH);                //添加标签组件
        repaint();                                       //重新绘制组件
    }
    public void mouseReleased(MouseEvent e) {            //鼠标释放事件
        grap.drawImage(img2, 0, 0, width, height, this);
        repaint();
        audioB.stop();
        audioA.play();
        la.setBackground(Color.yellow);
        la.setForeground(Color.black);
        la.setText("audioA is playing");
        this.add(la, BorderLayout.NORTH);
    }
    public void mouseEntered(MouseEvent e) {           //鼠标进入Applet所触发的事件
        grap.drawImage(img2, 0, 0, width, height, this);
        repaint();
    }
    public void mouseExited(MouseEvent e) {            //鼠标离开Applet所触发的事件
        grap.drawImage(img1, 0, 0, width, height, this);
        repaint();
    }
    public void paint(Graphics g) {
        g.drawImage(image, 0, 0, width, height, this);
    }
}

(3)运行结果的初始界面如图15-1所示。当鼠标进入界面区域中时,出现如图15-2所示界面。当长按鼠标时,出现如图15-3所示界面。当释放鼠标时,出现如图15-4所示界面。

图15-1 初始界面

图15-2鼠标进入界面

图15-3长按鼠标界面

图15-4释放鼠标界面

源程序解读

本实例采用了Java小应用程序Java Applet。

(1)创建一个带初始值的Label对象:

private Label la = new Label("多变的按钮");

(2)Applet的声音播放AudioClip类的使用:使用Applet播放声音时,需先定义AudioClip对象,getAudioClip方法能把声音赋予AudioClip对象,如果仅想播放一遍声音,应调用AudioClip类的play方法,如果想循环播放,应选用AudioClip类的loop方法。

(3)MediaTracker类的使用:Java专门提供了用于跟踪包括图像和声音等多媒体对象的ImageObserver类和MediaTracker类,将MediaTracker对象实例化。

media = new MediaTracker(this);

实例138 自制对话框

本实例介绍如何定义适合自己的组件,本实例自定义一个对话框,在对话框中输入文本,单击“确定”按钮后将输入的文本显示到主窗口中,当单击“保存”按钮时,会将主窗口中的内容保存到通过文件对话框选择的文件中。

技术要点

自制对话框的技术要点如下:

• 文本域JTextArea可以显示多行文本,可以使用append()方法向文本域中追加内容。

• 自定义对话框继承JDialog,通过JDialog的setVisible方法显示和隐藏自定义对话框。

• 通过JFileChooser创建一个文件选择器。

• 通过JFileChooser对象的showSaveDialog方法可以弹出一个保存的对话框。

实现步骤

(1)新建一个类名为DialogDemo.java。

(2)代码如下所示:

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileOutputStream;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
 * 使用对话框。功能介绍:界面包括一个文本域、一个添加内容按钮和一个保存按钮,单击添加内容按钮弹出一个对话框,
 * 在对话框中输入的字符串将在文本域中显示;单击保存按钮弹出一个保存对话框,将文本域保存在指定的文件中,若该文件不存在,则自动创建文件并保存
 */
public class DialogDemo extends JFrame implements ActionListener {
    private Simple_Dialog simple_dialog;          //声明Simple_Dialog类对象
    private JTextArea area;                       //声明JTextArea变量
    String lineSeparator;                         //文本域中行之间的分隔符
    public DialogDemo() {
          super("对话框示例");                     //调用父类的构造方法
          area = new JTextArea(5, 30);            //创建一个能显示5行30个字符的文本域
    area.setEditable(false);                      //文本域的状态为不可修改
          JMenuBar bar = new JMenuBar();          //创建一个空的菜单栏
          getContentPane().add("North", bar);     //将菜单栏添加到容器中
          getContentPane().add("Center", new JScrollPane(area));
                                                  //将带滚动条的文本添加到容器中
          JButton button = new JButton("添加内容");   //添加一个按钮,单击按钮弹出对话框
          button.setActionCommand("b1");          //设置激发操作事件的命令名称为b1
          button.addActionListener(this);         //添加单击监听事件
          JButton saveButton = new JButton("保存");      //同理
          saveButton.setActionCommand("save");    //同理
          saveButton.addActionListener(this);     //同理
          JPanel panel = new JPanel();            //创建一个JPanel对象
          panel.add(button);                      //将按钮添加到面板中
          panel.add(saveButton);
          getContentPane().add("South", panel);
          //获取文本域中行之间的分隔符。这里调用了系统的属性
          lineSeparator = System.getProperty("line.separator");
          this.pack();                            //调整窗体布局大小
    }
    public void actionPerformed(ActionEvent event) {
    //单击按钮时根据不同的命令名称进行不同的操作
        File file;                                              //声明File对象
        if (event.getActionCommand().equals("b1")) {            //如果单击添加按钮
              if (simple_dialog == null) {                      //弹出一个对话框
                    simple_dialog = new Simple_Dialog(this, "输入对话框");
              }
              simple_dialog.setVisible(true);                   //设置对话框可见
        }
        if (event.getActionCommand().equals("save")) {          //如果单击保存按钮
                JFileChooser fc = new JFileChooser();           //创建一个文件选择器
                fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                                                                //设置文件选择模式
              fc.setDialogType(JFileChooser.SAVE_DIALOG);       //设置对话框类型
              fc.showSaveDialog(this);            //弹出一个Save File文件选择器对话框
              try {
                    file = fc.getSelectedFile();                //返回选中的文件
                    if (!file.exists()) {                       //判断该文件是否存在
                            file.createNewFile();               //若不存在则创建
                  }
                  String s = area.getText();                    //返回文本区域中的内容
                  FileOutputStream fou = new FileOutputStream(file);
                                                          //创建一个文件输出流
                  byte[] by = s.getBytes();               //将字符串转换成字节数组
                  fou.write(by);                    //将字节数组中的内容写入指定的文件中
              } catch (Exception e) {
              }
        }
    }
    public void setText(String text) {
        area.append(text + lineSeparator);          //添加内容到文本域的后面,每次都新起一行
    }
    public static void main(String args[]) {
DialogDemo window = new DialogDemo();
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
/**
 * 自定义对话框。对话框包括一个Label、一个文本框和两个按钮。
 */
class Simple_Dialog extends JDialog implements ActionListener {
    JTextField text;                       //文本框,用于输入字符串
    DialogDemo p;                          //对话框的父窗体
    JButton comfig;                        //"确定"按钮
/** 构造函数,参数为父窗体和对话框的标题 */
Simple_Dialog(JFrame prent_Frame, String title) {
//调用父类的构造函数,第三个参数用false表示允许激活其他窗体,为true表示不能够激活其他窗体
super(prent_Frame, title, false);
p = (DialogDemo) prent_Frame;
//添加Label和输入文本框
JPanel pl = new JPanel();
JLabel label = new JLabel("请输入要添加的文本:");
pl.add(label);
text = new JTextField(30);
text.addActionListener(this);
pl.add(text);
getContentPane().add("Center", pl);
//添加确定和取消按钮
JPanel pl1 = new JPanel();
pl1.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton cancelButton = new JButton("取 消");
cancelButton.addActionListener(this);
comfig = new JButton("确 定");
comfig.addActionListener(this);
pl1.add(comfig);
pl1.add(cancelButton);
getContentPane().add("South", pl1);
pack();          //调整对话框布局大小
    }
/** 事件处理 */
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if ((source == comfig)) {
//如果确定按钮被按下,则将文本框的文本添加到父窗体的文本域中
              p.setText(text.getText());
          }
          text.selectAll();
          setVisible(false);   //隐藏对话框
    }
}

(3)运行结果:单击“添加内容”按钮,如图15-5所示。

(4)当单击“确定”按钮之后,就出现一个新的页面,如图15-6所示。

图15-5 添加按钮操作图

图15-6 添加内容之后的页面

(5)单击“保存”按钮,如图15-7所示。

(6)打开保存的文件,可以看到一个文件,如图15-8所示。

图15-7 保存按钮操作图

图15-8 文件的页面

源程序解读

1. Simple_Dialog类

(1)自定义组件必须继承一个标准的组件,在标准组件上进行功能改进比自己完全写一个组件容易得多。Simple_Dialog继承JDialog,使得主窗口可以调用Simple_Dialog的setVisible方法控制何时显示、隐藏对话框。

(2)自定义组件的构造方法必须先调用父类的构造方法。super(prent-Frame, title, false);语句初始化了JDialog对象,其中第三个参数为false表示该对话框不是总在最前,即当对话框被显示时,可以激活程序中的其他窗口;当第三个参数为true时,表示该对话框始终在最前,不能够激活其他窗口。

(3)ActionEvent的getSource方法可以获得事件发生的源对象。如果是“确定”按钮被单击,则调用主窗口的setText方法,传入对话框中文本框的文本,在父窗口的setText方法中通过JTextArea的append方法将传入的文本追加显示在文本域中。

2. DialogDemo类

(1)DialogDemo类继承JFrame实现ActionListener,在自身的构造方法中,可以调用父类的构造方法“super("对话框示例");”语句初始化JFrame对象。

(2)在actionPerformed方法中,ActionEvent的getActionCommand()可以得到标识事件命令的字符串标志。如果是“保存”按钮被单击,则创建文件选择器,根据选择的路径创建文件对象,然后通过FileOutputStream写入文件对象中。

实例139 模仿QQ空间的电子相册

QQ空间的电子相册大家都不陌生。本实例就是通过使用Java Applet实现电子相册效果。首先建立一个下拉框,改变下拉框的内容,页面上就会显示不同的图片。

技术要点

模仿QQ空间的电子相册的技术要点如下:

• 各种事件的使用,如ActionEvent、MouseAdapter等。

• 类java.awt.Component中方法boolean action(Event evt,Object what)的使用。

实现步骤

(1)新建一个类名为Picture.java。

(2)代码如下所示:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.*;
public class Picture extends JApplet implements ActionListener {
    private JLabel[] lab = new JLabel[6];      //定义一个JLabel数组,其长度为6
    private ImageIcon imic;                    //声明ImageIcon变量
    private ImageIcon[] icn = new ImageIcon[6];//创建一个ImageIcon数组,其长度为6
    private JPanel pl, pl1;                   //定义JPanel变量
    ArrayList list;                           //声明ArrayList变量
    private JButton btn[] = new JButton[3];   //定义一个JButton数组,其长度为3
    private Image image;                      //声明Image变量
    private int key = 0;                      //定义int类型变量
    private Graphics grapcs;                  //定义Graphics变量
    public void init() {                      //Applet初始化
        F_init();                             //调用自定义F_init()方法
        this.setSize(450, 450);               //设置Applet的大小
        this.setVisible(true);                //设置组件可见性
        image = createImage(getSize().width - 105, getSize().height - 140);
                                              //确定画图像的位置
        grapcs = image.getGraphics();         //返回一个Graphics对象
        image = icn[0].getImage();            //返回此图标的Image
        grapcs.drawImage(image, 100, 100, this);      //确定绘制image的起点
        repaint();                            //重新调用paint方法
    }
    public void actionPerformed(ActionEvent e) {      //处理单击的监听事件
        if (e.getActionCommand().equals("b1")) {      //如果单击"上一张"按钮
              int k = getKey();               //得到一个k值
              if (k != 0) {                   //如果k不等于0
                    imic = (ImageIcon) list.get(k - 1);
                                           //通过k值到ArrayList中取出相应的ImageIcon
                    setKey(k - 1);            //将k-1重新赋给k
                    image = imic.getImage();          //返回此图标的Image
                    repaint();                        //重新调用paint方法
              }
        }
        if (e.getActionCommand().equals("b2")) {      //如果单击"下一张"按钮
        //其余的代码可以参考上面的
              int k = getKey();
              if (k != 5) {
                    imic = (ImageIcon) list.get(k + 1);
                    setKey(k + 1);
                    image = imic.getImage();
                    repaint();
              }
        }
        if (e.getActionCommand().equals("b3")) {      //如果单击"返回"按钮
                imic = (ImageIcon) list.get(0);
                setKey(0);
                image = imic.getImage();
                repaint();
        }
    }
    public void paint(Graphics g) {                   //绘制图像
        F_init();
        g.drawImage(image, 120, 120, this);
    }
    public void F_init() {                         //主要是对各组件进行布局和初始化的作用
        pl = new JPanel(new GridLayout(1, 6));            //创建一个网格布局的面板pl
        pl1 = new JPanel();                               //创建面板pl1
        list = new ArrayList();                           //创建ArrayList对象
        for (int i = 0; i < lab.length; i++) {
              icn[i] = new ImageIcon("D:\\workspace\\Yin\\TP" + (i + 1) + ".jpg");
                                                          //创建ImageIcon对象
              lab[i] = new JLabel(icn[i]);                //创建带图标的标签
              pl.add(lab[i], i);                          //将标签组件添加到面板中
              list.add(i, icn[i]);                        //将图标对象添加到ArrayList中
        }
        //对每个标签进行鼠标单击事件监听
        lab[0].addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                  if (e.getButton() == 1) {               //如果鼠标左键被按下
                        imic = (ImageIcon) list.get(0);   //取出ArrayList中0位置上的图标
                        image = imic.getImage();          //返回此图标的图像
                        repaint();
                        setKey(0);
                  }
            }
        });
        lab[1].addMouseListener(new MouseAdapter() { //同上
                      public void mouseClicked(MouseEvent e) {
                            if (e.getButton() == 1) {
                                  imic = (ImageIcon) list.get(1);
                                  image = imic.getImage();
                                  repaint();
                                  setKey(1);
                            }
                      }
                  });
        lab[2].addMouseListener(new MouseAdapter() {
                      public void mouseClicked(MouseEvent e) {
                            if (e.getButton() == 1) {
                                  imic = (ImageIcon) list.get(2);
                                  image = imic.getImage();
                                  repaint();
                                  setKey(2);
                            }
                      }
                  });
        lab[3].addMouseListener(new MouseAdapter() {
                      public void mouseClicked(MouseEvent e) {
                            if (e.getButton() == 1) {
                                  imic = (ImageIcon) list.get(3);
                                  image = imic.getImage();
                                  repaint();
                                  setKey(3);
                            }
                      }
                  });
        lab[4].addMouseListener(new MouseAdapter() {
                      public void mouseClicked(MouseEvent e) {
                            if (e.getButton() == 1) {
                                  imic = (ImageIcon) list.get(4);
                                  image = imic.getImage();
                                  repaint();
                                  setKey(4);
                            }
                      }
                  });
        lab[5].addMouseListener(new MouseAdapter() {
                      public void mouseClicked(MouseEvent e) {
                            if (e.getButton() == 1) {
                                  imic = (ImageIcon) list.get(5);
                                  image = imic.getImage();
                                  repaint();
                                  setKey(5);
                            }
                      }
                  });
        //定义按钮
        btn[0] = new JButton("前一张");
        btn[0].setActionCommand("b1");
        btn[0].addActionListener(this);
        btn[1] = new JButton("下一张");
        btn[1].setActionCommand("b2");
        btn[1].addActionListener(this);
        btn[2] = new JButton("返回");
        btn[2].setActionCommand("b3");
        btn[2].addActionListener(this);
        pl1.add(btn[0], 0);
        pl1.add(btn[1], 1);
        pl1.add(btn[2], 2);
        this.add(pl, BorderLayout.NORTH);
        this.add(pl1, BorderLayout.SOUTH);
    }
    public int getKey() {
        return key;
    }
    public void setKey(int key) {
        this.key = key;
    }
}

(3)运行结果的初始页面,如图15-9所示。

(4)单击“下一张”按钮就会跳到下一个图片中,如图15-10所示。

图15-9 电子相册

图15-10 单击“下一张”按钮出现的页面

源程序解读

(1)绘制程序中的图像:

public void paint(Graphics g) {
          F_init();
          g.drawImage(image, 120, 120, this);
    }

(2)方法action()的使用:在Java Applet小程序中,可以通过action()方法来获得Java Applet小程序运行时所发生的事件,如单击按钮、键入文本、使用鼠标或执行任何界面相关的动作等。

(3)鼠标事件:程序中通过MouseAdapter将图片关联起来,单击鼠标时就会触动相应的鼠标事件,并执行操作。

实例140 会动的七彩文字

会动的七彩文字在网页上经常可以看到,现在采用Java Applet这个效果。可以想到文字的颜色是不停改变的,并产生波浪效果,这样就可以使页面中的文字更加美观,并且可以调整读者的积极性,让读者在开发中有一个轻松的心情。

技术要点

会动的七彩文字的技术要点如下:

• 类Font的使用。

• 类FontMetrics的使用。

• 文字的颜色及速度控制。

实现步骤

(1)新建一个类名为QC_Text.java。

(2)代码如下所示:

import java.awt.*;
import java.applet.*;
public class QC_Text extends Applet implements Runnable {    //有线程运行接口
String str = null;
int d = 1;
int h = 18;
int v = 18;
Thread thread = null;                                     //设置一个线程
char[] ch;
int p = 0;
Image image;
Graphics gphics;
Color[] color;
    private Font f;                                       //字体
    private FontMetrics fm;                               //字模
    public void init() {
          str = "有志者事竟成";                            //设置七彩文字内容
          this.setSize(500, 200);                         //设置Applet的大小
          setBackground(Color.black);                     //设置背景颜色
          ch = new char[str.length()];
          ch = str.toCharArray();                    //将字符串中的各个字符保存到数组中
          image = createImage(getSize().width, getSize().height);
          gphics = image.getGraphics();
          f = new Font("", Font.BOLD, 30);
          fm = getFontMetrics(f);                         //获得指定字体的字体规格
          gphics.setFont(f);                              //设置组件的字体
          float hue;
          color = new Color[str.length()];                //颜色的色元
          for (int i = 0; i < str.length(); i++) {
                hue = ((float) i) / ((float) str.length());
                color[i] = new Color(Color.HSBtoRGB(hue, 0.8f, 1.0f)); //颜色分配
          }
    }
    public void start() {                                 //线程开始的类
          if (thread == null) {                           //如果线程为空
                thread = new Thread(this);
                                                          //开始新的线程
                thread.start();                           //开始
          }
    }
                                                          //终止线程
    public void stop() {
          if (thread != null) {                           //如果线程不为空
                thread.stop();                            //终止线程,使它为空
                thread = null;
          }
    }
                                                          //运行线程
    public void run() {
          while (thread != null) {
              try {
                  thread.sleep(200);                      //让线程沉睡200毫秒
              } catch (InterruptedException e) {
              }
              repaint();                                  //重新绘制界面
        }
    }
    public void update(Graphics g) {                      //重写update方法,解决闪烁问题
        int x, y;
        double a;
        gphics.setColor(Color.black);
        gphics.fillRect(0, 0, getSize().width, getSize().height);
        p += d;
        p %= 7;                              //主要控制字的速度,被除数越小,速度越快
        //System.out.println(p+" p1");
        for (int i = 0; i < str.length(); i++) {
                a = ((p - i * d) % 7) / 4.0 * Math.PI; //主要控制弧度,被除数越小,弧度越大
                x = 30 + fm.getMaxAdvance() * i + (int) (Math.cos(a) * h);
                                                        //求x坐标值
                y = 80 + (int) (Math.sin(a) * v);       //求y坐标值
                gphics.setColor(color[(p + i) % str.length()]);
                gphics.drawChars(ch, i, 1, x, y);
        }
        paint(g);
    }
    public void paint(Graphics g) {
        g.drawImage(image, 0, 0, this);
    }
}

(3)运行结果的初始页面如图15-11所示。

(4)程序运行的下一个页面如图15-12所示。

图15-11 会动的七彩文字

图15-12 文字进入下一秒时的轨迹

源程序解读

(1)字体类(Font类)代表字体。通过类Graphics或组件的方法getFont()或setFont()可以获取或设置当前使用的字体(Font类的对象)。在上面的实例中,可以看到Font类的使用。

f = new Font("", Font.BOLD, 30);
fm = getFontMetrics(f);

(2)类Graphics2D也继承了类Graphics的两个方法getFont()和setFont()。

(3)字模(FontMetrics类):FontMetrics类表示字模,这个类提供了一系列方法,通过调用它们,可以得到用某种字体表示的一个字符串在屏幕上的特定尺寸。

fm = getFontMetrics(f);
gphics.setFont(f);

实例141 模仿3D渐层效果

3D渐层效果可以使读者更直观地看到文字或图片的立体效果,本实例主要采用Java Applet实现文字的3D效果,并随着时间的改变,不停地改变字体的颜色。

技术要点

模仿3D渐层效果的技术要点如下:

• 随机产生RGB颜色值。

• 使用for循环实现3D文字渐层显示。

• 设置绘图的字体。

实现步骤

(1)新建一个类名为TextOf3D.java。

(2)代码如下所示:

import java.awt.*;
import javax.swing.JApplet;
public class TextOf3D extends JApplet implements Runnable {
    private Image image;                                    //声明image变量
    private Image image_1;                                  //声明image_1变量
    private Graphics gp;                                    //声明绘图对象
    private Thread thread = null;                           //声明线程
    private MediaTracker tracker;                           //声明媒体跟踪器
    private int height, width;                              //声明int变量
    private String text;                                    //声明String变量text
    private Font font;                                      //声明Font对象
    public void init() {                                    //Applet初始化
          this.setSize(200, 100);                           //设置初始大小
          width = this.getWidth();                          //得到容器的宽
          height = this.getHeight();                        //获取容器的高
          this.setBackground(Color.lightGray);              //设置容器的背景色
          image = createImage(width, height);               //根据当前的宽和高创建一个图像
          text = "welcome";                                 //String变量text赋值
          String str = "中华儿女";                           //创建一个有初始值的String变量
          if (str != null)
              text = str;                    //在str的值不为空的前提下,将str的值赋给text
          font = new Font("仿宋_GB2312", Font.BOLD, 30);        //创建一个Font对象
          tracker = new MediaTracker(this);                 //创建一个MediaTracker对象
          tracker.addImage(image, 0);                       //将image加载到tracker中
          try {
                tracker.waitForID(0);                       //等待加载Id为0的图像
          } catch (InterruptedException e) {
          }
        image_1 = createImage(width, height);               //根据当前的宽和高创建一个图像
        gp = image_1.getGraphics();             //根据图像image_1,获得Graphics对象
    }
    public void start() {                                   //启动线程
        if (thread == null) {
              thread = new Thread(this);
              thread.start();
        }
    }
    public void run() {                                     //运行线程
        int x = 20;                                         //设置绘制图像的X坐标
        int y = height / 2;                                 //设置绘制图像的Y坐标
        int R, G, B;                                        //设置RGB颜色值
        gp.setFont(font);                                   //设置绘图的字体
        while (thread != null) {                            //在启动线程的前提下
                                                            //随机生成Color颜色值
            R = (int) (255 * Math.random());
            G = (int) (255 * Math.random());
            B = (int) (255 * Math.random());
            try {
                  thread.sleep(2000);                       //线程休眠2000毫秒
            } catch (InterruptedException ex) {
            }
            gp.setColor(Color.black);                       //设置绘图的背景色为黑色
            gp.fillRect(0, 0, width, height);               //设置所绘矩形的大小和位置
            repaint();                                      //重新绘制此组件
            for (int i = 0; i < 10; i++) {
                  gp.setColor(new Color((255 - (0 + R) * i / 10), (255 - (0 + G)
                              * i / 10), (255 - (0 + B) * i / 10)));
                                                                //根据RGB设置当前的绘图颜色
                  gp.drawString(text, x - i, y - i);
                                    //根据当前字体和颜色绘制由指定string给定的文本
                  repaint();                                //重新绘制此组件
                  try {
                        thread.sleep(60);                   //线程休眠60毫秒
                  } catch (InterruptedException e) {
                  }
            }
        }
    }
    public void paint(Graphics g) {                                   //绘图方法
        g.drawImage(image_1, 0, 0, this);
    }
}

(3)运行结果如图15-13所示,程序运行2秒后的页面如图15-14所示。程序再运行2秒后的页面如图15-15所示。

图15-13 模仿3D渐层效果

图15-14 2秒后的文字

图15-15 再过2秒后的页面

源程序解读

(1)3D文字效果的实现,使文字颜色伴随时间改变。利用随机数定义颜色:public void init(),利用这个构造方法来初始化文字,得到容器的宽和高,同时也为下面的程序做了声明。

(2)根据当前的值获得设置绘图颜色:

gp.setColor(newColor((255 - (0 + R) * i / 10),(255 - (0 + G)* i / 10),(255 - (0 + B) * i / 10)));

(3)运用线程来启动小程序,实现程序的效果,展现3D文字。

实例142 模仿QQ空间的欢迎动画

对于QQ空间相信每个人都不会感到陌生。在一个完美的空间,用户都喜欢有一个欢迎画面,欢迎画面既起到了宣传产品的作用,同时也标志了应用程序是否成功启动。本实例将展示一个欢迎画面,可以指定欢迎画面用的图片和显示的时间,当用户单击画面时,画面关闭;当显示时间到期时,画面自动关闭。

技术要点

制作欢迎画面的技术要点如下:

• 由于JWindow是一种没有标题、没有边框的窗口,因此它是实现欢迎画面的最佳组件。

• 掌握通过图片文件的目录路径可以构造ImageIcon对象,利用ImageIcon对象可以构造标签JLabel。此时调用JLabel的getPreferredSize方法将得到标签的最佳大小,也就是图片载入标签中的最佳尺寸。

• 掌握Toolkit类的构造方式和getScreenSize方法的使用。

• 掌握使用MouseAdapter事件处理器为组件添加鼠标事件。其中,它的mousePressed方法是在鼠标按键被按下时调用。

• 掌握SwingUtilities的invokeAndWait或者invokeLater方法。

实现步骤

(1)新建一个类名为Desktop_Window.java。

(2)代码如下所示:

package chp15;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
/**
 * 本例实现一个欢迎画面,常用作应用软件的启动画面。
 */
public class Desktop_Window extends JWindow implements Runnable {
Thread waitThread;
final int rest;
final Thread closerThread;
/**构造函数image_url:欢迎画面所用的图片,frame:欢迎画面所属的窗体,waitTime:欢迎画面显示的事件*/
    public Desktop_Window(String image_url, JFrame frame, int waitTime) {
          super(frame);
    //建立一个标签,标签中显示图片。将标签放在欢迎画面中间
          JLabel label = new JLabel(new ImageIcon(image_url));
          getContentPane().add(label, BorderLayout.CENTER);
          pack();
          //获取屏幕的分辨率大小
          Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
          Dimension labelSize = label.getPreferredSize();      //获取标签大小
          //将欢迎画面放在屏幕中间
          setLocation(screenSize.width / 2 - (labelSize.width / 2),
                    screenSize.height / 2 - (labelSize.height / 2));
          //增加一个鼠标事件处理器,如果用户用鼠标单击了欢迎画面,则关闭
          addMouseListener(new MouseAdapter() {
              public void mousePressed(MouseEvent e) {
                    setVisible(false);
                    dispose();
              }
          });
          rest = waitTime;
          /**Swing线程在同一时刻仅能被一个线程所访问。
          *一般来说,这个线程是事件派发线程(event-dispatching thread)。
          * 如果需要从事件处理(event-handling)或绘制代码以外的地方访问UI,
          * 则可以使用SwingUtilities类的invokeLater()或invokeAndWait()方法。
          */
          closerThread = new Thread() {            //关闭欢迎画面的线程
              public void run() {
                    setVisible(false);
                    dispose();                     //释放资源
              }
          };
          waitThread = new Thread();               //等待关闭欢迎画面的线程
          setVisible(true);
          waitThread.start();                      //启动等待关闭欢迎画面的线程
    }
    public static void main(String[] args) throws FileNotFoundException {
JFrame frame = new JFrame("自制欢迎动画");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
Desktop_Window dw = new Desktop_Window("D:/abc/wel.jpg", frame, 5000);
frame.pack();
frame.setVisible(true);
    }
    public void run() {
        try {
              Thread.sleep(rest);                 //当显示了rest后,尝试关闭欢迎画面
              SwingUtilities.invokeAndWait(closerThread);
        } catch (Exception e) {
              e.printStackTrace();
        }
    }
}

(3)运行结果如图15-16所示。

图15-16 欢迎动画

源程序解读

(1)通过ImageIcon把图片装入到缓存中,再用ImageIcon对象构造标签。通过JLabel的getPreferredSize()方法获得标签的最佳大小,也就是图片的最佳大小。

(2)获取屏幕的分辨率大小:

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

(3)为JWindow注册鼠标事件处理器,在mousePressed方法中关闭窗口,即当用户单击欢迎画面时,画面消失。setVisible 方法传入false表示隐藏欢迎画面,dispose方法表示销毁窗口资源。

(4)closerRunner对象是一个线程,它完成关闭欢迎画面的功能。

(5)关闭欢迎画面:

public void run() {
try {
Thread.sleep(rest);
SwingUtilities.invokeAndWait(closerThread);

(6)Swing线程在同一时刻仅能被一个线程所访问,而这个线程就是事件派发线程,它是执行绘制和事件处理的线程。例如,paint和actionPerformed方法会自动在事件派发线程中执行。

实例143 百叶窗效果

百叶窗效果是一种渐变的效果,让图片逐渐地映入眼帘,本实例是利用Java Applet动态改变显示的图片,并产生百叶窗的效果。希望通过这个实例能带给读者一点启发。百叶窗效果可以是多种,读者可以根据自己的需要,举一反三。

技术要点

百叶窗效果的技术要点如下:

• 类PixelGrabber的使用。

• 使用类MemoryImageSource创建图像。

• MediaTracker对象的使用。

实现步骤

(1)新建一个类名为Blinds.java。

(2)代码如下所示:

package chp15;
import java.awt.*;
import java.applet.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.awt.image.*;
public class Blinds extends Applet implements Runnable {
    private Image IMG[], image;                           //声明Image数组和变量
    private MediaTracker tracker;                         //声明MediaTracker变量
    private int width, height, image_count = 8, image2, image3; //声明int类型的变量
    private Thread thread;                                //声明Thread变量
    private int delay = 3000;                             //定义int变量,初始值为3000
    private int p, p_1[], p_2[], p_3[], p_4[], p_5[], p_6[], p_7[], p_8[],
                    p_A[], p_B[];                     //声明int型数组,用于接收生成图像的像素
    public void init() {
          this.setBackground(Color.black);                //设置背景颜色为黑色
          this.setSize(320, 250);                         //设置边框的宽和高
          IMG = new Image[image_count];      //创建数组长度为image_count的Image数组
          tracker = new MediaTracker(this);               //创建MediaTracker对象
          String s = "";
          for (int i = 0; i < image_count; i++) {
                s = "D://abc//" + (i + 1) + ".jpg";
                URL url;
                try {
                      url = new URL("file:" + s);         //创建URL对象
                      IMG[i] = getImage(url);             //创建Image对象
                      tracker.addImage(IMG[i], 0); //将Image对象添加到tracker的指定位置中
              } catch (MalformedURLException e) {
                      e.printStackTrace();
              }
          }
          try {
                tracker.waitForID(0);                     //等待加载ID为0的所有图像
          } catch (InterruptedException e) {
          }
          width = IMG[0].getWidth(this);                  //得到此图片的宽
          height = IMG[0].getHeight(this);                //得到此图片的高
          p = width * height;                             //宽和高的乘积
          p_1 = new int[p];                               //创建长度为p的int数组
        PixelGrabber PG1 = new PixelGrabber(IMG[0], 0, 0, width, height, p_1,0, width);
//创建一个PixelGrabber对象,以便从指定的图像中将像素矩形部分 (x, y, w,h) 抓取到给定的数组中
        try {
              PG1.grabPixels();
//请求Image开始传递像素,并等待传递完相关矩形中的所有像素,或者等待到超时期已过
        } catch (InterruptedException ex) {
        }
        p_2 = new int[p];
        PixelGrabber PG2 = new PixelGrabber(IMG[1], 0, 0, width, height, p_2,
                  0, width);
        try {
              PG2.grabPixels();
        } catch (InterruptedException ex) {
        }
        p_3 = new int[p];
        PixelGrabber PG3 = new PixelGrabber(IMG[2], 0, 0, width, height, p_3,
                  0, width);
        try {
              PG3.grabPixels();
        } catch (InterruptedException ex) {
        }
        p_4 = new int[p];
        PixelGrabber PG4 = new PixelGrabber(IMG[3], 0, 0, width, height, p_4,
                  0, width);
        try {
              PG4.grabPixels();
        } catch (InterruptedException ex) {
        }
        p_5 = new int[p];
        PixelGrabber PG5 = new PixelGrabber(IMG[4], 0, 0, width, height, p_5,
                  0, width);
        try {
              PG5.grabPixels();
        } catch (Exception ex) {
        }
        p_6 = new int[p];
        PixelGrabber PG6 = new PixelGrabber(IMG[5], 0, 0, width, height, p_6,
                  0, width);
        try {
              PG6.grabPixels();
        } catch (InterruptedException ex) {
        }
        p_7 = new int[p];
        PixelGrabber PG7 = new PixelGrabber(IMG[6], 0, 0, width, height, p_7,
                  0, width);
        try {
              PG7.grabPixels();
        } catch (InterruptedException ex) {
        }
        p_8 = new int[p];
        PixelGrabber PG8 = new PixelGrabber(IMG[7], 0, 0, width, height, p_8,
                  0, width);
        try {
                PG8.grabPixels();
        } catch (Exception ex) {
        }
        image2 = 0;
        p_A = new int[p];
        p_B = new int[p];
        image = IMG[0];
        thread = new Thread(this);               //创建线程
        thread.start();                          //启动线程
    }
    public void paint(Graphics g)                //绘制组件
    {
        g.drawImage(image, 0, 0, this);
    }
    public void update(Graphics g)               //更新组件
    {
        paint(g);
    }
    public void run()                            //运行线程
    {
        if (thread == null) {
thread = new Thread(this);
thread.start();
        }
        while (true)                             //控制图片和图片之间的过渡
        {
              try {
                  thread.sleep(delay);                  //线程休眠delay毫秒
                  image3 = ((image2 + 1) % image_count);//指向当前图片的下一张图片
                  if (image2 == 0) {
                        System.arraycopy(p_1, 0, p_A, 0, p);
    //从下标为0的p_1数组中复制一个数组,存放到p_A数组中下标为0的位置中,其数组长度为p
                      System.arraycopy(p_2, 0, p_B, 0, p);     //同上
                      image = createImage(new MemoryImageSource(width, height,
                                p_A, 0, width)); //根据指定的图像生成器创建一幅图像
                      repaint();
                  }
                  if (image2 == 1) {
                        System.arraycopy(p_2, 0, p_A, 0, p);
                        System.arraycopy(p_3, 0, p_B, 0, p);
                        image = createImage(new MemoryImageSource(width, height,
                                  p_A, 0, width));
                        repaint();
                  }
                  if (image2 == 2) {
                        System.arraycopy(p_3, 0, p_A, 0, p);
                        System.arraycopy(p_4, 0, p_B, 0, p);
                        image = createImage(new MemoryImageSource(width, height,
                                  p_A, 0, width));
                    repaint();
                }
                if (image2 == 3) {
                    System.arraycopy(p_4, 0, p_A, 0, p);
                    System.arraycopy(p_5, 0, p_B, 0, p);
                    image = createImage(new MemoryImageSource(width, height,
                              p_A, 0, width));
                    repaint();
                }
                if (image2 == 4) {
                    System.arraycopy(p_5, 0, p_A, 0, p);
                    System.arraycopy(p_6, 0, p_B, 0, p);
                    image = createImage(new MemoryImageSource(width, height,
                              p_A, 0, width));
                    repaint();
                }
                if (image2 == 5) {
                    System.arraycopy(p_6, 0, p_A, 0, p);
                    System.arraycopy(p_7, 0, p_B, 0, p);
                    image = createImage(new MemoryImageSource(width, height,
                              p_A, 0, width));
                    repaint();
                }
                if (image2 == 6) {
                    System.arraycopy(p_7, 0, p_A, 0, p);
                    System.arraycopy(p_8, 0, p_B, 0, p);
                    image = createImage(new MemoryImageSource(width, height,
                              p_A, 0, width));
                    repaint();
                }
                if (image2 == 7) {
                    System.arraycopy(p_8, 0, p_A, 0, p);
                    System.arraycopy(p_1, 0, p_B, 0, p);
                    image = createImage(new MemoryImageSource(width, height,
                              p_A, 0, width));
                    repaint();
                }
                while (true)                                     //控制叶宽
                {
                    for (int i = 0; i < (int) (height / 10); i++) {
                        try {
                              thread.sleep(30);                  //线程休眠30毫秒
                              for (int j = 0; j < height; j += (int) (height / 10)) {
                                      for (int k = 0; k < width; k++) {
                                              p_A[width * (j + i) + k] = p_B[width
                                                        * (j + i) + k];//进行数组间的复制
                                  }
                              }
                        } catch (InterruptedException e) {
                        }
                        image = createImage(new MemoryImageSource(width,
                                      height, p_A, 0, width));
                            repaint();
                        }
                        break;
                  }
                  image2 = image3;
                  repaint();
              } catch (InterruptedException e) {
              }
        }
    }
}

(3)运行结果的初始页面如图15-17所示。

(4)运行之后的页面如图15-18所示。

图15-17 百叶窗

图15-18 百叶窗运行后的页面

源程序解读

(1)类PixelGrabber的使用:获得了图像后,可以通过java.awt.image.PixelGrabber包中的PixelGrabber方法将图像中的像素信息完全读取出来。用法如下:

PixelGrabber PG1 = new PixelGrabber(IMG[0], 0, 0, width, height, p_1,0, width);

(2)MediaTracker对象的使用:在语句tracker = new MediaTracker(this);之后就将Image对象添加到MediaTracker中,并对其操作。

(3)通过启动线程来将图片进行获取及制作,最终运行出百叶窗效果。

实例144 闪电效果

本实例是一个Java小应用程序(Applet)的例子。使用线程技术,最终运行小程序就可以看到闪电效果,并且可以是多种颜色的,随时变化。

技术要点

闪电效果的技术要点如下:

• 类Graphics的使用。

• 类Image的使用。

实现步骤

(1)新建一个类名为LightnING.java。

(2)代码如下所示:

package chp15;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
public class LightnING extends Applet implements Runnable {
                                        //在applet中支持线程,需要实现Runnable接口
    private Thread thread = null;                  //applet支持的线程
    private int no_Light = 0;                      //没有闪电的标志变量:0表示没有闪电
    private int Light = 1;                         //有闪电的标志变量
    private int[] light;                           //声明int型数组light
    private int[] array_1;                         //声明int型数组array_1
    private int[] array_2;                         //声明int型数组array_2
    private Image T_image, image;                  //声明Image变量T_image, image
    private int delay = 3;                         //延长的时间倍数
    public void init() {                           //初始化applet
          this.setSize(640, 480);
          String imageName = "city.gif";
          image = getImage(getCodeBase(), imageName);
          //创建int型数组,定义其长度为getSize().height
          light = new int[getSize().height];
          array_1 = new int[getSize().height];
          array_2 = new int[getSize().height];
          T_image = this.createImage(getSize().width, getSize().height);
                                                   //根据指定的参数创建一幅图像
    }
    public void paint(Graphics g) {
          int i, t;
          if (no_Light == 0)                       //没有闪电
          {
              g.setColor(Color.black);             //设背景色为黑色
              g.fillRect(0, 0, getSize().width, getSize().height);   //填充背景色
              g.drawImage(image, 0, 0, this);      //输出city.gif
          } else                                   //有闪电
          {
              switch (Light) {
              case 1:
                    g.setColor(new Color(240, 255, 255));
                    break;                         //设背景色为蓝色
              case 2:
                    g.setColor(new Color(176, 48, 96));
                    break;                         //设背景色为红色
              case 3:
                    g.setColor(new Color(250, 255, 240));
                    break;                         //设背景色为黄色
              case 4:
                    g.setColor(new Color(240, 255, 240));
                    break;                         //设背景色为绿色
              case 5:
                  g.setColor(new Color(248, 248, 255));
                  break;                           //设背景色为紫色
              default:
                  g.setColor(new Color(245, 245, 245));
                  break;                           //设背景色为白色
              }
              g.fillRect(0, 0, getSize().width, getSize().height); //填充背景色
              t = (int) (0.6F * getSize().height); //输出闪电图像
              for (i = 1; i < getSize().height; i++) {
                    if (i < t) {                   //输出闪电周围的灰色矩形
                          g.setColor(Color.white);
                          g.drawRect(light[i] - 4, i, 3, 1);
                          g.drawRect(light[i] + 1, i, 3, 1);
                          g.setColor(Color.orange);
                          g.drawRect(light[i] - 1, i, 1, 1);
                          g.drawRect(light[i] + 1, i, 1, 1);
                  }
                  switch (Light) {
                  case 1:
                      g.setColor(new Color(0, 0, 255));
                      break;                       //蓝色闪电
                  case 2:
                      g.setColor(new Color(255, 0, 0));
                      break;                       //红色闪电
                  case 3:
                      g.setColor(new Color(255, 215, 0));
                      break;                       //黄色闪电
                  case 4:
                      g.setColor(new Color(0, 255, 0));
                      break;                       //绿色闪电
                  case 5:
                      g.setColor(new Color(160, 32, 240));
                      break;                       //紫色闪电
                  default:
                      g.setColor(new Color(225, 225, 225));
                      break;                       //白色闪电
                  }
                  g.drawLine(light[i], i, light[i - 1], i - 1);
                  if (array_1[i] >= 0) {           //输出闪电折线
                        g.drawLine(array_1[i], i, array_1[i - 1], i - 1);
                  }
                  if (array_2[i] >= 0) {           //输出闪电折线
                        g.drawLine(array_2[i], i, array_2[i - 1], i - 1);
                  }
              }
              g.drawImage(image, 0, 0, this);      //输出城市图像city.gif
              Light = (int) ((Math.random() * 10) + 1);
        }
    }
    void drawT_image() {                           //调用paint()方法
        Graphics g;
        g = T_image.getGraphics();
        paint(g);
    }
    public void start() {                          //启动applet,创建并启动线程
        if (thread == null) {
              thread = new Thread(this);
              thread.start();
        }
    }
    public void stop() {                           //停止运行线程
        if (thread != null) {
              thread.stop();
              thread = null;
        }
    }
    void createLight() {                           //生成闪电的坐标数组数据
        int i;
        int bs1, bs2;                              //开始位置的坐标
        int be1, be2;                              //结束位置的坐标
        light[0] = (int) (Math.random() * getSize().width); //随机产生闪电出现的位置
        array_1[0] = light[0];
        array_2[0] = light[0];
        bs1 = (int) (Math.random() * getSize().height) + 1;
        bs2 = (int) (Math.random() * getSize().height) + 1;
        be1 = bs1 + (int) (0.5 * Math.random() * getSize().height) + 1;
        be2 = bs2 + (int) (0.5 * Math.random() * getSize().height) + 1;
        for (i = 1; i < getSize().height; i++) {
              light[i] = light[i - 1] + ((Math.random() > 0.5) ? 1 : -1);
              array_1[i] = light[i];
              array_2[i] = light[i];
        }
        for (i = bs1; i < getSize().height; i++) {
              array_1[i] = array_1[i - 1] + ((Math.random() > 0.5) ? 2 : -2);
        }
        for (i = bs2; i < getSize().height; i++) {
              array_2[i] = array_2[i - 1] + ((Math.random() > 0.5) ? 2 : -2);
        }
        for (i = be1; i < getSize().height; i++) {
              array_1[i] = -1;
        }
        for (i = be2; i < getSize().height; i++) {
              array_2[i] = -1;
        }
    }
    public void run() {                             //启动进程
        Graphics g;
        while (true) {
            try {
                  //输出图像
                  drawT_image();
                  g = this.getGraphics();
                  g.drawImage(T_image, 0, 0, this);
                  Thread.sleep((int) (delay * 1000 * Math.random()));
                                                             //线程休眠,时间随机产生
                  no_Light = 1;                              //无闪电标记变量置位
                  createLight();                             //创建闪电
                                                             //输出图像
                  drawT_image();
                  g = this.getGraphics();
                  g.drawImage(T_image, 0, 0, this);
                  Thread.sleep(1000);                        //线程休眠1秒
                  no_Light = 0;                              //无闪电标记变量置位
              } catch (InterruptedException e) {
                  stop();                                    //发生异常,停止线程
              }
        }
    }
}

(3)运行结果的初始页面如图15-19所示。过2秒后就会出现一个新的闪电页面,如图15-20所示。

图15-19 闪电效果

图15-20 黄色闪电的效果

源程序解读

(1)初始化Applet小程序,加载图片:

Image=getImage(getCodeBase(),"xx.gif");

(2)实现闪电的特效。线程休眠,时间随机产生,之后就确定闪电的坐标位置,并通过线程来运行,最后确定闪电的精确位置。

Thread.Sleep((int)(Integer.parseInt(delay)*1000*Math.radom()));

(3)输出闪电以及图像使用Paint()方法来画出闪电的图像,并随之输出相应颜色的闪电。

(4)生成闪电的坐标数组数据,随机产生闪电出现的位置:

light[i]= light[i-1]+((Math.random()>0.5)?i-1);
b1[i]= light[i];
b2[i]= light[i];

实例145 模拟放大镜效果

大家都了解放大镜的基本功能,就是将小的字或图放大,能让人看得清楚。本实例就是一个Java小应用程序(Applet)的例子。使用线程技术,运行小程序就会看到一个图片放大镜的特效,随着放大镜的移动,放大框内的图像就会被放大。

技术要点

模拟放大镜效果的技术要点如下:

• 鼠标移动事件。

• 双缓冲技术处理图像。

• 类MediaTracker的使用。

实现步骤

(1)新建一个类名为Max_Glass.java。

(2)代码如下所示:

package chp15;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class Max_Glass extends Applet implements MouseMotionListener {
Graphics g;
Image image;
Image back_Image;
String name;
    MediaTracker tracker;                         //声明媒体跟踪器tracker
    int Glass_X = 0, Glass_Y = 0;                 //放大镜初始位置
    int Glass_W = 100, Glass_H = 100;             //放大镜宽度、高度
    int width, height;                            //声明背景图片的宽度和高度
    public void init() {                          //初始化applet
          //加载图片
          this.setSize(600, 300);
          g = getGraphics();
          name = "grid.jpg";
          tracker = new MediaTracker(this);
          back_Image = getImage(getCodeBase(), name);
          image = createImage(250, 100);          //设置放大后的图像的大小
          Graphics offg = image.getGraphics();
          offg.drawImage(back_Image, 0, 0, this);
        addMouseMotionListener(this);                  //添加鼠标事件监听
    }
    public void mouseDragged(MouseEvent e) {           //鼠标拖拽事件处理
    }
    public void mouseMoved(MouseEvent e) {             //处理鼠标移动事件
        reprintGlass(Glass_X, Glass_Y, e.getX(), e.getY());
                                                       //通过鼠标位置设置放大镜的位置
                                                       //设置放大镜的当前位置
        Glass_X = e.getX();
        Glass_Y = e.getY();
        //若放大镜溢出applet则进行调整
        if (Glass_X > (width - Glass_W / 2))
              Glass_X = width - Glass_W / 2;
        if (Glass_Y > (height - Glass_H / 2))
              Glass_Y = height - Glass_H / 2;
        printGlass();                                  //调用自定义方法—输出放大镜
    }
    void printGlass() {
        Graphics temp = g.create();                    //复制g的一个实例
        temp.clipRect(Glass_X, Glass_Y, Glass_W, Glass_H);//为temp限制一个矩形区域
        temp.drawImage(back_Image, -Glass_X, -Glass_Y, width * 2, height * 2,
                  null);                               //输出放大后的图像
        g.setColor(Color.black);                       //设置放大镜边框的颜色
        g.drawRect(Glass_X, Glass_Y, Glass_W - 1, Glass_H - 1);//输出放大镜边框
    }
    void reprintGlass(int X, int Y, int new_X, int new_Y) {
                                                   //清除已经画过的矩形框和放大的图像
        Graphics temp = g.create();                    //同上
        if (new_X <= X && new_Y <= Y) {
              temp.clipRect(new_X, new_Y + Glass_H, Glass_W + X - new_X, Y
                      - new_Y);
temp.drawImage(image, 0, 0, null);
temp = g.create();
temp.clipRect(new_X + Glass_W, new_Y, X - new_X, Glass_H + Y
                        - new_Y);
            temp.drawImage(image, 0, 0, null);
} else if (new_X > X && new_Y <= Y) {
temp.clipRect(X, new_Y + Glass_H, Glass_W + new_X - X, Y - new_Y);
temp.drawImage(image, 0, 0, null);
temp = g.create();
temp.clipRect(X, new_Y, new_X - X, Glass_H + Y - new_Y);
temp.drawImage(image, 0, 0, null);
} else if (new_X > X && new_Y > Y) {
temp.clipRect(X, Y, Glass_W + new_X - X, new_Y - Y);
temp.drawImage(image, 0, 0, null);
temp = g.create();
temp.clipRect(X, Y, new_X - X, Glass_H + new_Y - Y);
temp.drawImage(image, 0, 0, null);
} else {
temp.clipRect(new_X, Y, Glass_W + X - new_X, new_Y - Y);
temp.drawImage(image, 0, 0, null);
temp = g.create();
temp.clipRect(new_X + Glass_W, Y, X - new_X, Glass_H + new_Y - Y);
temp.drawImage(image, 0, 0, null);
          }
    }
    public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
              int h) {//判断infoflags参数是否已完全加载了图像,是则返回false;否则返回true
          if (infoflags == ALLBITS) {
            //ALLBITS指示现在已完成了一幅以前绘制的静态图像,并且可以其最终形式再次绘制它
                width = back_Image.getWidth(this);
                height = back_Image.getHeight(this);
                image = createImage(width + Glass_W / 2, height + Glass_H / 2);
                Graphics offg = image.getGraphics();
                offg.setColor(Color.white);
                offg.fillRect(0, 0, width + Glass_W / 2, height + Glass_H / 2);
                offg.drawImage(back_Image, 0, 0, this);
                repaint();
                return false;
          } else
              return true;
    }
    public void paint(Graphics g) {
          g.drawImage(back_Image, 0, 0, this);           //输出背景图片
          printGlass();                                  //画放大镜
    }
}

(3)运行结果如图15-21所示。

图15-21 放大镜效果

源程序解读

(1)Mouse移动事件,鼠标移动主要通过接口MouseMotionListener来实现,mouse-Dragged()当用户按下鼠标按钮,并在松开之前进行移动时发生;在mouseDragged()后松开鼠标不会导致mouseClicked();mouseMoved()当鼠标在组件上移动而不是拖动时发生。

(2)初始化Applet,addMouseMotion-Listener(this):添加鼠标事件监听。

(3)实现放大镜特效,通过该构造方法void printGlass()复制g的一个实例:

• temp=g.create():为temp限制一个矩形区域。

• temp.clipRect(boxX, boxY, boxW, boxH):输出放大后的图像。

• temp.drawImage(back,-boxX,-boxY,width*2,height*2,mull):输出放大镜边框。

• g.setColor(Color.white):g.drawRect(boxX, boxY,boxW-1,boxH-1)。

实例146 水面倒影

水面倒影在生活中可以见到,就是在水中呈现出一个物体的倒影。本实例是一个Java小应用程序(Applet)的例子。使用线程技术,运行小程序就会显示一个水面倒影的特效。

技术要点

水面倒影的技术要点如下:

• 类Math的使用。

• 类MediaTracket的使用。

• 类Graphics的使用。

• 水波特效。

实现步骤

(1)新建一个类名为Reflection.java。

(2)代码如下所示:

package chp15;
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
public class Reflection extends Applet implements Runnable {
    Thread thread = null;                //声明线程
    private Graphics g, inv_g;           //定义绘制正常图像和倒立后图像的Graphics对象
    private Image image, invimage;//image用于载入正常图像,invimage用于载入倒立后的图像
    private int inv;                     //应用于形成倒立影像的变量
    private int image_W = 0, image_H = 0;      //定义装载图片宽和高的变量
    private boolean load_Flag = false;   //定义标志,其作用是标志加载图片是否完毕
    private final int fre = 14;          //定义水纹波动的频率,数值越大,波动越慢
    private String picture_name = "";    //定义图片名字
    public void init() {                 //初始化applet
          picture_name = "flaw.jpg";
    }
    public void paint(Graphics g) {
          if (!load_Flag)                //如果已经载入图片,则返回
              return;
          if (invimage != null) {        //输出倒影图片
                g.drawImage(invimage, (-inv * image_W), image_H, this);
                g.drawImage(invimage, ((fre - inv) * image_W), image_H, this);
          }
          g.drawImage(image, 0, -1, this);       //输出正向图片
    }
    public void start() {                //启动applet,创建并启动线程
          if (thread == null) {
                thread = new Thread(this);
                thread.start();
          }
    }
    public void run() {                  //启动线程
//加载图片
inv = 0;
g = getGraphics();
MediaTracker imageTracker = new MediaTracker(this);
image = getImage(this.getCodeBase(), picture_name);
imageTracker.addImage(image, 0);
try {
              imageTracker.waitForAll();
              load_Flag = !imageTracker.isErrorAny();//检查媒体跟踪器跟踪的所有图像的错误状态
          } catch (InterruptedException e) {
          }
                                                     //图片宽度、图片高度
          image_W = image.getWidth(this);
          image_H = image.getHeight(this);
          this.setSize(image_W, image_H * 2 - 20);
          creatWater();                              //生成倒影
          repaint();                                 //重新输出applet
          while (true) {
              try {
                    if (!load_Flag)
                          return;
                    if (invimage != null) {
                          g.drawImage(invimage, (-inv * image_W), image_H, this);
                          g.drawImage(invimage, ((fre - inv) * image_W), image_H,
                                    this);
                    }
                    g.drawImage(image, 0, -1, this);
                    if (++inv == fre)
                        inv = 0;
                    Thread.sleep(50);
              } catch (InterruptedException e) {
                    stop();
              }
          }
    }
    public void creatWater() {                             //产生水波特效
          Image back = createImage(image_W, image_H + 1);
          Graphics graphics = back.getGraphics();
          int phase = 0;
          int x, y;
          double p1;
          graphics.drawImage(image, 0, 1, this);
          for (int i = 0; i < (image_H >> 1); i++) {
              graphics.copyArea(0, i, image_W, 1, 0, image_H - i);
              graphics.copyArea(0, image_H - 1 - i, image_W, 1, 0, -image_H + 1
                        + (i << 1));
              graphics.copyArea(0, image_H, image_W, 1, 0, -1 - i);
          }
          invimage = createImage((fre + 1) * image_W, image_H);
          inv_g = invimage.getGraphics();
          inv_g.drawImage(back, fre * image_W, 0, this);
          for (phase = 0; phase < fre; phase++) {
              p1 = 2 * Math.PI * (double) phase / (double) fre;
              x = (fre - phase) * image_W;
              for (int i = 0; i < image_H; i++) {
                  y = (int) ((image_H / 14)
                            * ((double) i + 28.0)
                            * Math.sin((double) ((image_H / 14) * (image_H - i))
                                      / (double) (i + 1) + p1) / (double) image_H);
                  if (i < -y)
                        inv_g.copyArea(fre * image_W, i, image_W, 1, -x, 0);
                  else
                        inv_g.copyArea(fre * image_W, i + y, image_W, 1, -x,
                                  -y);
              }
          }
graphics.drawImage(image, 0, 1, this);
image = back;
    }
}

(3)运行结果如图15-22所示。

图15-22 水面倒影

源程序解读

(1)先定义一个正常的图片和一个倒立的图片,初始化变量。

(2)启动线程,同时检查媒体跟踪器跟踪的所有图像的错误状态,如果有错误状态就会抛出异常,如果没有则程序就会向下进行,将上面的图片加载到程序中,再对线程进行控制,将图片生成倒影效果。最后关闭线程。

(3)实现水波特效。通过public void creatWater()方法可以产生倒影中的水波特效。

实例147 美丽的烟花

烟花是一个节日的象征,也是美的加忆,可以让人感觉到那一瞬间的美。本实例是一个Java小应用程序(Applet)的例子。使用线程技术,运行小程序就会显示一个烟花的特效。

技术要点

美丽的烟花的技术要点如下:

• 类Image的使用。

• 类Graphics的使用。

• 双缓冲技术处理闪烁。

实现步骤

(1)新建一个类名为MissileDemo.java。

(2)代码如下所示:

package chp15;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.net.URL;
import java.util.Random;
public class MissileDemo extends Applet implements Runnable {
public int speed, variability, Max_Number, Max_Energy, Max_Patch,
              Max_Length, G;
    public String sound;
    private int width, height;                    //获取当前容器边界的宽和高
    private Thread thread = null;                 //设置线程
    private BeaClassDemo bcd[];                   //创建BeaClassDemo类数组bcd
    public void init() {                          //Applet初始化
          int i;
          this.setSize(400, 400);                 //设置当前容器的宽和高
          width = getSize().width - 1;
          height = getSize().height - 1;
          speed = 30;                             //烟花绽放的速度
          variability = 10;
          Max_Number = 100;                       //可发出烟花的最大数目
          Max_Energy = width + 50;
          Max_Patch = 80;                         //最大的斑点数
          Max_Length = 200;                       //斑点的最大距离
          G = 50;                                 //向地面弯曲的力度
          bcd = new BeaClassDemo[Max_Number];     //初始化BeaClassDemo数组
          for (i = 0; i < Max_Number; i++)
              bcd[i] = new BeaClassDemo(width, height, G);//创建BeaClassDemo类对象
    }
    public void start() {                         //启动线程
          if (thread == null) {
              thread = new Thread(this);
              thread.start();
          }
    }
    public void stop() {                          //停止线程
          if (thread != null) {
              thread.stop();
              thread = null;
          }
    }
    public void run() {
          int i;
          int E = (int) (Math.random() * Max_Energy * 3 / 4) + Max_Energy / 4 + 1;
          int P = (int) (Math.random() * Max_Patch * 3 / 4)    //烟花的斑点数
                    + Max_Patch / 4 + 1;
          int L = (int) (Math.random() * Max_Length * 3 / 4)   //烟花可发射出的距离
                    + Max_Length / 4 + 1;
          long S = (long) (Math.random() * 10000);             //产生的随机数
          boolean sleep;                                       //休眠的标志
          Graphics g = getGraphics();
          URL u = null;
          while (true) {
              try {
                  thread.sleep(1000 / speed);
              } catch (InterruptedException x) {
              }
              sleep = true;
              for (i = 0; i < Max_Number; i++)
                  sleep = sleep && bcd[i].sleep;
              if (sleep && Math.random() * 100 < variability) {
                  E = (int) (Math.random() * Max_Energy * 3 / 4) + Max_Energy / 4
                            + 1;
P = (int) (Math.random() * Max_Patch * 3 / 4) + Max_Patch / 4
                            + 1;
L = (int) (Math.random() * Max_Length * 3 / 4) + Max_Length / 4
                            + 1;
                  S = (long) (Math.random() * 10000);
              }
              for (i = 0; i < Max_Number; i++) {
                  if (bcd[i].sleep && Math.random() * Max_Number * L < 1) {
bcd[i].init(E, P, L, S);
bcd[i].start();
                  }
                  bcd[i].show(g);
              }
        }
    }
    public void paint(Graphics g) {                   //绘制组件
        g.setColor(Color.black);                      //设置背景颜色为黑
        g.fillRect(0, 0, width + 1, height + 1);      //根据参数画矩形
    }
}
class BeaClassDemo {
public boolean sleep = true;
private int energy, patch, length, width, height, G, Xx, Xy, Ex[], Ey[], x,
              y, Red, Blue, Green, t;
    private Random random;                            //声明Random类对象
    public BeaClassDemo(int a, int b, int g) {        //类BeaClassDemo的构造方法
        width = a;
        height = b;
        G = g;
    }
    public void init(int e, int p, int l, long seed) {//初始化
        int i;
    //赋值运算
energy = e;
patch = p;
length = l;
        //创建一个带种子的随机数生成器
        random = new Random(seed);
        Ex = new int[patch];                      //初始化int数组Ex,其长度为patch
        Ey = new int[patch];                      //初始化int数组Ey,其长度为patch
        //随机生成不透明的RGB颜色值
        Red = (int) (random.nextDouble() * 128) + 128;
        Blue = (int) (random.nextDouble() * 128) + 128;
        Green = (int) (random.nextDouble() * 128) + 128;
        Xx = (int) (Math.random() * width / 2) + width / 4;
        Xy = (int) (Math.random() * height / 2) + height / 4;
        for (i = 0; i < patch; i++) {
            Ex[i] = (int) (Math.random() * energy) - energy / 2;
            Ey[i] = (int) (Math.random() * energy * 7 / 8) - energy / 8;
        }
    }
    public void start() {
t = 0;
sleep = false;
    }
    public void show(Graphics g) {               //输出烟花
        if (!sleep)                              //如果休眠状态为false
            if (t < length) {
                  int i, c;
                  double s;
                  Color color;
                  c = (int) (random.nextDouble() * 64) - 32 + Red;
                  if (c >= 0 && c < 256)
                      Red = c;
                  c = (int) (random.nextDouble() * 64) - 32 + Blue;
                  if (c >= 0 && c < 256)
                      Blue = c;
                  c = (int) (random.nextDouble() * 64) - 32 + Green;
                  if (c >= 0 && c < 256)
                          Green = c;
                  color = new Color(Red, Blue, Green);
                  for (i = 0; i < patch; i++) {
                        s = (double) t / 100;
                        x = (int) (Ex[i] * s);
                        y = (int) (Ey[i] * s - G * s * s);
                      g.setColor(color);
                      g.drawLine(Xx + x, Xy - y, Xx + x, Xy - y);
                      if (t >= length / 2) {
                            int j;
                            for (j = 0; j < 2; j++) {
                                  s = (double) ((t - length / 2) * 2 + j) / 100;
                                  x = (int) (Ex[i] * s);
                                  y = (int) (Ey[i] * s - G * s * s);
                                g.setColor(Color.black);
                                g.drawLine(Xx + x, Xy - y, Xx + x, Xy - y);
                            }
                      }
                  }
                  t++;
            } else {
                    sleep = true;
            }
    }
}

(3)运行结果的初始页面如图15-23所示。程序运行的第二个页面如图15-24所示。程序运行的第三个页面,如图15-25所示。

图15-23 烟花效果

图15-24 第二个页面

图15-25 第三个页面

源程序解读

(1)MissileDemo类随机生成烟花的颜色,这种随机的颜色可以通过RGB来实现颜色的分配。

(2)在运行烟花程序时,烟花绽放的颜色通过上面的方法可以控制,现在就得对烟花的速度进行一定的控制,主要是通过speed属性来控制。另外,就是对烟花的斑点数进行控制,可以参照下面的方法:

(Math.random() * Max_Patch * 3 / 4)

(3)最后就可以运行小程序,输出烟花,看到它绽放的效果。

实例148 开窗游戏

本实例实现一个开窗户的小游戏,有25个小窗户,单击一个窗户,周围窗户的图片就会发生变化,当所有的窗户变成开窗的图片时,游戏获胜。游戏过程中可以重置所有窗户到初始状态。

技术要点

开窗游戏的技术要点如下:

• 整个游戏的布局是以一个窗口中放置多个游戏面板的形式完成。

• 掌握JPanel类的使用,可以利用JPanel(LayoutManager layout)方法创建一个指定布局管理器的JPanel对象,然后将其他的组件按指定好的布局载入JPanel中。

• 将25个窗户按钮放在面板中,一个按钮对应一个窗户,用setActionCommand()方法设置按钮被单击时的动作命令。

• 由一个内部类Buttion_Pane专门处理事件,由于它继承了ActionListener接口,所以必须实现actionPerformed方法,在该方法中通过getActionCommand方法获取动作指令并执行相应的功能。

实现步骤

(1)新建一个类名为OpenWindowsGrame.java。

(2)代码如下所示:

package chp15;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
/**
 * 开窗户游戏。当单击游戏界面中任何一个具有窗户图标的按钮时,
 * 它周围的正方形按钮的图标就会发生变化,
 * 当全部正方形按钮的图标都变为开窗时,你就胜利了
 */
public class OpenWindowsGrame extends JFrame {
Buttion_Panel bp = new Buttion_Panel();
Control_Panel cp = new Control_Panel(bp);
public OpenWindowsGrame() {
          super.setTitle("开窗户游戏");
          JPanel panel = new JPanel();            //主面板,控制整体布局的面板
          panel.setLayout(new BorderLayout());
          panel.add(bp, "Center");
          panel.add(cp, "South");
          getContentPane().add(panel);
          this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          this.pack();
          this.setVisible(true);
    }
    public static void main(String[] args) {
          new OpenWindowsGrame();
    }
}
/** 加载按钮的面板 */
class Buttion_Panel extends JPanel implements ActionListener {
    JButton[] bn = new JButton[25];    //包括25个窗户按钮
    Icon icn = new ImageIcon("D:/abc/close.jpg");
    boolean[] bl = new boolean[25];    //定义boolean类型的数组,用于接收开闭窗的状态
    public Buttion_Panel() {
          this.setLayout(new GridLayout(5, 5));  //面板采用网格布局管理器
          for (int i = 0; i < 25; i++) {
              bn[i] = new JButton(icn);
              bn[i].setActionCommand(String.valueOf(i));
              bn[i].addActionListener(this);
              this.add(bn[i]);
          }
          this.setPreferredSize(new Dimension(350, 350)); //面板大小值为350*350
    }
/** 单击一个窗户按钮时引起开窗户事件 */
boolean f = false;//标志窗户的状态
Icon C_icn = new ImageIcon("D:/abc/close.jpg");
Icon O_icn = new ImageIcon("D:/abc/open.jpg");
public void actionPerformed(ActionEvent a) {
          f = !f;
          int x = Integer.parseInt(a.getActionCommand());      //获取被单击窗户的ID
          set_Select(x, f);                                    //设置被单击窗户被选
          isWin();                                             //判断是否胜利
    }
    //当一个窗户被选中时进行的操作。需要改变周围窗户的图标
    private void set_Select(int x, boolean flag) {
          if (x == 0) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x + 1], flag, x + 1);
change_Icon(bn[x + 5], flag, x + 5);
          } else if (x > 0 && x < 4) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 1], flag, x - 1);
change_Icon(bn[x + 1], flag, x + 1);
change_Icon(bn[x + 5], flag, x + 5);
          } else if (x == 4) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 1], flag, x - 1);
change_Icon(bn[x + 5], flag, x + 5);
          } else if (x == 20) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 5], flag, x - 5);
change_Icon(bn[x + 1], flag, x + 1);
          } else if (x == 24) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 5], flag, x - 5);
change_Icon(bn[x - 1], flag, x - 1);
          } else if (x > 20 && x < 24) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 5], flag, x - 5);
change_Icon(bn[x - 1], flag, x - 1);
change_Icon(bn[x + 1], flag, x + 1);
          } else if (x % 5 == 0) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 5], flag, x - 5);
change_Icon(bn[x + 1], flag, x + 1);
change_Icon(bn[x + 5], flag, x + 5);
          } else if (x % 5 == 4) {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 5], flag, x - 5);
change_Icon(bn[x - 1], flag, x - 1);
change_Icon(bn[x + 5], flag, x + 5);
          } else {
change_Icon(bn[x], flag, x);
change_Icon(bn[x - 5], flag, x - 5);
change_Icon(bn[x - 1], flag, x - 1);
change_Icon(bn[x + 1], flag, x + 1);
change_Icon(bn[x + 5], flag, x + 5);
          }
    }
    private void change_Icon(JButton bon, boolean flag, int i) {//改变周围窗户图标的方法
    //如果窗户的状态是打开的,则给相应的按钮设置打开的图标
          if (flag) {          //flag:false表示关,true表示开
              bl[i] = true;
              bon.setIcon(O_icn);
          } else {
          //如果窗户的状态是关闭的,则给相应的按钮设置关闭的图标
              bon.setIcon(C_icn);
              bl[i] = false;
          }
    }
    //判断是否胜出
    private void isWin() {
          int a = 1;
          //当25个窗户状态都变成true时,获胜
          for (int j = 0; j < 25; j++) {
              if (bl[j]) {
                    a++;
              }
          }
          if (a > 25) {
              JOptionPane.showMessageDialog(null, "恭喜过关");
          }
    }
}
/** 控制面板 */
class Control_Panel extends JPanel implements ActionListener {
JLabel label = new JLabel("开窗户游戏");
JButton restart = new JButton("重置");          //游戏重新开始按钮
Buttion_Panel bp;
Icon icn = new ImageIcon("D:/abc/close.jpg");
//构造函数,参数为待控制的窗户面板
    public Control_Panel(Buttion_Panel bp) {
this.bp = bp;
restart.addActionListener(this);
this.add(label);
this.add(restart);
    }
//重设窗户面板,将所有窗户变成关闭状态
    public void actionPerformed(ActionEvent a) {
          for (int i = 0; i < 25; i++) {
              bp.bn[i].setIcon(icn);
          }
    }
}

(3)运行结果的初始页面如图15-26所示。程序运行的第二个页面如图15-27所示。通过全部关卡,就会出现一个页面,如图15-28所示。

图15-26 开窗户游戏界面

图15-27 第二个页面

图15-28 过关之后的页面

源程序解读

1. Buttion_Panel类

该类定义了游戏主面板。

(1)继承JPanel,采用了网格布局管理器,设置网格的行数和列数均为5,刚好容纳25个窗户按钮。

(2)窗户按钮上没有文本,通过JButton的setActionCommand方法为按钮设置动作命令,当用户单击按钮时,根据按钮的动作命令,判断哪个按钮被单击了。为按钮添加事件处理器actionPerformed方法,当按钮被单击时,由actionPerformed方法处理。该方法实现了ActionListener接口,表示它是一个事件处理器的类。当按钮被单击时,actionPerformed方法根据ActionEvent参数的getActionCommand方法定位引起事件的源对象,再调用set_Select方法改变源对象周围的窗户按钮的背景图片,最后调用isWin方法判断是否胜出。

(3)set_Select私有方法改变某个窗户的所有邻居窗户的背景图片。根据被单击窗户的序号,确定邻居窗户的序号,中间的窗户有四个邻居窗户,靠边的窗户有三个邻居窗户,四个角上的窗户只有2个邻居窗户。

(4)isWin方法依次遍历25个窗体按钮,如果它们的背景图片都为开窗状态,则表明游戏胜出。

(5)change_Icon方法是根据当前按钮的窗户的状态和ID来改变背景图片。

2. Control_Panel类

该类定义了控制面板,包含一个标签和“重置”按钮。

(1)Control_Pane构造方法实现了ActionListener接口,它的actionPerformed方法将所有25个窗户按钮的背景图片设置为关闭窗户。

(2)注册“重置”按钮的事件处理器为Reset对象,当单击它时,所有窗户背景图片变成关闭窗户。

3. OpenWindowsGrame类

该类是游戏的主运行类,在main方法中创建一个JFrame,通过JFrame的getContentPane方法获得窗口的容器,调用容器的add方法将游戏主面板放入到窗口中;通过setDefault-CloseOperation(JFrame.EXIT_ON_CLOSE)语句设置当用户单击窗口的关闭按钮时,退出游戏;调用JFrame的pack方法调整窗口中各组件到最佳大小;通过JFrame的setVisible方法显示主窗口。