Java基础之《GUI编程》

GUI编程 --读“古以”编程

AWT
  AWT(Abstract Window Toolkit)包括了很多类和接口,用于Java Application
  的GUI(Graphics User Interface图形用户界面)编程
  GUI的各元素(如:窗口,按钮,文本框等)由Java类来实现
  使用AWT所涉及的类一般在java.awt包及其子包中

  Container和Component是AWT中的两个核心类
Java基础之《GUI编程》_第1张图片
  所有的可以显示出来的图形元素都叫Component
  Component中有一类比较特殊的叫做Container,它是用来容纳其它Component的元素

  Container里面又分两种,一种叫Window,一种叫Panel
  Window是可以独立显示的
  Window本身又分两种,一种叫Frame(一般的窗口),一种叫Dialog(对话框)
  Dialog又分两种,一种叫模态的对话框,一种叫非模态的对话框
  Panel一般看不见它,它也可以容纳其它的元素,把Panel装在Window里面再显示出来

组件和容器
  Component & Container

  Java的图形用户界面的最基本组成部分是Component,Component类及其子类的对象
  用来描述以图形化的方式显示在屏幕上并能与用户进行交互的GUI元素,例如,一个按钮,
  一个标签等

  一般的Component对象不能独立的显示出来,必须将“放在”某一个Container对象中才可以
  显示出来

  Container是Component子类,Container子类对象可以“容纳”别的Component对象
  Container对象可使用方法add(...)向其中添加其他Component对象
  Container是Component子类,因此Container对象也可以被当作Component对象添加
  到其他Container对象中

  有两种常有的Container:
  Window:其对象表示自由停泊的顶级窗口
  Panel:其对象可作为容纳其他Component对象,但不能独立存在,必须被添加到其他
  Container中(如Window 或 Applet)

  Window - Frame
  Frame是Window的子类,由Frame或其子类创建的对象为一个窗体
  Frame的常用构造方法:
    Frame()
    Frame(String s)创建标题栏为字符串s的窗口

    setBounds(int x, int y, int width, int height)
    //设置窗体位置和大小,x、y是左上角坐标,width和height是宽度和高度

    setSize(int width, int height)
    //设置窗体的大小,width和height分别是宽度和高度

    setLocation(int x, int y)
    //设置窗体的位置,x、y是左上角坐标

    setBackground(Color c)
    //设置背景颜色,参数为Color对象  java.awt包中的Color类定义了各种颜色

    setVisible(boolean b)
    //设置是否可见

    setTitle(String name)
    //设置标题栏

    String getTitle()
    //获得标题栏

    setResizable(boolean b)
    //设置是否可以调整大小

    Frame类有个子类叫JFrame,它是在swing包里

/*

* TestFrame.java

* Frame组件的创建及显示设置
*/
import java.awt.*;
public class TestFrame {
	public static void main(String args[]) {
		Frame f = new Frame("My First Test");
		f.setSize(170, 100);
		f.setBackground(Color.blue);
		f.setVisible(true);  //Frame new出来是内存里的一个对象,必须setVisible传入true才能显示出来
	}
}
/*

* TestMultiFrame.java

* 显示多个窗口

*/

import java.awt.*;

public class TestMultiFrame {
	public static void main(String args[]) {
		MyFrame f1 = 
			new MyFrame(100,100,200,200,Color.BLUE);
		MyFrame f2 = 
			new MyFrame(300,100,200,200,Color.YELLOW);
		MyFrame f3 = 
			new MyFrame(100,300,200,200,Color.GREEN);
		MyFrame f4 = 
			new MyFrame(300,300,200,200,Color.MAGENTA);
	}
}

class MyFrame extends Frame {
	static int id = 0;
	MyFrame(int x, int y, int w, int h, Color color) {
		super("MyFrame " + (++id));
		setBackground(color);
		setLayout(null);  //把窗口内部自己的布局管理器设为空
		setBounds(x, y, w, h);
		setVisible(true);
	}
}

  Window - Panel
    Panel对象可以看成是可以容纳Component的空间
    Panel对象可以拥有自己的布局管理器
    Panel类拥有从其父类继承来的:
      setBounds(int x, int y, int width, int height)
      setSize(int width, int height)
      setLocation(int x, int y)
      setBackground(Color c)
      setLayout(LayoutManager mgr)等方法

    Panel的构造方法为:
      Panel() 使用默认的FlowLayout类布局管理器初始化

      Panel(LayoutManager layout) 使用指定的布局管理器初始化

