javaGUI学习38:Swing-基本知识

  • 虽然Swing是AWT的扩展,但是两者的基本概念还是有许多个问乙处。
  • 首先,Swing 小应程序和应用程序的实现方式与AWT小应用程序和应用程序的实现万式有所不问。
  • 并且,如果开发人员想要开发同时使用AWT组件和Swing组件的小应用程序或应用程序,还必须注意混合使用轻量组件和重量组件所带来的许多问题。
  • Swing是线程不安全的,这就是说,在大多数情况下,只能从事件派发线程中访问Swing组件。

1、小应用程序与应用程序

  • Applet和Frame来实现小程序和应用程序可能出现事件处理问题和重新绘制问题,因此要使用JApplet和JFrame来实现Swing小应用程序和应用程序。
  • JApplet和 JFrame都是只包含一个组件的容器:JRootPane的一个实例,JRootPane包含一个称作内容窗格的容器。
  • 内容窗格包含与特定的小应用程序或应用程序有关的所有内容。这里,内容指包含在小应用程序和或应用程序中的组件
  • 这就是说小应用程序和应用程序必须把组件添加到内容窗格中而不是把它们直接添加到小应用程序或应用程序(或根窗格)中。而且,我们不应该直接为Swing小应用程序或应用程序设置布局管理器。因为组件添加到内容窗格中,所以应该为内容窗格而不是小应用程序或应用程序设置布局管理器。
  • 包含一个JRootPane实例的Swing容器重载用来添加组件和设置布局管理器的方法。这些方法会弹出提醒人们的异常信息:不能把组件直接添加到包含一个JRootPane实例的Swing容器中、也不能为该容器设置布局管理器。

2、JApplet类

  • 扩展java.applet.Applet并实现Accessibility接口,和RootPaneContainer接口
  • RootPaneContainer是一个包含根窗口的容器,被所有包含一个JRootPane实例的Swing容器所实现
  • JApplet 的实例可以有一个菜单栏,它是由setJMenuBar方法指定的。注意,Swing小应用程序能有一个菜单栏,而AWT小应用程序却不能。

3、应用程序

应用程序比小应用程序要稍微复杂些,这是因为它们不是在浏览器内部运行的,即浏览器不启动它们也不设置它们的大小。应用程序必须提供main方法,必须把一个窗体实例化,随后确定该窗体的大小并使该窗体可见。

Swing小应用程序和应用程序有许多共同点。它们都含有一个JRootPane实例,都必须把组件添加到恨窗格的内容窗格中。而且,不能显式地设置Swing小应用程序或Swing应用程序的布局管理器。

4、JFrame类

JFrame类扩展java . awt . Frame,与jJApplet类似,它也实现Accessible接口和RootPaneContainer接口。

JFrame还实现Swing. WindowsConstants接口,该接口定义缺省关闭操作的常量。

JFrange实现许多在::Applet中能找到的、相同的方法。与JApplet类似、为了不显式地设置其布局管理器或不把组件直接添加到窗体中,JFrame重载setLayout和 addImpl方法. JFrame 实现了所有在.RootPaneContainer接I口中定义的方法,还实现了允许和禁止根窗格检查的方法。JFrame还实现了确定当前是否启用了根窗格检查的方法。

使用AWT窗体时,开发人员要负责处理窗口关闭事件。通常,这需要重载事件处理方法,需要简单地隐藏窗口或隐藏窗口并清除其本地资源。而Swing通过把一个缺省关闭操作与每一个JFrame实例相关联来使窗口的关闭事件较容易处理。

可以用setDefaultCloseOperation方法来设置缺省的关闭操作,而且可以用getDefaultCloseOperation ()来获取缺省的关闭操作。可以传送给setDefaultCloseOperation ()的 integer值在WindowConstants类中定义。

WindowConstants 常数:

方法名 实现
DO_NOTHING_ON_CLOSE 关闭窗口时什么也不做
HIDE_ON_CLOSE 关闭窗口时隐藏该窗口
DISPOSE_ON_CLOSE 关闭窗口时隐藏该窗口并清除其本地资源

如果没有显式设置JFrame的缺省关闭操作,默认DO_NOTHING_ON_CLOSE

