Java学习笔记之GUI编程

GUI编程

1 AWT 类

1.1 AWT 介绍
  • 包含了很多的类和接口

  • 元素:窗口、标签、按钮、文本框 ...

  • 最基本的类

  • AWT 类关系图

    AWT类关系图

1.2 Frame
public class Info {
    public static void main(String[] args) {
        //标题
        Frame frame = new Frame("My GUI");
        //可见性
        frame.setVisible(true);
        //尺寸
        frame.setSize(300, 200);
        //背景
        frame.setBackground(new Color(253, 247, 200));
        //弹出位置
        frame.setLocation(500, 500);
        //尺寸是否可变
        frame.setResizable(false);
    }
}
//问题:窗口无法关闭,必须结束程序进程
public class MyFrame extends Frame {
    public static void main(String[] args) {

        int id = 0;
        MyFrame myFrame1 = new MyFrame(id, 100, 100, 500, 200, Color.blue);
        MyFrame myFrame2 = new MyFrame(++id, 600, 100, 500, 200, Color.red);
        MyFrame myFrame3 = new MyFrame(++id, 100, 300, 500, 200, Color.CYAN);
        MyFrame myFrame4 = new MyFrame(++id, 600, 300, 500, 200, Color.YELLOW);
    }

    public MyFrame(int id, int x, int y, int width, int height, Color color){
        super("MyFrame " + id);
        setBackground(color);
        setVisible(true);
        setBounds(x, y, width, height);
    }
}
1.3 Panel
public class MyPanel {

    public static void main(String[] args) {
        Frame frame = new Frame();
        Panel panel = new Panel();

        //不设置的话,窗口默认置顶
        frame.setLayout(null);
        frame.setBounds(200, 200, 500, 500);
        frame.setBackground(Color.blue);

        panel.setBackground(Color.YELLOW);
        panel.setBounds(50, 50, 400, 400);

        frame.add(panel);
        frame.setVisible(true);
		
        //解决关闭 Frame 问题
        //使用监听器来控制面板的关闭
        //适配器模式 new WindowAdapter()
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
}
1.4 布局管理器
1 流式布局
public class MyLayout {

    public static void main(String[] args) {
        Frame frame = new Frame();
        frame.setVisible(true);
        frame.setSize(500,500);

        new MyLayout().flowLayout(frame);
    }

    //流式布局
    public void flowLayout(Frame frame){
        Button button1 = new Button("Button1");
        Button button2 = new Button("Button2");
        Button button3 = new Button("Button3");

        frame.add(button1);
        frame.add(button2);
        frame.add(button3);

        frame.setLayout(new FlowLayout(FlowLayout.RIGHT));
    }
}

FlowLayout

2 东南西北中布局
public class MyLayout {

    public static void main(String[] args) {
        Frame frame = new Frame();
        frame.setVisible(true);
        frame.setSize(500,500);

        new MyLayout().borderLayout(frame);
    }


    //东南西北中布局
    public void borderLayout(Frame frame){
        Button west = new Button("west");
        Button east = new Button("east");
        Button north = new Button("north");
        Button south = new Button("south");
        Button center = new Button("center");

        frame.add(west, BorderLayout.WEST);
        frame.add(east, BorderLayout.EAST);
        frame.add(north, BorderLayout.NORTH);
        frame.add(south, BorderLayout.SOUTH);
        frame.add(center, BorderLayout.CENTER);
    }
}

BorderLayout

3 网格布局
public class MyLayout {

    public static void main(String[] args) {
        Frame frame = new Frame();
        frame.setVisible(true);
        frame.setSize(500,500);

        new MyLayout().gridLayout(frame);
    }
    

    //网格布局
    public void gridLayout(Frame frame){
        Button button1 = new Button("button1");
        Button button2 = new Button("button2");
        Button button3 = new Button("button3");
        Button button4 = new Button("button4");
        Button button5 = new Button("button5");
        Button button6 = new Button("button6");

        frame.setLayout(new GridLayout(2, 3));	//行数和列数

        frame.add(button1);
        frame.add(button2);
        frame.add(button3);
        frame.add(button4);
        frame.add(button5);
        frame.add(button6);
    }
}

GrideLayout

1.5 GUI布局练习

尝试布局出如下图的面板

GUI练习

public class MyGUITest {

    public static void main(String[] args) {
        Frame frame = new Frame("MyGUITest");
        frame.setVisible(true);
        frame.setBounds(50, 50, 600,400);
        frame.setResizable(false);
        frame.setLayout(new GridLayout(2,1));
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                super.windowClosing(e);
                System.exit(0);
            }
        });
        new MyGUITest().initGUI(frame);
    }

    
    public void initGUI(Frame frame){

        //Panel
        Panel p1 = new Panel(new BorderLayout());
        Panel p2 = new Panel(new GridLayout(2, 1));
        Panel p3 = new Panel(new BorderLayout());
        Panel p4 = new Panel(new GridLayout(2,2));

        p1.add(new Button("Button-1"), BorderLayout.WEST);
        p1.add(new Button("Button-3"), BorderLayout.EAST);
        p2.add(new Button("Button-2"));
        p2.add(new Button("Button-4"));
        p1.add(p2, BorderLayout.CENTER);

        p3.add(new Button("Button-5"), BorderLayout.WEST);
        p3.add(new Button("Button-10"), BorderLayout.EAST);

        for (int i = 6; i < 10; i++)
            p4.add(new Button("Button-" + i));
        p3.add(p4, BorderLayout.CENTER);
        
        frame.add(p1);
        frame.add(p3);
    }
}

