Java.GUI01

GUI简介

Java GUI的核心技术:Swing、AWT
已经不常用了:

  1. 由于界面不够美观
  2. 需要JRE环境

学习目的:

  1. 学习MVC思想
  2. 自己实现一些快捷工具
  3. 了解MVC架构,了解监听

AWT

AWT(Abstract Windows Tools),包含了很多的类和接口,提供了各种元素:窗口、按钮、文本框等,用于GUI编程。
其中最重要的组成部分:组件Component,包含button、textArea、label等控件;以及容器组件container、frame、dialog等

UML图:

Frame

Frame frame = new Frame("MyWindow");
// 设置窗口可见性
frame.setVisible(true);
// 设置窗口大小
frame.setSize(800, 500);
// 设置背景颜色
Color color = new Color(0, 0, 0);
frame.setBackground(color);
// 设置弹出初始位置
frame.setLocation(400, 200);
// 固定窗口大小
frame.setResizable(false);

关闭Frame

适配器模式:当接口中包含多个方法,且只想使用其中的某个方法时,可以通过实例化接口的抽象子类,然后重写抽象类中的方法来实现。

// 监听事件,监听窗口关闭事件
// 适配器模式:通过接口的抽象子类进行实例化
frame.addWindowListener(new WindowAdapter() {
	// 窗口关闭事件
	@Override
	public void windowClosing(WindowEvent e) {
		// 结束程序
		System.exit(0);
	}
});

Panel

// 流布局
Panel panel1 = new Panel();
panel1.setBounds(50, 50, 400, 100);
panel1.setBackground(Color.CYAN);
// 在frame中添加panel
frame.add(panel1);

布局管理器

  • 流式布局
  • 边界布局(东西南北中)
  • 表格布局

流式布局

Frame frame = new Frame();
frame.setBounds(200, 200, 400, 400);

// 设置为流式布局
// FlowLayout默认构造参数为1/CENTER,其余可选参数有0/LEFT,2/RIGHT
frame.setLayout(new FlowLayout());

// 按钮组件
Button button1 = new Button("button1");
Button button2 = new Button("button2");

frame.add(button1);
frame.add(button2);
frame.setVisible(true);         // setVisible(true)要写在添加控件之后,不然控件初始会不可见

边界布局

Frame frame = new Frame("BorderLayout");
frame.setBounds(200, 200, 300, 300);

// 设置button控件的边界布局位置
frame.add(new Button("1"), BorderLayout.EAST);
frame.add(new Button("2"), BorderLayout.WEST);
frame.add(new Button("3"), BorderLayout.SOUTH);
frame.add(new Button("4"), BorderLayout.NORTH);
frame.add(new Button("5"), BorderLayout.CENTER);

frame.setVisible(true);

边界布局效果

表格布局

Frame frame = new Frame("GridLayout");
frame.setBounds(200, 200, 300, 300);

// 设置3行2列的表格布局
frame.setLayout(new GridLayout(3, 2));

frame.add(new Button("1"));
frame.add(new Button("2"));
frame.add(new Button("3"));
frame.add(new Button("4"));
frame.add(new Button("5"));
frame.add(new Button("6"));

frame.setVisible(true);

表格布局效果:
b8uNaF.png
frame.pack()用于使frame自适应调整大小,添加frame.pack()后的效果:

事件监听

Frame对象

添加窗口监听

// 监听事件,监听窗口关闭事件
// 适配器模式:通过接口的抽象子类进行实例化
frame.addWindowListener(new WindowAdapter() {
	// 窗口关闭事件
	@Override
	public void windowClosing(WindowEvent e) {
		// 结束程序
		System.exit(0);
	}
});

Button对象

添加事件监听(通过Lambda表达式)

// 添加事件监听需要一个事件监听器作为参数,可以通过Lambda表达式实现事件监听器接口
// 事件监听器需要提供一个动作事件参数e
button.addActionListener((e)-> System.out.println("111"));

多个控件可以使用一个监听器

// 通过Lambda表达式创建动作监听器对象
ActionListener actionListener =(e) -> {
	// e.getActionCommand()获取控件的信息
	if (e.getActionCommand().equals("start")) {
		System.out.println("Start!!");
	} else {
		System.out.println("Plz click start");
	}
};

Button start = new Button("start");
Button stop = new Button("stop");
// 给两个按钮添加动作监听对象
start.addActionListener(actionListener);
stop.addActionListener(actionListener);

TextField对象

一般编程规范:main方法只负责启动程序,尽量不要在main方法中写大量代码。

单行文本框添加事件监听器,减少main方法中的代码,将代码实现放在其他类的构造器中,main方法中只用实例化类即可启动。

class MyFrame extends Frame {
    public MyFrame(){
        setLocation(600, 400);
        // 创建单行文本,设置长度为20
        TextField textField = new TextField(20);
        // setEchoChar():设置替换字符
        textField.setEchoChar('*');
        add(textField);
        pack();

        // Lambda表达式创建监听器,按下回车才会触发事件
        textField.addActionListener(e -> {
            // e.getSource用于获取绑定的对象
            TextField textField1 = (TextField) e.getSource();
            // textField.getText():获取文本框中的文本
            System.out.println(textField1.getText());
            // setText():设置文本框中的文本
            textField1.setText("");
        });

        setVisible(true);
    }
}

