Swing初体验 对于想学习Swing编程的朋友 我们特地为大家准备了一些小窍门 首先 下载并阅读代码是极有必要的 由于这是一篇关于Swing的教程 所以 我们只是尽可能讲解一些与Swing有关的内容 与Swing无关的内容一般不会涉及 例如算法部分 其次 受篇幅限制 也不可能在这里将每部分代码都写得完完整整的 所以 大家也需要对照完整代码来看 最后 为了使大家更容易把精力集中在Swing学习上 我们也将游戏开发中所需资源放在下载文件中 大家下载后便能够编译运行 看到执行结果 (下载游戏源文件) 顶层
容器 什么是顶层容器?当我们使用Java进行图形编程的时候 图在哪里绘制呢?我们需要一个能够提供图形绘制的容器 这个容器就被称为顶层容器 你也可以把它想象成一个窗口 顶层容器是进行图形编程的基础 一切图形化的东西 都必然包括在顶层容器中 在Swing中 我们有三种可以使用的顶层容器 它们分别是:JFrame:用来设计类似于Windows系统中的窗口形式的应用程序 JDialog:和JFrame类似 只不过JDialog是用来设计对话框 JApplet:用来设计可以在嵌入在网页中的Java小程序 如果需要使用Swing制作一个窗口类程序 我们的代码看起来应该是这样:import javax swing *public class KyodaiUIextends JFrame {……}
控件 控件是构成应用程序界面的基本元素 按钮 文本框 进度条等 这些都是控件 控件(这里我们只讨论可视化控件)又可以分为容器控件和非容器控件 从字面意义上来理解 容器控件就是能包含其他控件的特殊控件 例如 Java中的JPanel控件就属于容器型控件 我们可以在JPanel中放置按钮 文本框等非容器控件 你甚至可以在JPanel中再放置若干个JPanel控件(值得注意的是 顶层容器也是容器型控件 每一个窗口应用程序中有且只能有一个顶层容器控件 换句话说 顶层容器不能包括在其他的控件中) Java中的容器控件有很多 除刚才提到的JPanel外 还有JTabbedPane JScrollPane等 非容器控件有JButton JLabel JTextField等 如果你需要向某个容器型的控件中添加控件 你可以使用 add(Component p) 方法来实现 如:JPanel panel = new JPanel()JButton button = new JButton()panel add(button) 布局 什么是布局?布局是Java中用来控制控件排列位置的一种界面管理系统 使用过其他可视化编程开发语言的人在初次接触Java界面设计时 总会感觉到Java界面设计很别扭:居然没有提供所见即所得的设置控件坐标的方法!然而 事实证明 Java本身提供的布局管理系统也一样能够出色地完成我们的需要 而且在跨平台时表现得更有优势 常用的布局有:BorderLayout:将界面分割为上下左右以及中间一块区域的管理系统 在BorderLayout布局中 最多你只能放 个控件 如果超过 个控件 建议还是选用其他的布局系统吧 GridLayout:GridLayout是将用户界面切割为棋盘一样的布局管理系统 如果我们要设计一个类似于Windows中自带的计算器软件 GridLayout无疑是最佳选择 FlowLayout:FlowLayout与上述两类布局管理系统不太一样 在FlowLayout中 你不必指定每个控件放在哪 你只需要把控件加入到FlowLayout中 FlowLayout就会根据你添加控件的顺序依次放置控件 如果空间不够 会自动换行 在对这几个布局管理系统有了基本认识后 我们就一起来进入界面设计吧 在仔细观察了QQ游戏中 连连看 的设定后 我们可以发现 整个界面分为三个区 顶部是系统菜单区 占地面积最大的是用户游戏区 另外还有一个用户交互区 每个区域中都由若干控件组成 这么多控件 我们从哪开始入手呢?由于容器控件中可以放置其他控件 因此 我们只需要先确定放置的容器控件就可以了 既然已经知道需要使用容器控件的个数 接下来让我们就进入布局管理系统的选择 用GridLayout?似乎有点勉强 用FlowLayout?还有更好的选择吗?对了 我想你一定想到了是BorderLayout吧 如下图 所示 动手之前 大家一定要注意的是 界面的设计要先考虑好尺寸 不管是主程序界面的大小还是每个区域的大小 如果没有设计好合适的尺寸 将来改动起来会十分痛苦 下面便是相应的源程序:import java awt *import javax swing *public class KyodaiUI extends JFrame {public KyodaiUI() {this setSize( )//将窗体的大小设定为 * this setDefaultCloseOperation(JFrame EXIT_ON_CLOSE)this setResizable(false)//窗体不能改变大小this setTitle( 连连看 )//设置标题JPanel toolBar = new JPanel()toolBar setBackground(Color white)toolBar setPreferredSize(new Dimension( ))JPanel actionPanel = new JPanel()//新建JPanel型的控件actionPanel setBackground(Color yellow)//设置背景色actionPanel setPreferredSize(new Dimension( ))//设置大小JPanel contentPanel = new JPanel()contentPanel setBackground(Color blue)contentPanel setPreferredSize(new Dimension( ))this getContentPane() add(toolBar BorderLayout NORTH)this getContentPane() add(actionPanel BorderLayout EAST)this getContentPane() add(contentPanel BorderLayout CENTER)}public static void main(String[] args) throws HeadlessException {KyodaiUI kyodaiUI = new KyodaiUI()kyodaiUI show()}}让我们来看看上面这段程序是如何运行的 首先 extends JFrame表明了这是从JFrame中继承过来的 JFrame是最基本的顶层容器控件 实际上 在JDK中 以字母J打头的控件都是Swing控件 然后设置了容器的属性 其中 setDefaultCloseOperation(JFrame EXIT_ON_CLOSE)是用来告诉Java虚拟机 当用户点击窗体右上角的 关闭 按钮时 关闭该窗口进程 如果不这么做的话 你会发现虽然你可以点将窗口关闭 然而程序却没有退出 在接下来的代码中 我们为顶层容器添加了三个Panel容器 要注意的是 在AWT中 我们可以直接写为add(toolBar BorderLayout NORTH) 而在 Swing 中却一定要写成getContentPane() add(toolBar BorderLayout NORTH) 否则程序就会出错 现在大家可以放在编译运行看看 是不是和我的运行结果一样(见图 )? 边框 虽然我们使用了不同前景色来区别不同的区域 然而却没有层次感 加上边框一定会漂亮许多 在Java中 所有以J打头的Swing控件都可以使用setBorder方法来为自己设置边框 边框有很多种 线型 凸起 凹下 空的 你甚至可以自由组合形成个人风格 所有的Border都必须使用javax swing BorderFactory中提供的静态方法来创建 比如:Border border = BorderFactory createBevelBorder(BevelBorder LOWERED new Color( ) new Color( ) new Color( ) new Color( ))现在 我们将toolBar setBackground(Color white)改为toolBar setBorder(border) 立体效果是不是已经出现了?实战??写上自己的大名现在我们已经有了一个能够运行的界面了 虽然它什么也做不了 但是请你别慌 罗马不是一天建成的 现在让我们在菜单区提供一个 关于 菜单 用来显示程序的信息 难道你不想让别人知道你的大名吗?Swing本身就提供了现成的按钮控件JButton 我们只需要创建一个新的按钮:JButton about = new JButton( 关于 )这个按钮该怎么放到菜单区而不是别的地方呢?我们可以加入下面的代码:toolBar add(about)咦 怎么点按钮没有反应?这是因为你还没有告诉程序点击按钮时要做什么事情呢 要为按钮添加事件响应 首先需要使用about addActionListener(this)来告诉程序监听按钮按下时的事件 由于ActionListener是一个程序接口 因此 我们在类的申明的地方也得做一点小小的修改:public class KyodaiUI extends JFrame implements ActionListener { }实现ActionListener接口是为了告诉程序我要进行事件处理了 当然 最后我们得添加响应事件的代码:public void actionPerformed(ActionEvent e) {if (e getSource() == about) {JOptionPane showMessageDialog(this 我的大名 关于 JOptionPane INFORMATION_MESSAGE)return }}其中 e getSource() 表示当前触发事件的控件 由于我们的程序中往往会有多个以上的控件 这些控件都有可能产生事件 所以我们必须使用这个方法来找到产生事情的控件 小结 让我们一起来回顾一下今天所学的内容:首先我们了解了顶层容器 也知道了控件分为容器控件和非容器控件 同时还知道使用边框 最后 我们还小小的处理了一下按钮的事件 学而时习之 不亦说乎 就让我留点小小的作业 帮助大家巩固一下今天所学的内容:上面我们添加的按钮在菜单栏的中间 并不美观 lishixinzhi/Article/program/Java/hx/201311/25907
SwingAPI的设计目标是强大 灵活和易用 非凡地 我们希望能让程序员们方便地建立新的Swing组件 不论是从头开始还是通过扩展我们所提供的一些组件 出于这个目的 我们不要求Swing组件支持多线程访问 相反 我们向组件发送请求并在单一线程中执行请求 本文讨论线程和Swing组件 目的不仅是为了帮助你以线程安全的方式使用SwingAPI 而且解释了我们为什么会选择现在这样的线程方案 本文包括以下内容
单线程规则 Swing线程在同一时刻仅能被一个线程所访问 一般来说 这个线程是事件派发线程 规则的例外 有些 *** 作保证是线程安全的 事件分发 假如你需要从事件处理或绘制代码以外的地方访问UI 那么你可以使用SwingUtilities类的invokeLater要求在事件派发线程中执行某些代码 这个方法会立即返回 不会等待代码执行完毕 invokeAndWait行为与invokeLater类似 除了这个方法会等待代码执行完毕 一般地 你可以用invokeLater来代替这个方法 下面是一些使用这几个API的例子 请同时参阅《TheJavaTutorial》中的 BINGOexample 尤其是以下几个类 CardWindow ControlPane Player和OverallStatusPane
使用invokeLater方法你可以从任何线程调用invokeLater方法以请求事件派发线程运行特定代码 你必须把要运行的代码放到一个Runnable对象的run方法中 并将此Runnable对象设为invokeLater的参数 invokeLater方法会立即返回 不等待事件派发线程执行指定代码 这是一个使用invokeLater方法的例子
RunnabledoWorkRunnable=newRunnable }
SwingUtilities invokeLater使用invokeAndWait方法invokeAndWait方法和invokeLater方法很相似 除了invokeAndWait方法会等事件派发线程执行了指定代码才返回 在可能的情况下 你应该尽量用invokeLater来代替invokeAndWait 假如你真的要使用invokeAndWait 请确保调用invokeAndWait的线程不会在调用期间持有任何其他线程可能需要的锁
这是一个使用invokeAndWait的例子
voidshowHelloThereDialogthrowsException }SwingUtilities invokeAndWait}
类似地 假设一个线程需要对GUI的状态进行存取 比如文本域的内容 它的代码可能类似这样
voidprintTextField throwsException }SwingUtilities invokeAndWaitSystem out println}
假如你能避免使用线程 最好这样做 线程可能难于使用 并使得程序的debug更困难 一般来说 对于严格意义下的GUI工作 线程是不必要的 比如对组件属性的更新 不管怎么说 有时候线程是必要的 下列情况是使用线程的一些典型情况 执行一项费时的任务而不必将事件派发线程锁定 例子包括执行大量计算的情况 会导致大量类被装载的情况 和为网络或磁盘I/O而阻塞的情况 重复地执行一项 *** 作 通常在两次 *** 作间间隔一个预定的时间周期 要等待来自客户的消息 你可以使用两个类来帮助你实现线程 SwingWorker 创建一个后台线程来执行费时的 *** 作 Timer 创建一个线程来执行或多次执行某些代码 在两次执行间间隔用户定义的延迟 使用SwingWorker类SwingWorker类在SwingWorker java中实现 这个类并不包含在Java的任何发行版中 所以你必须单独下载它 SwingWorker类做了所有实现一个后台线程所需的肮脏工作 虽然许多程序都不需要后台线程 后台线程在执行费时的 *** 作时仍然是很有用的 它能提高程序的性能观感
SwingWorkersanexampleofusingSwingWorker 要使用SwingWorker类 你首先要实现它的一个子类 在子类中 你必须实现construct方法还包含你的长时间 *** 作 当你实例化SwingWorker的子类时 SwingWorker创建一个线程但并不启动它 你要调用你的SwingWorker对象的start方法来启动线程 然后start方法会调用你的construct方法 当你需要construct方法返回的对象时 可以调用SwingWorker类的get方法 这是一个使用SwingWorker类的例子
//在main方法中 finalSwingWorkerworker=newSwingWorker }worker start //在动作事件处理方法中 JOptionPane showMessageDialog)
当程序的main方法调用start方法 SwingWorker启动一个新的线程来实例化ExpensiveDialogComponent main方法还构造了由一个窗口和一个按钮组成的GUI 当用户点击按钮 程序将阻塞 假如必要 阻塞到ExpensiveDialogComponent创建完成 然后程序显示一个包含ExpensiveDialogComponent的模式对话框 你可以在MyApplication java找到整个程序 使用Timer类Timer类通过一个ActionListener来执行或多次执行一项 *** 作 你创建定时器的时候可以指定 *** 作执行的频率 并且你可以指定定时器的动作事件的监听者 启动定时器后 动作监听者的actionPerformed方法会被调用来执行 *** 作 定时器动作监听者定义的actionPerformed方法将在事件派发线程中调用 这意味着你不必在其中使用invokeLater方法 这是一个使用Timer类来实现动画循环的例子
publicclassAnimatorApplicationTimer extendsJFrameimplementsActionListener publicvoidstartAnimationelse } publicvoidstopAnimation publicvoidactionPerformed }
在一个线程中执行所有的用户界面代码有这样一些优点 组件开发者不必对线程编程有深入的理解 像ViewPoint和Trestle这类工具包中的所有组件都必须完全支持多线程访问 使得扩展非常困难 尤其对不精通线程编程的开发者来说 最近的一些工具包如SubArctic和IFC 都采用和Swing类似的设计 事件以可预知的次序派发 invokeLater排队的runnable对象从鼠标和键盘事件 定时器事件 绘制请求的同一个队列派发 在一些组件完全支持多线程访问的工具包中 组件的改变被变化无常的线程调度程序穿插到事件处理过程中 这使得全面测试变得困难甚至不可能 更低的代价 尝试小心锁住临界区的工具包要花费实足的时间和空间在锁的治理上 每当工具包中调用某个可能在客户代码中实现的方法时 工具包都要保存它的状态并释放所有锁 以便客户代码能在必要时获得锁 当控制权交回到工具包 工具包又必须重新抓住它的锁并恢复状态 所有应用程序都不得不负担这一代价 即使大多数应用程序并不需要对GUI的并发访问 这是的SubArcticJavaToolkit的对在工具包中支持多线程访问的问题的描述 我们的基本信条是 当设计和建造多线程应用程序 尤其是那些包括GUI组件的应用程序时 必须保证极端小心 线程的使用可能会很有欺骗性 在许多情况下 它们表现得能够极好的简化编成 使得设计 专注于单一任务的简单自治实体 成为可能 在一些情况下它们的确简化了设计和编码 然而 在几乎所有的情况下 它们都使得调试 测试和维护的困难大大增加甚至成为不可能 无论大多数程序员所受的练习 他们的经验和实践 还是我们用来帮助自己的工具 都不是能够用来对付非决定论的 例如 全面测试在bug依靠于时间时是几乎不可能的 尤其对于Java来说 一个程序要运行在许多不同类型的机器的 *** 作系统平台上 并且每个程序都必须在抢先和非抢先式调度下都能正常工作 由于这些固有的困难 我们力劝你三思是否绝对有使用线程的必要 尽管如此 有些情况下使用线程是必要的 所以subArctic提供了一个线程安全的访问机制
lishixinzhi/Article/program/Java/gj/201311/27616
评论列表(0条)