AWT 和 Swing

GUI基本类在java.awt包中,这个包也称为抽象窗口工具包(Abstract Window Toolkit,AWT)。

java.awt包中有一个抽象类Component,它是除菜单类外所有类的父类,他有如下所有组件都有的方法

  • getBackground(): 返回背景色
  • getGraphics(): 返回组件用的画笔
  • getHeight(): 返回组件高度
  • getLocation(): 获得组件位置
  • getSize(): 获得组件大小
  • getWidth(): 获得组件宽度
  • getX(): 获得组件x值
  • getY():
  • isVisible(): 组件是否可见
  • setBackGround(): 设置背景色
  • setBackGrounds(int x, int y, int width, int height): 设置位置及大小
  • setEnabled(boolean b): 设置组件是否可用
  • setFont(Font f): 设置字体
  • setForground(Color c): 设置前景色
  • setLocation(int x, int y): 设置组件位置
  • setSize(int width, int height): 设置组件宽高
  • setSize(Dimension d): 设置组件大小
  • setVisible(boolean b): 设置组件是否可见

Container表示容器,继承了Component类。容器用来存放别的组件。有两种容器

  • Window(窗口): 他有两个子类,Frame(窗体)和Dialog(对话框)。Frame带有标题,并且可以自动调整大小,Dialog可以被移动,但不能改变大小
  • Panel(面板): 他不能单独存在,只能存在于其他容器(如Window或其子类)。他有一个子类Applet,Applet可以在web中运行。

为了使java创建的图形界面能够跨平台,引入了Swing组件。这些组件位于javax.swing包中。接下来主要讲swing。

多数Swing的父类是JComponent(除了JFrame和JDialog外其他都继承了这个)。

JComponent的一些方法

  • setFont(Font font): 设置字体
  • setForeground(Color c): 设置字体颜色
  • setToolTipText(String text): 设置鼠标移动到上面的时候显示的文本
  • setBackGround(Color c)
  • setOpaque(boolean isOpaque): 设置组件是否不透明,JLabel默认是透明。不透明才可以显示背景。
  • setLocationRelativeTo(Component c): 设置窗口相对于指定组件的位置。当为null时,设置为中间。中点可以使用 GraphicsEnvironment.getCenterPoint 确定。
  • isFocusOwner(): 判断这个组件是否拥有焦点

JFrame一些方法:

  • setIconImage(Image image)
  • setResizable(boolean resizable):窗口是否可以放大缩小
  • setLocation(int x, int y): 位置
  • setBounds(int x, int y, int width, int height): 设置大小和宽高
  • Point getLocationOnScreen(): 获得位置
  • dispose(): 销毁窗口。如果再次使用setVisiable()会重构组件

创建界面基本步骤

JFrame有一个构造方法JFrame(String title)。创建一个title位标题的JFrame对象,但是创建后它是不可见的,必须要经过下列步骤才可以可见:

  • 调用setSize(int width, int height): 设置JFrame大小,或者调用peck()自动确定大小。
  • 调用setVisible(true): 使JFrame可见
public static void main(String[] args)
{
JFrame frame = new JFrame("Hello");
jframe.setSize(200, 100);
jframe.setVisible(true);
}

每个JFrame都会有内容面板(contentpane),加入JFrame容器的组件实际上都是加入这个面板中

Container content = jframe.getContentPane();
content.add(jButton);//加入按钮组件

add()方法直接向与之关联的内容面板加入组件,所以可以直接jframe.add(jButton).

JFrame的setContentPane(Container content)用来重新设置面板。

JFrame的setDefaultCloseOperation(int operation)用来设置如何相应关闭操作
operation可以有以下操作

  • JFrame.DO_NOTHING_ON_CLOSE: 什么也不做
  • JFrame.HIDE_ON_CLOSE: 隐藏窗体,这是默认选项
  • JFrame.DISPOSE_ON_CLOSE: 销毁窗体
  • JFrame.EXIT_ON_CLOSE: 结束程序

布局管理器

JFrame的默认布局管理器是BorderLayout,JPanel默认布局管理器是FlowLayout。

可以通过setLayout(Layout layout)设置布局。也可以通过setLayout(null)取消布局管理器,但是接下来要自己去管理布局,并且现在每个操作系统不同。

FlowLayout 流式布局管理器

他按照组件先后顺序从左到右放到容器中,到达边界时,会放置到下一行。

当容器被缩放时,组件位置可能变化,但是组件大小不会改变