程序结果

GUI练习结果

1.6 事件监听器
  • 一个组件可以同时监听多个事件
  • 多个组件也可以同时监听一个事件
public class ListenerInfo {
    public static void main(String[] args) {

        Frame frame = new Frame();
        Button button1 = new Button("Button-1");
        Button button2 = new Button("Button-2");
        Button button3 = new Button("Button-3");

        frame.setBounds(100,100,200,100);
        frame.setVisible(true);
        frame.setLayout(new GridLayout(2,1));
        frame.add(button1);
        frame.add(button2);
        frame.add(button3);
        windowClose(frame);

        //多个按钮可以监听同一个事件
        MyListener1 myListener1 = new MyListener1();
        button1.addActionListener(myListener1);
        button2.addActionListener(myListener1);

        //一个按钮可以同时监听多个事件
        MyListener2 myListener2 = new MyListener2();
        button3.addActionListener(myListener1);
        button3.addActionListener(myListener2);
    }

    private static void windowClose(Frame frame) {
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
}

//监听器
class MyListener1 implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Hello World!");
    }
}

class MyListener2 implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Hello Java!");
    }
}
  • 使用 setActionCommand()getActionCommand() 可以方便的实现监听(不用重复写监听器的实现)
public class ListenerTest {

    public static void main(String[] args) {
        Frame frame = new Frame();
        frame.setVisible(true);
        frame.setBounds(100,100,200,200);
        frame.setLayout(new FlowLayout());

        Button bStart = new Button("button-start");
        Button bStop = new Button("button-stop");

        frame.add(bStart);
        frame.add(bStop);
		
        //设置监听器的对应的命令
        MyListener myListener = new MyListener();
        bStart.addActionListener(myListener);
        bStart.setActionCommand("start");
        bStop.addActionListener(myListener);
        bStop.setActionCommand("stop");

        new ListenerTest().windowClose(frame);
    }

    private void windowClose(Frame frame){
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
}


class MyListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("start"))
            System.out.println("program is start");
        else if (e.getActionCommand().equals("stop"))
            System.out.println("program is stop");
    }
}
1.7 文本框 TextField
public class TextFiledInfo {

    public static void main(String[] args) {
        new MyFrameTest();
    }
}

class MyFrameTest extends Frame {
    public MyFrameTest(){

        Frame frame = new Frame();
        //TextField 只能输入一行,TextArea 可以输入多行
        TextField textField = new TextField();
        add(textField);
        setVisible(true);
        TextFiledListener textFiledListener = new TextFiledListener();
        textField.addActionListener(textFiledListener);

        //设置文本框中替换的内容
        textField.setEchoChar('*');
        windowClose();
    }

    public void windowClose(){
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
}

class TextFiledListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        TextField text = (TextField)e.getSource();
        System.out.println(text.getText());
        text.setText("");    //用户输入完毕按回车后,清空文本框
    }
}
1.7 简易计算器实现

题目:输入两个数,计算出两数之和并返回

思路:

  • CalculatorTest 类:程序启动类
  • MyCalculator 类:计算器的图形化界面
  • CalculatorListener 类:文本框的监听器
public class CalculatorTest {
    public static void main(String[] args) {
        new MyCalculator().loadFrame();
    }
}


class MyCalculator extends Frame {

    TextField num1, num2, num3;

    public void loadFrame() {
        setBounds(200, 200, 500, 150);
        setVisible(true);
        setLayout(new FlowLayout());
        pack();

        this.num1 = new TextField(5);
        this.num2 = new TextField(5);
        this.num3 = new TextField(10);
        Label label = new Label("+");
        Button button = new Button("=");
        button.addActionListener(new CalculatorListener(this));
        
        add(num1);
        add(label);
        add(num2);
        add(button);
        add(num3);

        windowClosed();
    }

    private void windowClosed() {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
}


class CalculatorListener implements ActionListener {

    MyCalculator myCalculator;

    public CalculatorListener(MyCalculator myCalculator) {
        this.myCalculator = myCalculator;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        int number1 = Integer.parseInt(myCalculator.num1.getText());
        int number2 = Integer.parseInt(myCalculator.num2.getText());

        int number3 = number1 + number2;

        myCalculator.num1.setText("");
        myCalculator.num2.setText("");
        myCalculator.num3.setText("" + number3);
    }
}

代码优化:修改外部类 CalculatorListener 为内部类:降低编码成本,可以直接访问外部类的所有属性

public class CalculatorTest {
    public static void main(String[] args) {
        new MyCalculator().loadFrame();
    }
}


class MyCalculator extends Frame {

    TextField num1, num2, num3;

    public void loadFrame() {
        setVisible(true);
        setLayout(new FlowLayout());
        pack();

        this.num1 = new TextField(5);
        this.num2 = new TextField(5);
        this.num3 = new TextField(10);
        Label label = new Label("+");
        Button button = new Button("=");
        button.addActionListener(new CalculatorListener());

        add(num1);
        add(label);
        add(num2);
        add(button);
        add(num3);

        windowClosed();
    }

    private void windowClosed() {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    private class CalculatorListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            int number1 = Integer.parseInt(num1.getText());
            int number2 = Integer.parseInt(num2.getText());

            int number3 = number1 + number2;

            num1.setText("");
            num2.setText("");
            num3.setText("" + number3);
        }
    }
}
1.8 Paint
1 自动绘制图案
public class PaintTest {

    public static void main(String[] args) {
        new MyPaint().loadFrame();
    }
}