/*

* TestPanel.java

*/

import java.awt.*;

public class TestPanel {
	public static void main(String args[]) {
		Frame f = new Frame("Java Frame with Panel");
		Panel p = new Panel(null);
		f.setLayout(null);
		f.setBounds(300, 300,500, 500);  //Frame设置Bounds相对于屏幕
		f.setBackground(new Color(0, 0, 102));
		p.setBounds(50, 50, 400, 400);  //Panel设置Bounds相对于它装到谁里边,就相对于谁
		p.setBackground(new Color(204, 204, 255));
		f.add(p);
		f.setVisible(true);
	}
}
/*
* TestMultiPanel.java
*/
import java.awt.*;

public class TestMultiPanel {
	public static void main(String args[]) {
		new MyFrame2("My Frame with Panel", 300, 300, 400, 400);
	}
}

class MyFrame2 extends Frame {
	private Panel p1,p2,p3,p4;
	MyFrame2(String s, int x, int y, int w, int h) {
		super(s);
		setLayout(null);
		p1 = new Panel(null);  //传空表明这几个Panel不在自己的布局管理器里
		p2 = new Panel(null);
		p3 = new Panel(null);
		p4 = new Panel(null);
		
		p1.setBounds(0, 0, w/2, h/2);
		p2.setBounds(0, h/2, w/2, h/2);
		p3.setBounds(w/2, 0, w/2, h/2);
		p4.setBounds(w/2, h/2, w/2, h/2);
		
		p1.setBackground(Color.BLUE);
		p2.setBackground(Color.GREEN);
		p3.setBackground(Color.YELLOW);
		p4.setBackground(Color.MAGENTA);
		
		add(p1);
		add(p2);
		add(p3);
		add(p4);
		
		setBounds(x, y, w, h);
		setVisible(true);
	}
}

布局管理器

  Java语言中提供了布局管理器类的对象可以管理:
    1)管理Component在Container中的布局,不必直接设置Component位置和大小
    2)每个Container都有一个布局管理器对象,当容器需要对某个组件进行定位
    或判断其大小尺寸时,就会调用其对应的布局管理器,调用Container的setLayout
    方法改变其布局管理器对象
  AWT提供了5种布局管理器类:
    FlowLayout
    BorderLayout
    GridLayout
    CardLayout
    GridBagLayout
  布局管理器是布局排列的大管家,布局管理器各有各的风格

  封装了布局管理器的类是LayoutManager,AWT提供给我们的布局管理器都是实现了
  LayoutManager接口的类,因此我们可以用Container的setLayout方法来设置哪一种
  布局管理器在起作用

  FlowLayout布局管理器
    FlowLayout是Panel类的默认布局管理器
    FlowLayout布局管理器对组件逐行定位,行内从左到右,一行排满后换行
    不改变组件的大小,按组件原有尺寸显示组件,可设置不同的组件间距,行距以及对齐方式

  FlowLayout布局管理器默认的对齐方式是居中
Java基础之《GUI编程》_第2张图片
    FlowLayout的构造方法
    new FlowLayout(FlowLayout.RIGHT, 20, 40);
    右对齐,组件之间水平间距20个像素,垂直间距40个像素

    new FlowLayout(FlowLayout.LEFT);
    左对齐,水平和垂直间距为缺省值(5)

    new FlowLayout();
    使用缺省的居中对齐方式,水平和垂直间距为缺省值(5)

  BorderLayout布局管理器
  BorderLayout是Frame类的默认布局管理器
  BorderLayout将整个容器和布局划分成
    东(EAST)
    西(WEST)
    南(SOUTH)
    北(NORTH)
    中(CENTER)五个区域,组件只能被添加到指定的区域
  如不指定组件的加入部位,则默认加入到CENTER区
  每个区域只能加入一个组件,如加入多个,则先前加入的会被覆盖

  BorderLayout型布局管理器尺寸缩放原则:
    北、南两个区域在水平方向缩放
    东、西两个区域在垂直方向缩放
    中部可在两个方向上缩放
Java基础之《GUI编程》_第3张图片


  GridLayout布局管理器
  GridLayout型布局管理器将空间划分成规则的矩形网络,每个单元格
  区域大小相等。组件被添加到每个单元格中,先从左到右添满一行后
  换行,再从上到下

  在GridLayout构造方法中指定分隔的行数和列数
    如:GridLayout(3, 4)