DISPOSE_ ON_ CLOSE隐藏窗体并清除与这个窗体有关的系统资源。如果该窗体是应用程序窗体,则在该窗体清除后,应用程序将继续运行。例如,应用程序把应用程序窗体的缺省关闭操作设置为DISPOSE_ ON_CLOSE,但是,应用程序仍然负责处理窗体关闭事件。到应用程序得到窗体已关闭(当调用windowClosed方法时)窗体已隐藏并清除的通知时,应用程序仍在运行;结果,应用程序在windowClosed方法中调用System.exit ()。

5、小应用程序与应用程序组合

实现思想:实现一个小应用程序,这个小应用程序包含一个main方法,这个main方法把JFrame实例化,而且还创建这个小应用程序的一个实例。在调用小应用程序的init方法后,窗体用该小应用程序的内容窗格来代替该窗体的内容窗格。这个窗体接着设置其边界和标题,并把它的可见性设置为true。

6、GJApp

该类提供了一个状态区,并能从属性文件中读取资源。

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Locale;
import java.util.ResourceBundle;

public class GJApp {
    static private JPanel statusArea = new JPanel();
    static private JLabel status = new JLabel("");
    static private ResourceBundle resources;

    static {
      resources = ResourceBundle.getBundle("GJApp", Locale.getDefault());//使用默认的语言格式,加载GJApp.properties文件
    }

    public static void launch(final JFrame f, String title, final int x, final int y, final int w, final int h){
        f.setTitle(title);
        f.setBounds(x,y,w,h);//移动并调整此组件的大小。 
        f.setVisible(true);//显示或隐藏此 Frame
        statusArea.setBorder(BorderFactory.createEtchedBorder());//设置此组件的边框。 
        statusArea.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
        statusArea.add(status);
        f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    public static JPanel getStatusArea(){
        return statusArea;
    }

    public static void showStatus(String s){
        status.setText(s);
    }

    public static String getResource(String key){
        return (resources == null) ? null : resources.getString(key);
    }
}

7、ResourceBundle和Properties的区别

一般来说,ResourceBundle类通常是用于针对不同的语言来使用的属性文件。
而如果你的应用程序中的属性文件只是一些配置,并不是针对多国语言的目的。那么使用Properties类就可以了。

1.ResourceBundle.getBundle(String arg0)中的参数arg0必须包含属性文件的完整路径。
2.将属性文件(例如config.properties)和读取文件在一起;
属性文件和读取的文件要放在一起,如果要分开这两个文件呢?那么我们自己可以自己建立一个目录,将属性文件放到其下,再将该目录设置为classloader加载的目录(加入类路径中)则可,如下:
1、在项目下建一文件夹,路径随意,名字任意(这为:properties)。
2、(Eclipse中)选择项目->Properties->java Build Path->Libraries->Add
Class Folder,将properties文件加入类路径即可(或者手动在.classpath文件中加入:<classpathentry kind="lib" path="properties"/>)。然后直接用
ResourceBundle.getBundle("config");则可读取
properties/config.properties文件的内容。
3.resourceBundle.getBundle(args0)中传入的参数为资源文件的basename.且不用加.properties文件后缀。
如资源文件名为:myres_zh_CN.properties或myres.properties则只需要传入myres就可以 了。
4.ResourceBundle这个类的作用就是读取资源属性文件(properties),然后根据.properties文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定),然后获取相应的properties文件的内容。

8、混合使用Swing和AWT

8.1 层序

组件的层序是指同一容器中组件之间显示的层次关系。
如果容器是同类的(即它包含的组件都是轻量组件或都是重量组件),则按组件被添加到容器中的顺序来确定其层序。

第一个被添加到容器中的组件有最高的层序,即它在同一容器中所有其他组件的上面显示。最后添加到容器中的组件的层序最低,即它在同一个容器中的所有其他组件的下面显示。
如果容器是异类的(即它既有轻量组件又有重量组件),则事情要稍微复杂些。轻量组件不是显示在它们自己的窗口中,而是显示在它们的重量容器的窗口中。所以,轻量组件的层序与重量容器的层序相同。如果多个轻量组件被添加到一个容器中,则这些轻量组件的层序是由组件被添加到容器中的顺序来决定的。