class MyPaint extends Frame{
    public void loadFrame(){
        setVisible(true);
        setBounds(200,200,500,400);
    }

    @Override
    public void paint(Graphics g) {
        g.setColor(Color.lightGray);
        g.drawOval(50,50,20,30);
        g.setColor(Color.GREEN);
        g.fillRect(200,200,100,50);
    }
}
2 鼠标监听

小型画板思路

public class MyPaintPoints {

    public static void main(String[] args) {
        new PointFrame("画图");
    }
}

class PointFrame extends Frame{

    //使用集合来存储鼠标点击的点
    ArrayList points;

    public PointFrame(String title){
        super(title);
        setBounds(200,200,500,400);
        setVisible(true);

        //存储鼠标点击的点
        points = new ArrayList<>();

        //鼠标监听器
        this.addMouseListener(new MyMouseListener());
    }

    @Override
    public void paint(Graphics g) {
        Iterator iterator = points.iterator();
        while (iterator.hasNext()){
            Point point = (Point) iterator.next();
            g.setColor(Color.blue);
            g.fillOval(point.x, point.y, 10,10);
        }
    }

    //添加点到点集合中
    public void addPoint(Point point){
        points.add(point);
    }
}


class MyMouseListener extends MouseAdapter{
    @Override
    public void mouseClicked(MouseEvent e) {
        PointFrame pointFrame = (PointFrame)e.getSource();
        pointFrame.addPoint(new Point(e.getX(), e.getY()));
        pointFrame.repaint();
    }
}
3 窗口监听
public class MyWindowListener {

    public static void main(String[] args) {
        new MyWindowListener();
    }

    public MyWindowListener(){
        Frame frame = new Frame();
        frame.setVisible(true);
        frame.setBounds(200,200,200,100);

        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                System.out.println("windowOpened");
            }

            @Override
            public void windowClosing(WindowEvent e) {
                System.out.println("windowClosing");
            }

            @Override		//常用
            public void windowClosed(WindowEvent e) {
                System.out.println("windowClosed");
            }

            @Override		//常用
            public void windowActivated(WindowEvent e) {
                System.out.println("windowActivated");
            }
        });
    }
}
4 键盘监听
public class MyKeyListener {

    public static void main(String[] args) {
        new MyKey();
    }
}

class MyKey extends Frame{

    public MyKey(){
        setBounds(100,100,200,200);
        setVisible(true);
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_UP)
                    System.out.println("key UP Pressed");
            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_UP)
                    System.out.println("key UP Released");
            }
        });
    }
}

2 Swing 类

2.1 Swing 介绍
  • 可以做更高级的操作,是 AWT 的子类
2.2 窗口、面板
public class JFrameInfo {

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

class MyJFrame extends JFrame{
    public void init(){
        setVisible(true);
        setBounds(100,100,200,200);
        //窗体关闭方法
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        JLabel jLabel = new JLabel("Label");
        add(jLabel);
        //设置水平居中
        jLabel.setHorizontalAlignment(SwingConstants.CENTER);

        //将标签装进容器(没有 Container, 所有的设置不会生效)
        Container container = getContentPane();
        container.setBackground(Color.blue);
    }
}
2.3 弹窗 Dialog
public class DialogInfo {

    public static void main(String[] args) {
        new MyDialog();
    }
}

class MyDialog extends JFrame{

    public MyDialog(){
        this.setBounds(200,200,300,250);
        this.setVisible(true);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        Container container = this.getContentPane();
        container.setLayout(null);  //绝对定位,常用

        JButton button = new JButton("点击按钮弹出弹窗");
        button.setBounds(20,20,100,50);
        button.setHorizontalAlignment(SwingConstants.CENTER);
        button.setVerticalAlignment(SwingConstants.CENTER);
        container.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                new newDialog();
            }
        });
    }
}


class newDialog extends JDialog{
    public newDialog() {
        //弹窗默认有关闭事件,不用重写
        this.setVisible(true);
        this.setBounds(300,300,100,100);
        this.setLayout(null);

        JLabel jLabel = new JLabel("这是弹窗");
        Container container = new Container();
        container.add(jLabel);
        jLabel.setHorizontalAlignment(SwingConstants.CENTER);
        container.setBackground(Color.CYAN);
    }
}
2.4 标签
1 Icon 标签
public class MyJLabel extends JFrame implements Icon {

    int width, height;

    public MyJLabel (){};

    public MyJLabel(int width, int height){
        this.width = width;
        this.height = height;
    }

    public void init(){
        this.setBounds(200,200,300,300);
        this.setVisible(true);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        MyJLabel myJLabel = new MyJLabel(50, 50);
        JLabel jLabel = new JLabel("iconTest", myJLabel, SwingConstants.CENTER);
        Container container = getContentPane();
        container.add(jLabel);
    }

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        g.fillOval(x, y, width, height);
    }

    @Override
    public int getIconWidth() {
        return this.width;
    }

    @Override
    public int getIconHeight() {
        return this.height;
    }

    public static void main(String[] args) {
        new MyJLabel().init();
    }
}
2 Image 标签
public class ImageLabelTest extends JFrame{

    public static void main(String[] args) {
        new ImageLabelTest();
    }

    public ImageLabelTest(){
        JLabel label = new JLabel("ImageIcon");
        //获取当前类所在目录下的文件
        URL url = ImageLabelTest.class.getResource("tt.jpg");
        ImageIcon imageIcon = new ImageIcon(url);
        
        label.setIcon(imageIcon);
        label.setHorizontalAlignment(SwingConstants.CENTER);

        Container container = getContentPane();
        container.add(label);

        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}
2.5 面板
1 JPanel
public class MyJPanelInfo extends JFrame {