Java基础之《GUI编程》_第4张图片


  布局管理器总结
  1)Frame是一个顶级窗口,Frame的缺省布局管理器为BorderLayout
  2)Panel无法单独显示,必须添加到某个容器中
    Panel的缺省布局管理器为FlowLayout
  3)当把Panel作为一个组件添加到某个容器中后,该Panel仍然可以有
  自己的布局管理器
  4)使用布局管理器时,布局管理器负责各个组件的大小和位置,因此
  用户无法在这种情况下设置组件大小和位置属性,如果试图使用Java
  语言提供的setLocation(),setSize(),setBounds()等方法,则都会
  被布局管理器覆盖
  5)如果用户确实需要亲自设置组件大小或位置,则应取消该容器的布局
  管理器,方法为:
    setLayout(null);

事件处理
  事件监听
Java基础之《GUI编程》_第5张图片
  事件源对象:发出这件事的是哪个Component,是哪个元素?TextFild、Button啊等
  事件:事件本身也是对象。到底发生的是什么事。事件和监听这个事件的接口一一对应
  当某件事发生的时候:事件源对象会调用实现了监听器接口的对象的方法
  注册:把自己的监听器的对象注册到事件源对象中

  当有一个按钮,我们想要知道它什么时候被按下去了,可以主动的监听,
  是一件很昂贵的一件事,显然不好。实际上我们可以监听按钮被按下去这件
  事有没有发生,发生了就可以执行后面的操作

  这种执行操作,叫钩子函数,也叫回调函数

  原理是:你实现某一个特定的接口,而Button里边也照着这个接口调用实际当中
  的对象,Button把你这个类的对象当成某一个固定的接口来调用,你肯定要实现
  某一个固定的接口的某一个特定的方法
  Button调用的是接口里的方法,根据多态的原理,实际上你new的是谁调用的就是
  谁的方法

  当我们的Button被按下去时,发出的事叫做ActionEvent(动作事件)

/*
* TestActionEvent.java
* 1. Java事件处理机制
* 2. 事件源、事件监听器概念及作用
* 3. 如何在一个现有组件上注册监听器
*/

import java.awt.*;
import java.awt.event.*;

public class TestActionEvent {
	public static void main(String args[]) {
		Frame f = new Frame("Test");
		Button b = new Button("Press Me!");
		Monitor bh = new Monitor();
		b.addActionListener(bh);
		f.add(b, BorderLayout.CENTER);
		f.pack();
		f.setVisible(true);
	}
}

class Monitor implements ActionListener {
	public void actionPerformed(ActionEvent e) {  //ActionEvent对象包装了这件事发生的具体信息
		System.out.println("a button has been pressed");
	}
}

  这个事件模型,它实现了Observer设计模式(观察者模式)。一般的伴随事件模型

  还有Command模式

  TextField类
    java.awt.TextField类用来创建文本框对象。就是一个输入框。
    TextField有如下常用方法:
      TextField()
      TextField(int columns)
      TextField(String text)
      TextField(String text, int columns)

      public void setText(String t)
      public String getText()
      public void setEchoChar(char c)  //设置回显字符
      public void setEditable(boolean b)
      public boolean isEditable()
      public void setBackground(Color c)
      public void select(int selectionStart, int selectionEnd)
      public void selectAll()
      public void addActionListener(ActionListener l)  //添加动作监听器

      tf.setEchoChar("*");  //和密码框差不多,文本框回显的都是*号

  TextField事件监听
    1)TextField对象可能发生Action(光标在文本框内敲击回车)事件。与该事件对应
    的事件类是java.awt.event.ActionEvent
    2)用来处理ActionEvent事件是实现了java.awt.event.ActionListener接口的
    类的对象。ActionListener接口定义有方法:
      public void actionPerformed(ActionEvent e)
    3)实现该接口的类要在该方法中添加处理该事件(Action)的语句
    4)使用addActionListener(ActionListener l)方法为TextField对象注册一个
    ActionListener对象,当TextField对象发生Action事件时,会生成一个ActionEvent
    对象,该对象作为参数传递给ActionListener对象的actionPerformerd方法在方法中
    可以获取该对象的信息,并做相应的处理

/*
* TFTestActionEvent.java
*/

import java.awt.*;
import java.awt.event.*;

public class TFTestActionEvent {
	public static void main(String args[]) {
		new TFFrame();
	}
}