8.2 Swing弹出式菜单

缺省时,Swing 弹出式菜单是轻量组件。如果轻量弹出式菜单与重量组件重叠,则弹出式菜单将在该重量组件下面显示。
有些Swing 组件使用弹出式菜单。Swing 菜单组件就是一种使用弹出式菜单的组件,它在一个菜单被激活时,显示一个弹出式菜单。缺省时,如果一个与某个菜单相关联的弹出式菜单完全处在弹出式菜单所在的窗口中,则弹出式菜单使用轻量组件。

8.3 滚动

把重量组件和轻量组件混合使用时所要关心的另一个问题是滚动。
Swing提供了一个替代AWT重量滚动窗格的轻量组件——JScrollPane组件。由于JScrollPane是轻量的,所以,任何添加到JScrollPane实例中的重量组件都将在这个滚动窗格之上显示。如果重量组件滚动超出了JScrollPane实例的边框,则它就不能正确地显示了。

8.4 内部窗体

Swing 的内部窗体是包含在桌面窗格中的窗体,Swing 的内部窗体是轻量组件,如果把重量组件添加到一个内部窗体中,则这个窗体很可能会遇到麻烦。

所以我们不提倡Swing的轻量组件和AWT重量组件混合使用。

9、Swing和线程

大多数情况下Swing是线程不安全的。Swing是线程不安全的主要原因是为了简化扩展组件的任务。

Swing是线程不安全的另一个原因是由于获取和释放锁定及恢复状态所带来的开销。使用线程安全GU工具包的所有应用程序〈无论它们是否是多线程的)都必须付出同样的性能代价。

有些Swing组件方法确实支持多线程访问。例如,JComponent的 repaint、revalidate和 invali-
date等方法都对放在事件派发线程上的请求进行排队,因此,可从任何线程中调用这些万法。

另外,可以从多个线程把监听器添加到事件监听器列表中或从列表中删掉。最后,有些组件方法是同步的。例如,JCheckBoxMenultem . setState ()是同步的,因此,可以从多线程中调用它。

9.1 Swing单线程设计的结果

Swing单线程设计的主要结果是:大多数情况下,只能从事件派发线程中访问将要在屏幕上绘制的Swing组件。
事件派发线程是调用paint和update等回调方法的线程,而且,它还是事件监听器接口中定义的事件处理方法。例如,ActionListener和PropertyListener接口的实现使它们的actionPerformed 方法和propertyChange方法在事件派发线程中调用。
技术上说,在Swing组件的对等组件创建之前(指可在屏幕上绘制之前),它们可以从多个线程中访问。例如,可以在一个小应用程序的init方法中构造和操纵组件,只要在操纵它们之前,还没有使它们成为可见的。

9.2 SwingUtilities类的invokeLater和invokeAndWait方法

由于AWT和Swing都是事件驱动工具包,所以在回调方法中更新可见的CUI就是很自然的事。例如,如果在一个按钮激活,项目列表需要更新时,则通常在与该按钮相关联的事件监听器的actionPerformed方法中来实现该列表的更新。
然而,有时可能需要从事件派发线程以外的线程中更新Swing组件。例如,如果上述项目列表中包含了很多来自数据库或Internet的数据,则可能在按钮激活后还要等一段时间才能看到更新的列表。如果信息的获取是在 actionPerfomed中实现的,则按钮仍保持按下的状态,直到对actionPerformed 的调用返回,不仅按钮的弹起需要一段时间,而且一般来说,耗时较长的操作也不应当在事件处理方法中执行,因为在事件处理方法返回之前,其他的事件不能派发。有时,在独立的线程上执行耗时的操作可能更好,这将允许立即更新用户界面和释放事件派发线程去派发其他的事件。幸运的是,Swing提供了两种机制,它们都支持这种想法
SwingUtilities类提供了两个方法:invokeLater和 invokeAndWait,它们都使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。

上一篇:javaGUI 简单实现切换面板背景颜色


下一篇:javaGUI学习26:AWT-文本构件