构造方法:

  • FlowLayout()
  • FlowLayout(int align)
  • FlowLayout(int align, int hgap, int vgap):align设置对齐方式,可以有FlowLayout.LEFT,FlowLayout.RIGHT, FlowLayout.CENTER. hgap和vgap设置组件间水平和垂直间隙

BorderLayout(边界布局管理器)

他把容器分成五个区域:东西南北中。

  • 它的东和西组件保持最佳宽度,高度拉伸至和所在区域一样高。南北保持最佳高度,宽度和这个区域一样宽。中间和这个区域一样大。
  • 窗口垂直拉伸时,东西中拉伸,当窗口垂直拉伸时,南北中拉伸。
  • 窗口缩放时,组件相对位置不变,但是组件大小改变
  • 如果某个区域组件添加不止一个,只有最后一个是可见的。

构造方法:

  • BorderLayout()
  • BorderLayout(int hgap, int vgap)

添加组件: void add(Component comp, Object constraints)。其中constraints是区域,可以是BorderLayout.NORTH,BorderLayout.SOUTH,BorderLayout.EAST,BorderLayout.WEST,BorderLayout.CENTER. 默认是中

还可以直接用字符串设置区域。但是首字母必须大写,其他必须小写。f.add(new JButton("b1"), "North")

GridLayout(网格布局管理器)

他把容器分成许多行和列,添加组件时首先防止到左上角网格中,然后从左到右放置其他组件。

  • 组件相对位置不会随缩放而改变,但是组件大小会改变。
  • 他总是忽略组件最佳大小,所有组件宽度相同,高度也相同

构造方法:

  • GridLayout()
  • GridLayout(int rows, int cols)
  • GirdLayout(int rows, int cols, int hgap, int vgap)
public class Calculater extends JFrame
{
private JPanel panel;
private JLabel label;
private String[] name = {"7","8","9","+","4","5","6","-","1","2"
,"3","*","0",".","=","/"};
private JButton[] buttons = new JButton[16];
public Calculater(String title)
{
super(title);
}
label = new JLabel(" ");
panel = new panel();
panel.setLayout(new GridLayout(4, 4));
add(label, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);

for(int i=0; i<buttons.length; i++)
{
buttons[i] = new JButton(name[i]);
panel.add(buttons[i]);
}
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible();
public static void main(String[] args)
{
new Calculater("Calculater");
}
}

这是一个计算器的图形界面,可以看到外面采用了BorderLayout,里面面板采用了GridLayout。

CardLayout

他将界面看成一系列的卡片,任何时候只有一张卡片可见。

构造方法:

  • CardLayout()
  • CardLayout(int hgap, int vgap)

添加组件 void add(Component comp, Object constraints)。其中constraints实际上是一个字符串,表示卡片的名字。默认显示第一张卡片,可以采用show(Container parent, String name)指定显示哪一张卡片。parent指定容器

GridBagLayout(网格包 布局管理器)

创建步骤:

  • 创建GridBagLayout,并启用
    GridBagLayout layout = new GridBagLayout();
    container.setLayout(layout);
  • 创建一个GridBagConstraints对象GridBagConstraints constraints = new GridBagConstraints();
  • 配置a的各种属性
    constraints.gridx = 1;
    constraints.gridy = 1;
    constraints.gridwidth = 1;
    constraints.gridwidth = 1;
  • 设置布局信息lyout.setConstraints(component1, constraints)
  • 加入容器container.add(component1)

其中GridBagConstraints只需要设置一个,然后重复设置里面属性即可。

这种布局器最为自由,可以完全自己配置,但是首先要画一个草图先规划好各个组件的坐标。

GridBagConstraints的属性:

  • gridx和gridy: 左上角横纵坐标。最左边是(0,0)。默认值是RELATIVE,即最新添加元素后面
  • gridwidth,gridheight: 宽和高。默认值是1
  • fill: 当某组件显示区域大于他所要求大小时使用。可以设置如下取值
    • GridBagConstraints.NONE: 默认,不改变组件大小
    • GridBagConstraints.HORIZONTAL: 填充水平方向,不改变水平大小
    • GridBagConstraints.VERTICAL: 填充垂直方向
    • GridBagConstraints.BOTH: 水平垂直都扩大以适应大小
  • ipadx,ipady: 指定内部填充大小,即最小尺寸下还要加多少。宽度和高度都至少要加ipadx/ipady
  • insets: 指定外部填充大小。组件和区域边界之间最小区域大小,例如a.insets = new Insets(1, 1, 1, 1);//上左下右边距
  • anchor: 当组件小于区域的时候使用。决定了在区域中的位置,有GridBagContraints.CENTER,等九个区域(包括NORTHEAST等角上区域)
  • weightx,weighty: 水平重量和垂直重量。他们决定拉伸时谁将占据空白区域。注意每一行或每一列至少要有一个占据重量,不然拉伸时组件大小将不会发生改变,从而导致周围空白。