    public MyJPanelInfo(){

        Container container = getContentPane();
        container.setLayout(new GridLayout(2,1,10,10));
        JPanel panel1 = new JPanel(new GridLayout(1,3));
        JPanel panel2 = new JPanel(new GridLayout(2,2));
        JButton button1 = new JButton("1");
        JButton button2 = new JButton("2");
        JButton button3 = new JButton("3");
        JButton button4 = new JButton("4");
        JButton button5 = new JButton("5");
        JButton button6 = new JButton("6");
        JButton button7 = new JButton("7");

        panel1.add(button1);
        panel1.add(button2);
        panel1.add(button3);
        panel2.add(button4);
        panel2.add(button5);
        panel2.add(button6);
        panel2.add(button7);

        container.add(panel1);
        container.add(panel2);

        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(500,500);
    }

    public static void main(String[] args) {
        new MyJPanelInfo();
    }
}
2 JScrollPane
public class JScrollPanelInfo extends JFrame {

    public JScrollPanelInfo(){
        Container container = getContentPane();
        this.setVisible(true);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JTextArea text = new JTextArea(300,200);
        text.setText("Hello Java!");

        JScrollPane panel = new JScrollPane(text);
        container.add(panel);
    }

    public static void main(String[] args) {
        new JScrollPanelInfo();
    }
}
2.6 按钮
1 图片按钮
public class ImageButtonInfo extends JFrame {

    public ImageButtonInfo(){

        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();

        URL url = ImageButtonInfo.class.getResource("tt.jpg");
        Icon myImage = new ImageIcon(url);

        JButton button = new JButton();
        button.setIcon(myImage);
        button.setToolTipText("图片按钮");

        container.add(button);

    }

    public static void main(String[] args) {
        new ImageButtonInfo();
    }
}
2 单选框
public class RadioButtonInfo extends JFrame {

    public static void main(String[] args) {
        new RadioButtonInfo();
    }

    public RadioButtonInfo(){
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();

        JRadioButton radioButton1 = new JRadioButton("radioButton1");
        JRadioButton radioButton2 = new JRadioButton("radioButton2");
        JRadioButton radioButton3 = new JRadioButton("radioButton3");

        //单选框只能选择一个,因此要将所有的按钮分成一个组
        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.add(radioButton1);
        buttonGroup.add(radioButton2);
        buttonGroup.add(radioButton3);

        container.add(radioButton1, BorderLayout.CENTER);
        container.add(radioButton2, BorderLayout.NORTH);
        container.add(radioButton3, BorderLayout.SOUTH);
    }
}
3 复选框
public class CheckBoxInfo extends JFrame {

    public static void main(String[] args) {
        new CheckBoxInfo();
    }

    public CheckBoxInfo(){
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();

        JCheckBox checkBox1 = new JCheckBox("checkBox1");
        JCheckBox checkBox2 = new JCheckBox("checkBox2");

        container.add(checkBox1, BorderLayout.NORTH);
        container.add(checkBox2, BorderLayout.SOUTH);
    }
}
2.7 列表
1 下拉列表框
public class JComboBoxInfo extends JFrame {

    public static void main(String[] args) {
        new JComboBoxInfo();
    }

    public JComboBoxInfo(){
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();

        JComboBox status = new JComboBox();

        status.addItem(null);
        status.addItem("喜剧");
        status.addItem("校园剧");
        status.addItem("推理剧");

        container.add(status);
    }
}
2 列表框
public class JListInfo extends JFrame {

    public static void main(String[] args) {
        new JListInfo();
    }

    public JListInfo(){
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();
        //静态列表
        String[] str = {"Item-1", "Item-2", "Item-3"};
        //动态列表
        Vector str = new Vector();
        str.add("A");
        str.add("B");
        str.add("C");
        
        JList jList = new JList(str);

        container.add(jList);
    }
}
2.8 文本框
1 文本框
public class JTextFieldInfo extends JFrame {

    public static void main(String[] args) {
        new JTextFieldInfo();
    }

    public JTextFieldInfo(){
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();

        JTextField text = new JTextField("Hello", 5);
        JTextField text2 = new JTextField("Hello", 5);

        container.add(text, BorderLayout.SOUTH);
        container.add(text2, BorderLayout.NORTH);
    }
}
2 密码框
public class JTextFieldInfo2 extends JFrame {

    public static void main(String[] args) {
        new JTextFieldInfo2();
    }

    public JTextFieldInfo2(){
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();
		
        //默认显示 *
        JPasswordField jPasswordField = new JPasswordField();  
        jPasswordField.setEchoChar('@');    //可以设置别的
        container.add(jPasswordField);
    }
}
3 文本域
public class JTextAreaInfo extends JFrame {

    public static void main(String[] args) {
        new JTextAreaInfo();
    }

    public JTextAreaInfo(){
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setBounds(200,200,500,300);

        Container container = getContentPane();

        JTextArea text = new JTextArea("Hello");

        container.add(text);
    }
}

练习:贪吃蛇

1 界面设计
  • 主界面大小:900 * 720
  • 蛇身大小:25 * 25
  • 蛇头大小:25 * 25
  • 食物大小:25 * 25
2 静态页面绘制
2.1 项目结构

贪吃蛇项目结构

2.2 初始化界面
  • 面板类
public class GamePanel extends JPanel{

