图形化用户界面

Swing基础

大多数Swing应用都被构建在基础的JFrame内部,JFrame在你使用的任何操作系统中都可以创建视窗应用。

视窗的标题可以像下面这样使用JFrame的构造器来设置:

import javax.swing.*;

public class HelloSwing{
	public static void main(String[] args){
		JFrame frame = new JFrame("Hello Swing");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(300,100);   //以像素为单位设置视窗的尺寸
		frame.setVisible(true);   //设置视窗是否可见
	}
}

setDefaultCloseOperation()告诉JFrame当用户执行关闭操作时应该做些什么。EXIT_ON_CLOSE常量告诉它要退出程序。如果没有这个调用,默认的行为是什么也不做,因此应用不会关闭,默认情况下,该值被设置为HIDE_ON_CLOSE,关闭时只是隐藏窗体。

在JFrame中添加一个JLabel:

import javax.swing.*;
import java.util.concurrent.*;

public class HelloLabel {
  public static void main(String[] args) throws Exception {
    JFrame frame = new JFrame("Hello Swing");
    JLabel label = new JLabel("A Label");
    frame.add(label);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(300, 100);
    frame.setVisible(true);
    TimeUnit.SECONDS.sleep(1);
    label.setText("Hey! This is Different!");
  }
}

在一秒钟之后,JLabel的文本发生了变化。 对于main()线程来说,直接对GUI组件编写代码并非是一种好的想法。Swing有它自己的专用线程来接受UI事件并更新屏幕,如果你从其他线程着手对屏幕进行操作,那么就可能会产生冲突和死锁。 其他线程,例如这里是想main()这样的线程,应该通过Swing事件分发线程提交要执行的任务。你可以通过将任务提交给SwingUtilities.invokeLater()来实现这种方式。这个方法会通过事件分发线程将任务放置到(最终将得到执行的)待执行事件队列中。例如:

import javax.swing.*;
import java.util.concurrent.*;

public class SubmitLabelManipulationTask {
	public static void main(String[] args) throws Exception{
		JFrame frame = new JFrame("Hello Swing");
		final JLabel label=new JLabel("A Label");
		frame.add(label);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(300,100);   
		frame.setVisible(true);  
		TimeUnit.SECONDS.sleep(1);
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
				label.setText("Hey!This is Different!");
			}
		});
	}
}

现在不用直接操作JLabel了。取而代之的是,提交一个Runnable,当事件分发线程在事件队列中获取这项任务时,它将执行实际的操作,并且在执行这个Runnable时,不会做其他任何事情,因此也就不会产生任何冲突,当然,前提是程序中的所有代码都遵循这种通过SwingUtilities.invokeLater()来提交操作的方式。这包括启动程序自身,即main()也不应该调用Swing的方法,就像上面的程序一样,它应该向事件队列提交任务。

一个显示框架

import javax.swing.*;

public class SwingConsole {
	public static void run(final JFrame f,final int width,final int height){
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
				f.setTitle(f.getClass().getSimpleName());
				f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				f.setSize(width, height);
				f.setVisible(true);
			}
		});
	}
}

要想使用它,应用必须位于一个JFrame中。静态的run()方法可以将视窗的标题设置为类的简单名。

创建按钮

一般来说,要在类中为按钮创建一个字段,以便以后可以引用这个按钮。 JButton是一个组件,它有自己的小窗口,能作为整个更新过程的一部分而自动被重绘。也就是说,不必显式绘制一个按钮或者别的类型的控件;只要把它们放在窗体上,它们可以自动绘制自己。通常会在构造器内部把按钮加入窗体:

import javax.swing.*;
import java.awt.*;
import static 一个显示框架.SwingConsole.*;

public class Button1 extends JFrame {
	private JButton
		b1=new JButton("Button 1"),
		b2=new JButton("Button 2");
	public Button1(){
		setLayout(new FlowLayout());
		add(b1);
		add(b2);
	}
	public static void main(String[] args){
		run(new Button1(),200,100);
	}
}

新内容:再向JFrame添加任何组件之前,先给出一个新的FlowLayout类型的“布局管理器”。布局管理器是面板用来隐式地决定控件在窗体上的位置的工具。JFrame通常使用BorderLayout管理布局,但这里不能使用,因为它的默认行为是每加入一个控件,将完全覆盖其他控件。FlowLayout使得控件可以在窗体上从左到右、从上到下连续均匀分布。

捕获事件

事件驱动编程(包含了许多关于GUI的内容)的基础,就是把事件同处理时间的代码连接起来。 在Swing中,这种关联的方式就是通过清楚地分离接口(图形组件)和实现(当和组件相关的时间发生时,你要执行的代码)而做到的。每个Swing组件都能够报告其上所有可能发生的事件,并且它能单独报告每种事件。 首先,对所使用的组件,我们只把重点放在它感兴趣的主要事件上。对于JButton,“感兴趣的事件”就是按钮被按下。可以调用JButton的addActionListener()方法。这个方法接受一个实现ActionListener接口的对象作为参数,ActionListener接口只包含一个actonPerformed()方法。所以要想把事件处理代码和JButton关联,需要在一个类中实现ActionListener接口,然后把这个类的对象通过addActionListener()方法注册给JButton。这样按钮按下的时候就会调用actionPerformed()方法(通常这也称为回调)。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static 一个显示框架.SwingConsole.*;

public class Button2 extends JFrame{
	private JButton
		b1= new JButton("Button 1"),
		b2= new JButton("Button 2");
	private JTextField txt= new JTextField(10);
	class ButtonListener implements ActionListener{
		public void actionPerformed(ActionEvent e){
			String name=((JButton)e.getSource()).getText();  //强制转换
			txt.setText(name);
		}
	}
	private ButtonListener bl=new ButtonListener();
	public Button2(){
		b1.addActionListener(bl);
		b2.addActionListener(bl);
		setLayout(new FlowLayout());
		add(b1);
		add(b2);
		add(txt);
	}
	public static void main(String[] args){
		run(new Button2(),200,150);
	}
}

actionPerformed()方法的参数是ActionEvent类型,它包含事件和事件源的所有信息。getSource()方法产生的对象表明了事件的来源。getText()方法返回按钮上的文本,这个文本被放进JTextField。 通常,把ActionListener实现成匿名内部类会更方便,尤其是对每个监听器类只使用一个实例时候更是如此:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static 一个显示框架.SwingConsole.*;

public class Button2b extends JFrame {
	private JButton
		b1=new JButton("Button 1"),
		b2=new JButton("Button 2");
	private JTextField txt=new JTextField(10);
	private ActionListener bl=new ActionListener(){
		public void actionPerformed(ActionEvent e){
			String name=((JButton)e.getSource()).getText();
			txt.setText(name);
		}
	};
	public Button2b(){
		b1.addActionListener(bl);
		b2.addActionListener(bl);
		setLayout(new FlowLayout());
		add(b1);
		add(b2);
		add(txt);
	}
	public static void main(String[] args){
		run(new Button2b(),200,150);
	}
}

文本区域

《Java编程思想(第4版)》

Last updated