class TFFrame extends Frame {
	TFFrame() {
		TextField tf = new TextField();
		add(tf);
		tf.addActionListener(new TFActionListener());
		pack();
		setVisible(true);
	}
}

class TFActionListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
		TextField tf = (TextField)e.getSource();  //getSource拿到事件源,返回值是事件发生在哪个对象上
		System.out.println(tf.getText());
		tf.setText("");
	}
}

  持有对方的引用

    这种模式叫做Facade模式,门面模式。有一个大管家管着所有的事,其他人要互相打交道
    都要通过这个大管家。对内叫调停者模式,Mediator模式

/*
* TFMath.java
* 持有对方的引用
*/

import java.awt.*;
import java.awt.event.*;

public class TFMath {
	public static void main(String args[]) {
		new TFFrame().launchFrame();
	}
}

class TFFrame extends Frame {
	TextField num1, num2, num3;  //设置成成员变量,Button才能拿到它的内容
	public void launchFrame() {
		num1 = new TextField(10);
		num2 = new TextField(10);
		num3 = new TextField(15);
		
		Label lbPlus = new Label("+");
		Button btnEqual = new Button("=");
		btnEqual.addActionListener(new MyMonitor(this));
		
		setLayout(new FlowLayout());
		add(num1);
		add(lbPlus);
		add(num2);
		add(btnEqual);
		add(num3);
		pack();
		setVisible(true);
	}
}

class MyMonitor implements ActionListener {
	//在监听器对象里边持有整个Frame的引用
	//不需要记住Frame内部装了什么,不用再装一遍
	TFFrame tf = null;
	
	public MyMonitor(TFFrame tf) {
		this.tf = tf;
	}
	
	public void actionPerformed(ActionEvent e) {
		int n1 = Integer.parseInt(tf.num1.getText());
		int n2 = Integer.parseInt(tf.num2.getText());
		tf.num3.setText("" + (n1+n2));
	}
}

  内部类

    内部类可以非常方便的访问包装类的成员变量或成员方法
    内部类编译出来的class文件:TFFrame2$MyMonitor.class

    好处:
    可以方便的访问包装类的成员
    可以更清楚的组织逻辑,防止不应该被其他类访问的类进行访问

    何时使用:
    该类不允许或不需要其他类进行访问时,可以把内部类设成private class XXX

/*
* TFMath2.java
* 内部类
*/

import java.awt.*;
import java.awt.event.*;

public class TFMath2 {
	public static void main(String args[]) {
		new TFFrame2().launchFrame();
	}
}

class TFFrame2 extends Frame {
	TextField num1, num2, num3;  //设置成成员变量,Button才能拿到它的内容
	public void launchFrame() {
		num1 = new TextField(10);
		num2 = new TextField(10);
		num3 = new TextField(15);
		
		Label lbPlus = new Label("+");
		Button btnEqual = new Button("=");
		btnEqual.addActionListener(new MyMonitor());
		
		setLayout(new FlowLayout());
		add(num1);
		add(lbPlus);
		add(num2);
		add(btnEqual);
		add(num3);
		pack();
		setVisible(true);
	}
	
	//内部类可以畅通无阻的访问包装类的成员变量或成员方法
	//外部类对象默认的就拥有内部类对象的引用
	class MyMonitor implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int n1 = Integer.parseInt(num1.getText());
			int n2 = Integer.parseInt(num2.getText());
			num3.setText("" + (n1+n2));
		}
	}
}

Java图形

  Graphics类
    每个Component都有一个paint(Graphics g)用于实现绘图目的,每次重画
    该Component是都自动调用paint方法

    Graphics类中提供了许多绘图方法,如:
      drawRect(int x, int y, int width, int height)
      fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)等

/*
* TestPaint.java
*/

import java.awt.*;

public class TestPaint {
	public static void main(String args[]) {
		new PaintFrame().launchFrame();
	}
}

class PaintFrame extends Frame {
	public void launchFrame() {
		setBounds(200, 200, 640, 480);
		setVisible(true);
	}
	