    public GamePanel(){
    }

    //面板绘制方法
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    //清屏
        setBackground(Color.WHITE);

        //头部计分板
        GameSources.info.paintIcon(this, g, 20,5);

        //游戏区域
        g.fillRect(20,65,850,600);
    }
}
  • 资源加载类
public class GameSources extends JFrame{

    //头部计分板
    public static URL INFO_URL = GameSources.class.getResource("../statics/header.png");

    //蛇身体
    public static URL HEAD_UP_URL = GameSources.class.getResource("../statics/up.png");
    public static URL HEAD_DOWN_URL = GameSources.class.getResource("../statics/down.png");
    public static URL HEAD_LEFT_URL = GameSources.class.getResource("../statics/left.png");
    public static URL HEAD_RIGHT_URL = GameSources.class.getResource("../statics/right.png");
    public static URL BODY_URL = GameSources.class.getResource("../statics/body.png");

    //食物
    public static URL FOOD_URL = GameSources.class.getResource("../statics/food.png");


    public static ImageIcon info = new ImageIcon(INFO_URL);

    public static ImageIcon up = new ImageIcon(HEAD_UP_URL);
    public static ImageIcon down = new ImageIcon(HEAD_DOWN_URL);
    public static ImageIcon left = new ImageIcon(HEAD_LEFT_URL);
    public static ImageIcon right = new ImageIcon(HEAD_RIGHT_URL);
    public static ImageIcon body = new ImageIcon(BODY_URL);

    public static ImageIcon food = new ImageIcon(FOOD_URL);
}

  • 主启动类
public class Application {

    public static void main(String[] args) {

        //主界面初始化
        JFrame frame = new JFrame("悟道九霄 — 贪吃蛇 1.0.0");

        frame.setBounds(450,160,910,720);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(new GamePanel());

        frame.setVisible(true);
    }
}
3 让蛇动起来
3.1 静态小蛇和提示语
public class GamePanel extends JPanel implements KeyListener {

    int length = 0;
    //蛇头坐标
    int[] snakeX = new int[800];
    int[] snakeY = new int[800];
    String fx;
    boolean isStart = false;    //游戏是否开始

    public GamePanel(){
        init();
        //获取键盘的监听事件
        this.addKeyListener(this);
        this.setFocusable(true);
    }

    public void init() {
        //蛇初始化
        fx = "RIGHT";      //蛇头默认朝右
        length = 3;
        snakeX[0] = 100;  snakeY[0] = 100;
        snakeX[1] = 75;   snakeY[1] = 100;
        snakeX[2] = 50;   snakeY[2] = 100;
    }

    //面板绘制方法
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);    //清屏
        setBackground(Color.WHITE);

        //头部计分板
        GameSources.info.paintIcon(this, g, 20,5);

        //游戏区域
        g.fillRect(20,65,850,600);

        //蛇头 + 蛇身
        if (fx.equals("RIGHT"))
            GameSources.right.paintIcon(this,g,snakeX[0], snakeY[0]);
        else if (fx.equals("LEFT"))
            GameSources.left.paintIcon(this,g,snakeX[0], snakeY[0]);
        else if (fx.equals("UP"))
            GameSources.up.paintIcon(this,g,snakeX[0], snakeY[0]);
        else if (fx.equals("DOWN"))
            GameSources.down.paintIcon(this,g,snakeX[0], snakeY[0]);

        for (int i = 1; i < length; i++)    //通过 length 来控制蛇的长度
            GameSources.body.paintIcon(this, g, snakeX[i], snakeY[i]);

        //是否开始游戏提示
        if (!isStart){
            /*【重点】:可能是 JPanel 版本更新的问题,在构造函数中将 setFocusable 设置为 true 之后
            以前不用再次激活,便可实现实时监听键盘
            但是现在,需要在“适当的时候”再次激活,才可以实现键盘监听
            此处因为 isStart 改变,所以要重新激活焦点才可以再次监听
            */
            this.requestFocus();    
            
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格 开始/暂停 游戏",220, 350);
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (keyCode == KeyEvent.VK_SPACE){
            isStart = !isStart;
            repaint();  //重复刷新,小蛇才会动起来
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }
}

【遇到的问题】

问:明明在构造方法里添加了 keyListener,键盘焦点 setFocusable 也设置为 true ,但是按键盘就是没反应?

答:这是一个很容易忽略的小细节(导致我当初 Debug 了一下午,各种 Google...),解决此问题有两种方法

法一:在 isStart 改变的时候,这个时候键盘已经失去焦点,需要再次激活,因此需要在重新绘画之前,再次激活键盘焦点

if (!isStart){
	this.requestFocus();  //再次激活焦点
 ......
}

法二:在 Application 启动类中,需要将主窗体 JFrame 的可见性设置放在调用 GamePanel 的构造方法之后,若写在构造方法之前,可见性已经为 true,窗体已经显示,因此 GamePanel 构造方法中的 setFocusable 就算设置为 true,也只能在内存中生效,所以如【法一】 所述,使用前需要再次激活。为了一劳永逸,将构造方法写在窗体可见性的前边即可。即:

//错误写法
public class Application {

 public static void main(String[] args) {
     frame.setVisible(true);
     frame.add(new GamePanel());
 }
}

//正确写法
public class Application {

 public static void main(String[] args) {
     frame.add(new GamePanel());
     frame.setVisible(true);
 }
}
3.2 静态初始界面
  • 暂停时

暂停

  • 开始后(点击空格后)

开始