事件处理

每一个可以触发的事件都是事件源,每一种事件都会有相应的监听器,监听器负责接受和处理这些事件。一个事件源可以触发多种事件,事件源可以使按钮,键盘,鼠标等,他们可以产生时间如按键,按按钮,点击然后触发事件处理。

事件处理的实现

每个具体的事件都是某个事件类的实例,事件类有: ActionEvent,ItemEvent,MouseEvent,KeyEvent,FocusEvent,WindowEvent等。每个事件类对应一个事件监听接口,如ActionEvent对应ActionListener。

如果程序需要处理某种事件,就需要实现这些接口。

用内部类实现接口

public class ButtonCounter extends JFrame
{
private JButton button = new JFrame("1");
public ButtonCounter(String title)
{
super(title);
//注册监视器
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent evt)
{
int count = Integer.parseInt(button.getText());
button.setText(new Integer(++count).toString());
}
});
add(button);
setSize(100, 100);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

使用容器类实现接口

可以用容器类实现多个监听接口。

public class FrmaeCounter extends JFrame implements ActionListener
{
private JButton button = new JButton("1");
public FrameCounter(String title)
{
super(title);
//注册监听器
button.addActionListener(this);
add(button);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSzie(100, 100);
setVisible(true);
}

public void actionPerformed(ActionEvent evt)//实现接口
{
int count = Integer.parseInt(button.getText());
button.setText(new Integer(++count).toString());
}
}

它可以让按钮中的数字按一次就加一

定义顶层类实现接口

创建一个专门用来处理事件的类,优点是可以使处理时间的代码和创建GUI的代码分离,缺点是监听类无法直接访问事件源,必须通过事件类的getSource()方法获得事件源。

pubilc class OuterCounter extends JFrame
{
private JButton button = new JButton("1");
public OuterCounter(String title)
{
super(title);
button.addActionListener(new MyListener(2));//创建监听器
...
}
}

public class MyListener implements ActionListener
{
private int step;
public MyListener(int step)
{
this.step = step;
}
public void actionPerformed(ActionEvent evt)
{
JButton button = (JButton)evt.getSource();//获得事件源
int count = Integer.parseInt(button.getText());
...
}
}

当有动作(按按钮)产生时,会产生ActionEvent让Mylistener处理,如果是在一个类里,我们可以直接使用这个事件源(按钮),但是如果是其他类,就需要ActionEvent的getSource()来获得事件源(谁产生的设个事件)。

事件适配器

如果实现监听街口,就必须实现里面所有的方法。MouseListener有五个方法:mousePressed(),mouseReleased(),mouseEntered(),mouseExited(),
mouseClicked();在实际应用中,往往用不到这么多方法,因此可以使用适配器,适配器中实现了所有方法,但是都是空。例如MouseListener适配器类就是MouseAdapter

事件源,时间和监听器之间的关系

事件和监听接口

事件 监听接口 抽象方法
Action ActionListener actioPerformed(ActionEvent evt)
ItemEvent ItemListener itemStateChanged(ItemEvent)
MouseEvent MouseMotionListener mouseDargged(MouseEvent)/mouseMoved(MouseEvent)
MouseEvent MouseListener mousePressed()/mouseReleased()/mouseEntered()/ mouseExited()/mouseClicked()
KeyEvent KeyListener keyPressed()/keyReleased()/keyTyped()
FocusEvent FocusListener focusGained()/foucusLost()
AdjustmentEvent AdjustmentListener adjustmentValueChanged()
ComponentEvent ComponentListneer componentMoved()/ componentResized()/componentShown()
WindowEvent WinodwListener windowClosing()/windowOpened()/ windowIconified()/windowDeiconfied()/ windowclosed() /windowActivated()/windowDeativated()
ContainerEvent ContainerListener componentAdded()/componentRemoved()
Textevent TextListener textValueChanged()

MouseListener和MouseMotionListener都是监听MouseEvent。MouseMotionListener监听和鼠标移动相关事件。他们有以下方法

  • mouseMoved(): 用户未按下鼠标直接移动时调用此方法
  • mouseDragged(): 按下鼠标并拖动
  • mouseClicked(): 单击鼠标
  • mosuePressed(): 按下鼠标
  • mouseReleased(): 释放鼠标
  • mouseExited(): 退出组件区域
  • mouseEntered(): 进入组件区域