	public void paint(Graphics g) {  //重写paint方法
		Color c = g.getColor();
		g.setColor(Color.red);
		g.fillOval(50, 50, 30, 30);
		g.setColor(Color.GREEN);
		g.fillRect(80, 80, 40, 40);
		g.setColor(c);  //把原来的颜色设回去
	}
}


    画直线:在Graphics类中有public abstract void drawLine方法,它是一个抽象方法。为什么
    抽象方法它可以直接调用?这里面有一个多态的存在,传到paint方法里面的是Graphics类的子类
    调用的是子类的drawLine方法

  鼠标事件适配器
    抽象类java.awt.event.MouseAdapter实现了MouseListener接口,可以使用
    其子类作为MouseEvent的监听器,只要重写其相应的方法即可

    对于其他的监听器,也有对应的适配器

    使用适配器可以避免监听器类定义没有必要的空方法

    MyMouseAdapter.java鼠标适配器

/*
* MyMouseAdapter.java
*/

import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class MyMouseAdapter {
	public static void main(String args[]) {
		new MyFrame("drawing...");
	}
}

class MyFrame extends Frame {
	ArrayList points = null;
	MyFrame(String s) {
		super(s);
		points = new ArrayList();
		setLayout(null);
		setBounds(300, 300, 400, 300);
		this.setBackground(new Color(204, 204, 255));
		setVisible(true);
		this.addMouseListener(new Monitor());
	}
	
	public void paint (Graphics g) {
		Iterator i = points.iterator();
		while (i.hasNext()) {  //把这个ArrayList里所有点都拿出来
			Point p = (Point)i.next();
			g.setColor(Color.BLUE);
			g.fillOval(p.x, p.y, 10, 10);
		}
	}
	
	public void addPoint(Point p) {
		points.add(p);
	}
}

//addMouseListener参数是MouseListener接口
//MouseAdapter实现了MouseListener接口
//如果Monitor直接实现MouseListener接口,要把5个接口都定义一边
class Monitor extends MouseAdapter {
	public void mousePressed(MouseEvent e) {
		MyFrame f = (MyFrame)e.getSource();
		f.addPoint(new Point(e.getX(), e.getY()));
		f.repaint();  //让Frame强制重画,这个方法内部调用paint方法
	}
}


    repaint - update() - paint();
      图形中的双缓冲,repaint调用update方法,update方法调用了paint方法
      大多数图形系统都会有双缓冲

Window事件

  Window事件所对应的事件类为WindowEvent,所对应的事件监听接口为WindowListener
  WindowListener定义的方法有:
    public void windowOpened(WindowEvent e)
    public void windowClosing(WindowEvent e) //窗口正在被关闭
    public void windowClosed(WindowEvent e) //窗口已经关了
    public void windowIconified(WindowEvent e) //让窗口最小化
    public void windowDeiconified(WindowEvent e) //让窗口恢复正常
    public void windowActivated(WindowEvent e)
    public void windowDeactivated(WindowEvent e)

  与WindowListener对应的适配器为WindowAdapter

  当一个类和除了某个方法之外都没有关系,可以用匿名内部类

/*
* TestWindowClose.java
*/

import java.awt.*;
import java.awt.event.*;

public class TestWindowClose {
	public static void main(String args[]) {
		new MyFrame55("MyFrame");
	}
}

class MyFrame55 extends Frame {
	MyFrame55 (String s) {
		super(s);
		setLayout(null);
		setBounds(300, 300, 400, 300);
		this.setBackground(new Color(204, 204, 255));
		setVisible(true);
		this.addWindowListener(
			new WindowAdapter() {  //使用匿名类
				public void windowClosing(WindowEvent e) {
				setVisible(false);
				System.exit(-1);
				}
			}
		);
	}
}


键盘事件

  键盘上的按键对应一个虚拟的码

/*
* TestKey.java
*/

import java.awt.*;
import java.awt.event.*;

public class TestKey {
	public static void main(String args[]) {
		new KeyFrame().launchFrame();
	}
}

class KeyFrame extends Frame {
	public void launchFrame() {
		setSize(200, 200);
		setLocation(300, 300);
		addKeyListener(new MyKeyMonitor());
		setVisible(true);
	}
	
	class MyKeyMonitor extends KeyAdapter {
		public void keyPressed(KeyEvent e) {
			//System.out.println("ok");
			int keyCode = e.getKeyCode();
			if (keyCode == KeyEvent.VK_UP) {
				System.out.println("up");
			}
		}
	}
}


小结

  一个图
  布局管理器
  事件模型
    ActionEvent
    MouseEvent
    WindowEvent
    KeyEvent
  graphics类和paint方法
    双缓冲 repaint - update - paint
  内部类、匿名类

你可能感兴趣的:(JAVA基础)