3.3 添加定时器
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    int length = 0;
    //蛇头坐标
    int[] snakeX = new int[800];
    int[] snakeY = new int[800];
    String fx;
    boolean isStart = false;    //游戏是否开始
    //通过改变 delay 来控制游戏的速率大小
    Timer timer = new Timer(100, this);

    public GamePanel(){
        init();
        //获取键盘的监听事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start();	//一定要在构造方法里启动定时器,否则没用
    }

    public void init() {
        //蛇初始化
        fx = "RIGHT";      //蛇头默认朝右
        length = 3;
        snakeX[0] = 100;  snakeY[0] = 100;
        snakeX[1] = 75;   snakeY[1] = 100;
        snakeX[2] = 50;   snakeY[2] = 100;
    }

    //面板绘制方法
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);    //清屏
        setBackground(Color.WHITE);

        //头部计分板
        GameSources.info.paintIcon(this, g, 20,5);

        //游戏区域
        g.fillRect(20,65,850,600);

        //蛇头 + 蛇身
        if (fx.equals("RIGHT"))
            GameSources.right.paintIcon(this,g,snakeX[0], snakeY[0]);
        else if (fx.equals("LEFT"))
            GameSources.left.paintIcon(this,g,snakeX[0], snakeY[0]);
        else if (fx.equals("UP"))
            GameSources.up.paintIcon(this,g,snakeX[0], snakeY[0]);
        else if (fx.equals("DOWN"))
            GameSources.down.paintIcon(this,g,snakeX[0], snakeY[0]);

        for (int i = 1; i < length; i++)    //通过 length 来控制蛇的长度
            GameSources.body.paintIcon(this, g, snakeX[i], snakeY[i]);

        //是否开始游戏提示
        if (!isStart){
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格 开始/暂停 游戏",220, 350);
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        //游戏的暂停和开始
        if (keyCode == KeyEvent.VK_SPACE){
            isStart = !isStart;
            repaint();
        }

        //蛇的走向
        if (keyCode == KeyEvent.VK_RIGHT){
            fx = "RIGHT";
        }else if (keyCode == KeyEvent.VK_UP){
            fx = "UP";
        }else if (keyCode == KeyEvent.VK_LEFT){
            fx = "LEFT";
        }else if (keyCode == KeyEvent.VK_DOWN){
            fx = "DOWN";
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (isStart){
            //右移
            for (int i = length - 1; i > 0; i--){
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            //头部移动
            if (fx.equals("RIGHT")){
                snakeX[0] += 25;
                if (snakeX[0] > 845){snakeX[0] = 25;}
            }else if (fx.equals("LEFT")){
                snakeX[0] -= 25;
                if (snakeX[0] < 25){snakeX[0] = 845;}
            }else if (fx.equals("UP")){
                snakeY[0] -= 25;
                if (snakeY[0] < 75) {snakeY[0] = 630;}
            }else if (fx.equals("DOWN")){
                snakeY[0] += 25;
                if (snakeY[0] > 630) {snakeY[0] = 75;}
            }
            repaint();
        }
        timer.start();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }
    
}
4 吃食物
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    int length = 0;
    //蛇头坐标
    int[] snakeX = new int[800];
    int[] snakeY = new int[800];
    int foodX, foodY;
    Random random = new Random();
    String fx;
    boolean isStart = false;    //游戏是否开始
    //通过改变 delay 来控制游戏的速率大小
    Timer timer = new Timer(100, this);

    public GamePanel(){
        init();
        //获取键盘的监听事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start();
    }

    public void init() {
        //蛇初始化
        fx = "RIGHT";      //蛇头默认朝右
        length = 3;
        snakeX[0] = 100;  snakeY[0] = 100;
        snakeX[1] = 75;   snakeY[1] = 100;
        snakeX[2] = 50;   snakeY[2] = 100;

        foodX = 25 + 25 * random.nextInt(33);
        foodY = 75 + 25 * random.nextInt(23);
    }

    //面板绘制方法
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);    //清屏
        setBackground(Color.WHITE);

        //头部计分板
        GameSources.info.paintIcon(this, g, 20,5);

        //游戏区域
        g.fillRect(20,65,850,600);

        //蛇头 + 蛇身
        if (fx.equals("RIGHT"))
            GameSources.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("LEFT"))
            GameSources.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("UP"))
            GameSources.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("DOWN"))
            GameSources.down.paintIcon(this, g, snakeX[0], snakeY[0]);

        for (int i = 1; i < length; i++)    //通过 length 来控制蛇的长度
            GameSources.body.paintIcon(this, g, snakeX[i], snakeY[i]);

        //绘制食物
        GameSources.food.paintIcon(this, g, foodX, foodY);

        //是否开始游戏提示
        if (!isStart){
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格 开始/暂停 游戏",220, 350);
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        //游戏的暂停和开始
        if (keyCode == KeyEvent.VK_SPACE){
            isStart = !isStart;
            repaint();
        }

        //蛇的走向
        if (keyCode == KeyEvent.VK_RIGHT){
            fx = "RIGHT";
        }else if (keyCode == KeyEvent.VK_UP){
            fx = "UP";
        }else if (keyCode == KeyEvent.VK_LEFT){
            fx = "LEFT";
        }else if (keyCode == KeyEvent.VK_DOWN){
            fx = "DOWN";
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (isStart){
            for (int i = length - 1; i > 0; i--){
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            //头部移动
            if (fx.equals("RIGHT")){
                snakeX[0] += 25;
                if (snakeX[0] > 845){snakeX[0] = 25;}
            }else if (fx.equals("LEFT")){
                snakeX[0] -= 25;
                if (snakeX[0] < 25){snakeX[0] = 845;}
            }else if (fx.equals("UP")){
                snakeY[0] -= 25;
                if (snakeY[0] < 75) {snakeY[0] = 630;}
            }else if (fx.equals("DOWN")){
                snakeY[0] += 25;
                if (snakeY[0] > 630) {snakeY[0] = 75;}
            }

            //添加食物
            //若直接使用两坐标相等判断,会因为像素的问题导致吃不到,因此两坐标差值在一定范围内,就判定为迟到了食物
            if (Math.abs(snakeX[0] - foodX) < 15 && Math.abs(snakeY[0] - foodY) < 15){
                length++;
                //解决每次生成食物左上角 body 会闪一下
                snakeX[length - 1] = snakeX[length - 2] * 2 - snakeX[length-3];
                snakeY[length - 1] = snakeY[length - 2] * 2 - snakeY[length-3];
                //随机生成食物
                foodX = 25 + 25 * random.nextInt(33);
                foodY = 75 + 25 * random.nextInt(23);
            }

            repaint();
        }
        timer.start();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

}
5 失败判定
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    int length = 0;
    //蛇头坐标
    int[] snakeX = new int[800];
    int[] snakeY = new int[800];
    int foodX, foodY;
    int speed = 100;
    Random random = new Random();
    String fx;
    boolean isStart = false;    //游戏是否开始
    //通过改变 delay 来控制游戏的速率大小
    Timer timer = new Timer(speed, this);
    //失败判定
    boolean isFail = false;
 

    public GamePanel(){
        init();
        //获取键盘的监听事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start();
    }

    public void init() {
        //蛇初始化
        fx = "RIGHT";      //蛇头默认朝右
        length = 3;
        snakeX[0] = 100;  snakeY[0] = 100;
        snakeX[1] = 75;   snakeY[1] = 100;
        snakeX[2] = 50;   snakeY[2] = 100;

        foodX = 25 + 25 * random.nextInt(33);
        foodY = 75 + 25 * random.nextInt(23);
    }

    //面板绘制方法
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);    //清屏
        setBackground(Color.WHITE);

        //头部计分板
        GameSources.info.paintIcon(this, g, 20,5);

        //游戏区域
        g.fillRect(20,65,850,600);

        //蛇头 + 蛇身
        if (fx.equals("RIGHT"))
            GameSources.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("LEFT"))
            GameSources.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("UP"))
            GameSources.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("DOWN"))
            GameSources.down.paintIcon(this, g, snakeX[0], snakeY[0]);

        for (int i = 1; i < length; i++)    //通过 length 来控制蛇的长度
            GameSources.body.paintIcon(this, g, snakeX[i], snakeY[i]);

        //绘制食物
        GameSources.food.paintIcon(this, g, foodX, foodY);

        //是否开始游戏提示
        if (!isStart){
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格 开始/暂停 游戏",220, 350);
        }

        //失败提醒
        if (isFail){
            g.setColor(Color.RED);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("游戏失败!",365, 330);
            g.drawString("按下 空格 重新开始",280, 380);
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        //游戏的暂停和开始
        if (keyCode == KeyEvent.VK_SPACE){
            if (isFail){    //重新开始
                isFail = false;
                init();
            }else{  //游戏暂停
                isStart = !isStart;
            }
            repaint();
        }

        //蛇的走向
        if (keyCode == KeyEvent.VK_RIGHT){
            fx = "RIGHT";
        }else if (keyCode == KeyEvent.VK_UP){
            fx = "UP";
        }else if (keyCode == KeyEvent.VK_LEFT){
            fx = "LEFT";
        }else if (keyCode == KeyEvent.VK_DOWN){
            fx = "DOWN";
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (isStart && !isFail){    //游戏开始且没有失败
            for (int i = length - 1; i > 0; i--){
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            //头部移动
            if (fx.equals("RIGHT")){
                snakeX[0] += 25;
                if (snakeX[0] > 845){snakeX[0] = 25;}
            }else if (fx.equals("LEFT")){
                snakeX[0] -= 25;
                if (snakeX[0] < 25){snakeX[0] = 845;}
            }else if (fx.equals("UP")){
                snakeY[0] -= 25;
                if (snakeY[0] < 75) {snakeY[0] = 630;}
            }else if (fx.equals("DOWN")){
                snakeY[0] += 25;
                if (snakeY[0] > 630) {snakeY[0] = 75;}
            }

            //添加食物
            //若直接使用两坐标相等判断,会因为像素的问题导致吃不到,因此两坐标差值在一定范围内,就判定为迟到了食物
            if (Math.abs(snakeX[0] - foodX) < 20 && Math.abs(snakeY[0] - foodY) < 20){
                length++;
                //解决每次生成食物左上角 body 会闪一下
                snakeX[length - 1] = snakeX[length - 2] * 2 - snakeX[length-3];
                snakeY[length - 1] = snakeY[length - 2] * 2 - snakeY[length-3];
                //随机生成食物
                foodX = 25 + 25 * random.nextInt(32);
                foodY = 75 + 25 * random.nextInt(22);
            }
            //判定是否咬到自己
            for (int i = 1; i < length; i++){
                if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]){
                    isFail = true;
                }
            }
            repaint();
        }
        timer.start();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

}
6 积分系统
package com.jiuxiao.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class GamePanel extends JPanel implements KeyListener, ActionListener {

    int length = 0;
    //蛇头坐标
    int[] snakeX = new int[800];
    int[] snakeY = new int[800];
    int foodX, foodY;
    int speed = 100;
    Random random = new Random();
    String fx;
    boolean isStart = false;    //游戏是否开始
    //通过改变 delay 来控制游戏的速率大小
    Timer timer = new Timer(speed, this);
    //失败判定
    boolean isFail = false;
    //积分系统
    int score;

    public GamePanel(){
        init();
        //获取键盘的监听事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start();
    }

    public void init() {
        //蛇初始化
        fx = "RIGHT";      //蛇头默认朝右
        length = 3;
        score = 0;
        snakeX[0] = 100;  snakeY[0] = 100;
        snakeX[1] = 75;   snakeY[1] = 100;
        snakeX[2] = 50;   snakeY[2] = 100;

        foodX = 25 + 25 * random.nextInt(33);
        foodY = 75 + 25 * random.nextInt(23);
    }

    //面板绘制方法
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);    //清屏
        setBackground(Color.WHITE);

        //头部计分板
        GameSources.info.paintIcon(this, g, 20,5);

        //游戏区域
        g.fillRect(20,65,850,600);

        //蛇头 + 蛇身
        if (fx.equals("RIGHT"))
            GameSources.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("LEFT"))
            GameSources.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("UP"))
            GameSources.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        else if (fx.equals("DOWN"))
            GameSources.down.paintIcon(this, g, snakeX[0], snakeY[0]);

        for (int i = 1; i < length; i++)    //通过 length 来控制蛇的长度
            GameSources.body.paintIcon(this, g, snakeX[i], snakeY[i]);

        //绘制食物
        GameSources.food.paintIcon(this, g, foodX, foodY);

        //是否开始游戏提示
        if (!isStart){
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格 开始/暂停 游戏",220, 350);
        }



        //绘制积分系统
        g.setColor(Color.WHITE);
        g.setFont(new Font("微软雅黑", Font.BOLD, 16));
        g.drawString("总长度   :   " + length, 700, 23);
        g.drawString("总得分   :   " + score, 700, 45);

        //失败提醒
        if (isFail){
            g.setColor(Color.CYAN);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("游戏失败!",350, 290);
            g.drawString("按下空格重新开始",270, 380);
            g.setColor(Color.green);
            g.setFont(new Font("微软雅黑", Font.BOLD, 20));
            g.drawString("得分 : " + score + "分",380, 330);
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        //游戏的暂停和开始
        if (keyCode == KeyEvent.VK_SPACE){
            if (isFail){    //重新开始
                isFail = false;
                init();
            }else{  //游戏暂停
                isStart = !isStart;
            }
            repaint();
        }

        //蛇的走向
        if (keyCode == KeyEvent.VK_RIGHT){
            fx = "RIGHT";
        }else if (keyCode == KeyEvent.VK_UP){
            fx = "UP";
        }else if (keyCode == KeyEvent.VK_LEFT){
            fx = "LEFT";
        }else if (keyCode == KeyEvent.VK_DOWN){
            fx = "DOWN";
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (isStart && !isFail){    //游戏开始且没有失败
            for (int i = length - 1; i > 0; i--){
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            //头部移动
            if (fx.equals("RIGHT")){
                snakeX[0] += 25;
                if (snakeX[0] > 845){snakeX[0] = 25;}
            }else if (fx.equals("LEFT")){
                snakeX[0] -= 25;
                if (snakeX[0] < 25){snakeX[0] = 845;}
            }else if (fx.equals("UP")){
                snakeY[0] -= 25;
                if (snakeY[0] < 75) {snakeY[0] = 630;}
            }else if (fx.equals("DOWN")){
                snakeY[0] += 25;
                if (snakeY[0] > 630) {snakeY[0] = 75;}
            }

            //添加食物
            //若直接使用两坐标相等判断,会因为像素的问题导致吃不到,因此两坐标差值在一定范围内,就判定为迟到了食物
            if (Math.abs(snakeX[0] - foodX) < 20 && Math.abs(snakeY[0] - foodY) < 20){
                length++;
                score = (length - 3) * 5;
                //解决每次生成食物左上角 body 会闪一下
                snakeX[length - 1] = snakeX[length - 2] * 2 - snakeX[length-3];
                snakeY[length - 1] = snakeY[length - 2] * 2 - snakeY[length-3];
                //随机生成食物
                foodX = 40 + 25 * random.nextInt(32);
                foodY = 90 + 25 * random.nextInt(22);
            }

            //判定是否咬到自己
            for (int i = 1; i < length; i++){
                if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]){
                    isFail = true;
                }
            }
            repaint();
        }
        timer.start();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

}
7 打包发布
  • 点击右上角的 【项目结构图标】

项目结构图标

  • 依次点击【Artifacts】、【JAR】、【From moduls with ...】

Artifacts

  • 点击右上角文件夹图标,选择程序主启动类,依次点击【OK】

主启动类

Applacation

snakeOk

  • 依次点击【apply】、【OK】

ApplyOk

  • 依次点击【Build】、【Build Artifacts...】

Build

  • 再点击【Bulid】,等待项目打包结束

等项目结束

  • 打包好之后,会在项目【out】路径下生成一个 【xxx.jar】包,这就是打包好的 jar 包

OutPath

  • 验证是否打包成功,右击 jar 包,选择在文件管理器中打开,可以看到文件夹中已经有了一个 jar 包,在此打开 cmd,运行 jar 包

SnakeJar

cmdJar

项目结束

8 项目源码
posted @   悟道九霄  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示