组件及监听接口

组件可以通过addxxxlistener方法注册监听器(监听器就是上面第二列)。

适配器

监听接口 监听器
ComponentListener ComponentAdapter
FocusListener FocusAdapter
KeyListener KeyAdapter
MouseListener MouseAdapter
MouseMotionListener MouseMotionAdapter
ContainerListener ContainerAdapter
WindowListener
ItemListener 没有
ActionListener 没有
InputMethodListener 没有

基本上有多个抽象方法的都有适配器

AWT绘图

在component中有两个和绘图有关方法:

  • paint(Graphics g): 绘制组件外观
  • repaint(): 调用paint(),刷新组件的外观(对于awt组件来说调用repaint前还要调用update方法)

在下列情况中,会调用paint:

  • 第一次显示在屏幕上时
  • 组件大小发生变化时
  • 调用repaint

JComponent覆盖了paint,并将会话任务委托给三个protected的方法:

  • paintComponent(): 画当前组件,
  • paintBorder(): 画边界
  • paintChildren(): 如果这个组件时容器,则画容器包含的组件

JComponent会议组件背景色覆盖整个区域,所以如果画了图形想清除的话只需要调用super.paintComponent().

例:

public class ColorChange extends JPanel
{
private Color color = Color.RED;
private int times;
public ColorChange()
{
JButton button = new JButton("change color");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
color = (color==Color.RED) ? Color.GREEN : Color.RED;
repaint();//刷新
}
});
add(button);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);//清空画板
g.setColor(color);
g.fillReet(0, 0, 300, 300);//画矩形
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Hello");
frame.setContentPane(new ColorChanger());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
}
}

上面就重写了paintComponent方法,然后repaint()调用的就是重写后的方法。

Graphics

Graphics代表画笔,提供了各种绘制图形的方法:

  • drawLine(int x1, int y1, int x2, int y2): 画一条直线
  • drawString(String string, int left, int bottom): 写字符串
  • drawImage(Image image, int left, int top, ImageObserver observer):画一个图片
  • drawRect(int left, int top, int width, int height): 画一个矩形
  • drawOval(int x, int y, int width, int height):画一个椭圆
  • fillRect(int left, int top, int width, int height): 填充矩形
  • fillOval(int x, int y, int width, int height): 填充一个椭圆

其中left,top是左上角坐标。

如果没有调用Graphics的setColor()方法,将以前景色填充

使用完之后要用dispose()进行销毁

drawString(String str, int x, int y)将用当前画笔颜色和字体,将str显示.字符的左下角是(x,y)

Graphics2D

方法:

  • setStroke(Stroke s); 获得画笔的特性
  • draw(Shape shape): 滑参数指定的图形
  • fill(Shape shape): 填充参数指定的图形
  • translate(int x, int y): 平移
  • rotate(double theta, double originX, double originY):旋转
  • scale(double sx, double sy): 缩放

一般使用BasicStroke类。构造方法是BasicStroke(float width, int cap, int join, float miterlimit, float[] dash, float_phase)

  • width:笔画宽度,此宽度必须大于或等于0.0f。如果将宽度设置为0.0f,则将笔画设置为当前设备的默认宽度
  • cap:线端点的装饰
  • join:应用在路径线段交汇处的装饰
  • miterlimit:斜接处的裁剪限制。该参数值必须大于或等于1.0f
  • dash:表示虚线模式的数组
  • dash phase:开始虚线模式的偏移量

他还有一些绘制图形的类

  • java.awt.geom.Line2D: 画直线
  • java.awt.geom.Ellipse2D: 画椭圆
  • java.awt.geom.Rectangle2D: 画矩形

上面这些画图形的都是抽象类,他们都有两个子类:Double和Float。例如LineD.Double。

上面这些实现类的构造方法和前面说的draw大致相似。

还可以利用java.awt.Toolkit的getScreenSize()来获得屏幕大小,从而设定JFrame大小。

线程安全

虚拟机中会创建一个专门的awt线程处理窗口,但是需要我们专门添加,添加代码SwingUtilities.invokeLater(Runnable doRun)

例如:

public static void main(String[] args) {
// 此处处于 主线程,提交任务到 事件调度线程 创建窗口
SwingUtilities.invokeLater(
new Runnable() {
@Override
public void run() {
// 此处处于 事件调度线程
createGUI();
}
}
);
}

上面的线程只是用来创建窗口,绘图等不耗时的工作。详细可看