程序启动中会输出一个warning:java[28590:6332245] TSM AdjustCapsLockLEDForKeyTransitionHandling - _ISSetPhysicalKeyboardCapsLockLED Inhibit,这是因为输入法设置中存在其他非英语的输入法,可以忽视。
(参考自stack overflow,在遇到不懂的报错时,推荐用stack overflow查询)。

CalcDemo

尝试用TextField和Button连接监控器实现简易的计算器。

class Calculator extends Frame {
    public Calculator() {
        // 创建控件
        TextField num1 = new TextField(5);
        TextField num2 = new TextField(5);
        TextField rst = new TextField(6);
        Button calc = new Button("=");
        Button clear = new Button("Clear");
        clear.setForeground(Color.RED);
        Label plus = new Label("+");

        // 设置为流式布局,按顺序添加控件
        setLayout(new FlowLayout());
        setLocation(500, 400);
        add(num1);
        add(plus);
        add(num2);
        add(calc);
        add(rst);
        add(clear);
        pack();

        // 创建事件监听器
        ActionListener actionListener = e -> {
            int n1 = Integer.parseInt(num1.getText());
            int n2 = Integer.parseInt(num2.getText());
            rst.setText((n1 + n2) + "");
        };

        // 绑定事件监听器
        calc.addActionListener(actionListener);
        clear.addActionListener(e -> {
            num1.setText("");
            num2.setText("");
            rst.setText("");
        });
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        setVisible(true);
    }
}

界面效果:

OOP组合

OOP原则:组合优于继承。
组合是指想要使用其他类中的方法或属性时,通过实例化需要的类,来使用类中的属性和方法,而非继承。组合可以降低耦合性。
当A类想要使用B中的方法时:

  1. 继承方式:
class A extends B {}
  1. 组合方式:
class A {
	public B b;
	b.method();
	...
}

采用面向对象的思想重写CalcDemo

类中主要实现类的属性和方法,不应将代码全部写在构造器中,这是面向过程的思想,应该通过方法和属性实现。
如:以下Calculator2类提供了Textfield类型的属性和loadFrame方法。

package xyz.lilmoon.awt.listener;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

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

class Calculator2 extends Frame {
    TextField num1, num2, rst;

    public void loadFrame() {
        // 创建控件
        num1 = new TextField(5);
        num2 = new TextField(5);
        rst = new TextField(5);
        Button calc = new Button("=");
        Button clear = new Button("clear");
        clear.setForeground(Color.RED);
        Label plus = new Label("+");

        // 绑定监听器
        calc.addActionListener(new Listener(this));
        clear.addActionListener(new Clear((this)));
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        // 设为流式布局,添加控件,设置自适应大小及可见性
        setLayout(new FlowLayout());
        setLocation(500, 400);
        add(num1);
        add(plus);
        add(num2);
        add(calc);
        add(rst);
        add(clear);
        pack();
        setVisible(true);
    }
}

class Listener implements ActionListener {
    // 组合:在类中实例化需要的类,来使用类中的属性和方法
    Calculator2 calculator;

    public Listener(Calculator2 calculator) {
        this.calculator = calculator;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        int n1 = Integer.parseInt(calculator.num1.getText());
        int n2 = Integer.parseInt(calculator.num2.getText());
        calculator.rst.setText((n1 + n2) + "");
    }
}

class Clear implements ActionListener {
    // 组合:在类中实例化需要的类,来使用类中的属性和方法
    Calculator2 calculator2;

    public Clear(Calculator2 calculator2) {
        this.calculator2 = calculator2;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        calculator2.num1.setText("");
        calculator2.num2.setText("");
        calculator2.rst.setText("");
    }
}

使用内部类进一步优化

将计算和清楚类移到计算器类内部,可以直接使用外部类的属性和方法。内部类一般使用private修饰。

class Calculator3 extends Frame {
    TextField num1, num2, rst;

    public void loadFrame() {
        // 创建控件
        num1 = new TextField(5);
        num2 = new TextField(5);
        rst = new TextField(5);
        Button calc = new Button("=");
        Button clear = new Button("clear");
        clear.setForeground(Color.RED);
        Label plus = new Label("+");

        // 绑定监听器
        calc.addActionListener(new Listener());
        clear.addActionListener(new Clear());
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        // 设为流式布局,添加控件,设置自适应大小及可见性
        setLayout(new FlowLayout());
        setLocation(500, 400);
        add(num1);
        add(plus);
        add(num2);
        add(calc);
        add(rst);
        add(clear);
        pack();
        setVisible(true);
    }

    private class Listener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            int n1 = Integer.parseInt(num1.getText());
            int n2 = Integer.parseInt(num2.getText());
            rst.setText((n1 + n2) + "");
        }
    }

    private class Clear implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            num1.setText("");
            num2.setText("");
            rst.setText("");
        }
    }
}
tag(s):
show comments · back · home