精华内容
下载资源
问答
  • 文章从系统模型和心理模型需要很好匹配的角度出发,通过引用大量软件产品设计的例子,分析了为什么有些产品难以使用,并详细分析了该原则在编程语言的发展过程中的体现
  • 但是按钮按下的时候应该有什么结果呢 我们希望看到屏幕有所改变 所以在这里介绍一个新的Swing组件 JTextField 这个组件支持用户输入文本 在本例中 或者像本例一样由程序插入文本 尽管有很多方法可以创建JTextField ...

    设计中要遵循的一条基本原则是 让简单的事情变得容易 让困难的事情变得可行

    applet
    它是一种可以在Internet上传递 并在Web浏览器中运行的程序(出于安全性 只能在所谓的沙盒内运行)

    Swing基础
    大多数Swing应用都被构建在基础的JFrame内部 JFrame在你使用的任何操作系统中都可以创建视窗应用 视窗的标题可以像下面这样使用JFrame的构造器来设置
    在这里插入图片描述
    请注意最后一行
    在这里插入图片描述
    如果没有这行 你在屏幕上将什么也看不到
    我们可以通过在JFrame中添加一个JLable来使事情变得更有趣一些
    在这里插入图片描述
    在一秒钟之后 JLable的文本发生了变化 尽管这对于这个小程序来说既有趣又安全 但是对于main()线程来说 直接对GUI组件编写代码并非是一种好的想法 Swing有它自己的专用线程来接收UI事件并更新屏幕 如果你从其他线程着手对屏幕进行操作 那么就可能会产生冲突和死锁
    取而代之的是 其他线程 例如这里是像main()这样的线程 应该通过Swing事件分发线程提交要执行的任务 你可以通过将任务提交给SwingUtilities.invokeLater()来实现这种方式 这个方法会通过事件分发线程将任务放置到(最终将得到执行的)待执行事件队列中 如果我们将这种方式应用于上面的示例 那么它就会变成下面的样子
    在这里插入图片描述
    在这里插入图片描述
    现在你再也不用直接操作JLable了 取而代之的是 你提交一个Runnable 当事件分发线程在事件队列中获取这项任务时 它将执行实际的操作 并且在执行这个Runnable时 不会做其他任何事情 因此也就不会产生任何冲突 当然 前提是程序中的所有代码都遵循这种通过SwingUtilities.invokeLater()来提交操作的方式 这包括启动程序自身 即main()也不应该调用Swing的方法 就像上面的程序一样 它应该向事件队列提交任务 因此 所编写的恰当的程序看起来应该是下面的样子
    在这里插入图片描述

    一个显示框架
    我们可以创建一个显示框架 将其用于本章剩余部分的Swing示例中 从而使得上面的想法得以结合 并减少了冗余代码
    在这里插入图片描述

    创建按钮
    创建一个按钮非常简单 只要用你希望出现在按钮上的标签调用JButton的构造器即可 在后面你会看到一些更有趣的功能 比如在按钮上显示图形
    一般来说 要在类中为按钮创建一个字段 以便以后可以引用这个按钮
    JButton是一个组件 它有自己的小窗口 能作为整个更新过程的一部分而自动被重绘 也就是说 你不必显式绘制一个按钮或者别的类型的控件 只要把它们放在窗体上 它们可以自动绘制自己 通常你会在构造器内部把按钮加入窗体
    在这里插入图片描述

    捕获事件
    如果编译并运行前面的程序 那么当按下按钮的时候 什么也不会发生 事件驱动编程(包含了许多关于GUI的内容)的基础 就是把事件同处理事件的代码连接起来
    在Swing中 这种关联的方式就是通过清楚地分离接口(图形组件)和实现(当和组件相关的事件发生时 你要执行的代码)而做到的 每个Swing组件都能够报告其上所有可能发生的事件 并且它能单独报告每种事件 所以 你要是对诸如 鼠标移动到按钮上 这样的事件不感兴趣的话 那么你不注册这样的事件就可以了 这种处理事件驱动编程的方式非常直接和优雅 一旦你理解了其基本概念 就能够很容易将其应用到甚至从未见过的Swing组件之上 实际上 只要是JavaBean 这个模式都适用
    首先 对所使用的组件 我们只把重点放在它感兴趣的主要事件上 对于JButton 感兴趣的事件 就是按钮被按下 为了表明(注册)你对按钮按下事件感兴趣 可以调用JButton的addActionListener()方法 这个方法接受一个实现ActionListener接口的对象作为参数 ActionListener接口只包含一个actionPerformed()方法 所以要想把事件处理代码和JButton关联 需要在一个类中实现ActionListener接口 然后把这个类的对象通过addActionListener()方法注册给JButton 这样按钮按下的时候就会调用actionPerformed()方法(通常这也称为回调)
    但是按钮按下的时候应该有什么结果呢 我们希望看到屏幕有所改变 所以在这里介绍一个新的Swing组件 JTextField 这个组件支持用户输入文本 在本例中 或者像本例一样由程序插入文本 尽管有很多方法可以创建JTextField 但是最简单的方式就是告诉构造器你所希望的文本域宽度 一旦JTextField被放置到窗体上 就可以使用setText()方法来修改它的内容 下面就是其具体程序
    在这里插入图片描述
    在这里插入图片描述

    通常 把ActionListener实现成匿名内部类会更方便 尤其是对每个监听器类只使用一个实例的时候更是如此 可以像下面这样修改Button2.java 这里使用一个匿名内部类
    在这里插入图片描述

    文本区域
    除了可以有多行文本以及更多的功能不同之外 JTextArea与JTextField在其他方面都很相似 JTextArea有一个比较常用的方法是append() 因为可以往回滚动 所以比起在命令行程序中把文本打印到标准输出的做法 这就成为了一种进步 例如 下面的程序使用前几节中的Countries生成器的输出来填充JTextArea
    在这里插入图片描述

    控制布局

    BorderLayout
    除非你设置为其他的布局模式 否则JFrame将使用BorderLayout作为默认的布局模式 如果不加入其他指令 它将接受你调用add()方法而加入的组件 把它放置在中央 然后把组件向各个方向拉伸 直到与边框对齐
    BorderLayout具有四个边框区域和一个中央区域的概念 当向由BorderLayout管理的面板加入组件的时候 可以使用重载的add()方法 它的第一个参数接受一个常量值 这个值可以为以下任何一个
    在这里插入图片描述
    如果没有为组件指定放置的位置 默认情况下它将被放置到中央
    在下面的示例中使用了默认布局 因为默认情况下JFrame使用的就是BorderLayout
    在这里插入图片描述

    FlowLayout
    它直接将组件从左到右 流动 到窗体上 直到占满上方的空间 然后向下移动一行 继续流动
    在下面的例子中 先把布局管理器设置为FlowLayout 然后在窗体上放置按钮 你将注意到 在使用FlowLayout的情况下 组件将呈现出 合适 的大小 比如 一个JButton的大小就是其标签的大小
    在这里插入图片描述

    GridLayout
    GridLayout允许你构建一个放置组件的表格 在向表格里面添加组件的时候 它们将按照从左到右 从上到下的顺序加入 在构造器中要指定需要的行数和列数 它们将均匀分布在窗体上
    在这里插入图片描述

    GridBagLayout
    GridBagLayout提供了强大的控制功能 包括精确判断视窗区域如何布局 以及视窗大小变化的时候如何重新放置组件 不过 它也是最复杂的布局管理器 所以很难理解 它的目的主要是辅助GUI构造工具(它可能使用GridBagLayout而不是绝对位置来控制布局)自动生成代码 如果你发现自己的设计非常复杂 以至于需要使用GridBagLayout 那么你应该使用GUI构造工具来生成这个设计
    作为一种可替换的选择 你可能会考虑TableLayout 它不属于Swing类库 但是可以从http://java.sun.com处下载 这个组件被置于GridBagLayout之上 并且隐藏了其大多数细节 因此可以极大地简化使用这种模式的方式

    绝对定位
    我们也可以设置图形组件的绝对位置

    1. 使用setLayout(null)方法把容器的布局管理器设置为空
    2. 为每个组件调用setBounds()或者reshape()方法(取决于语言的版本) 为方法传递以像素坐标为单位的边界矩形的参数 根据你要达到的目的 可以在构造器或者paint()方法中调用这些方法

    BoxLayout
    由于人们在理解和使用GridBagLayout的时候遇到了很多问题 所以Swing还提供了BoxLayout 它具有GridBagLayout的许多好处 却不像GridBagLayout那么复杂 所以当你需要手工编写布局代码的时候 可以考虑使用它 BoxLayout使你可以在水平方向或者垂直方向控制组件的位置 并且通过所谓的 支架和胶水(struts and glue)的机制来控制组件的间隔

    最好的方式是什么
    Swing功能强大 用少数几行代码就可以做很多事情 通过组合简单布局 就能得到非常多的结果 不过 在某些情况下 手工编写GUI窗体就不太适合了 这样做太复杂 也不能充分利用编程时间 Java和Swing设计者的最初目的就是要使语言和库能对GUI构造工具提供支持 创建这些工具的明确的目的也是为了使你更容易地获取编程经验 只要理解了布局的方式以及如何处理事件 那么如何手工放置组件的细节就显得不那么重要了 应该让合适的工具帮你去做这些事情(毕竟 设计Java的目的是为了提高程序员的生产率)

    Swing事件模型

    事件与监听器的类型
    所有Swing组件都具有addXXXListener()和removeXXXListener()方法 这样就可以为每个组件添加或移除相应类型的监听器 注意 每个方法的 XXX 还表示方法所能接收的参数 比如addMyListener(MyListenerm) 下表包含相互关联的基本事件 监听器以及通过提供addXXXListener()和removeXXXListener()方法来支持这些事件的基本组件 记住 事件模型是可以扩展的 所以将来你也许会遇到表格里没有列出的事件和监听器
    在这里插入图片描述
    在这里插入图片描述
    可以观察到 每种组件所支持的事件类型都是固定的 为每个组件列出其支持的所有事件是相当困难的 一个比较简单的方法是修改前几节的ShowMethods.java程序 这样它就可以显示出你所输入的任意Swing组件所支持的所有事件监听器

    下面是ShowMethods.java的更好用的GUI版本 专门用来查找Swing组件里的addListener方法
    在这里插入图片描述
    在这里插入图片描述

    这个程序为查询Swing组件所支持的事件类型提供了一种便利方式 一旦知道了某个组件支持哪些事件 不用参考任何资料就可以处理这个事件了 你只要

    1. 获取事件类的名称 并移除单词 Event 然后将剩下的部分加上单词 Listener 得到的就是内部类必须实现的监听器接口
    2. 实现上面的接口 为要捕获的事件编写出方法 比如 你可能要查找鼠标移动 所以你可以为MouseMotionListener接口的mouseMoved()方法编写代码
    3. 为第二步编写的监听器类创建一个对象 然后通过调用方法向组件注册这个对象 方法名为 add 前缀加上监听器名称 比如addMouseMotionListener()
      下面是一些监听器接口
      在这里插入图片描述

    使用监听器适配器来进行简化
    在上面的表中可以发现 某些监听器接口只有一个方法 这种接口实现起来很简单 不过 具有多个方法的监听器接口使用起来却不太方便 比如 如果你想捕获一个鼠标单击事件(例如 某个按钮还没有替你捕获该事件) 那么就需要为mouseClicked()方法编写代码 但是因为MouseListener是一个接口 所以尽管接口里的其他方法对你来说没有任何用处 但是你还是必须要实现所有这些方法 这非常烦人
    要解决这个问题 某些(不是所有的)含有多个方法的监听器接口提供了相应的适配器(可以在上面的表中看到具体的名称) 适配器为接口里的每个方法都提供了默认的空实现 现在你要做的就是从适配器继承 然后仅覆盖那些需要修改的方法 比如 你要用的典型的MouseListener像这样
    在这里插入图片描述
    适配器的出发点就是为了使编写监听器类变得更容易 不过 适配器也有某种形式的缺陷 假设你写了一个与前面类似的MouseAdapter
    在这里插入图片描述
    这个适配器将不起作用 而且要想找出问题的根源也非常困难 这足以让你发疯 因为除了鼠标单击的时候方法没有被调用以外 程序的编译和运行都十分良好 你能发现这个问题吗 它出在方法的名称上 这里的名称是MouseClicked()而没有写成mouseClicked() 这个简单的大小写错误导致加入了一个新方法 它不是关闭视窗的时候所应该调用的方法 所以无法得到所希望的结果 尽管使用接口有些不方便 但可以保证方法被正确实现
    要想保证实际上的确是覆盖了某个方法 一种改进的方法是在这段代码的上面使用内建的@Override注解

    跟踪多个事件
    作为一个有趣的试验 也为了证明这些事件确实可以被触发 编写一个程序 使其能够跟踪JButton除了 是否被按下 事件以外的行为 将会显得很有价值 这个例子还演示如何从JButton中继承出自己的按钮对象
    在下面的代码中 MyButton是TrackEvent类的内部类 所以MyButton能访问父窗口 并操作其文本区域 这正是能够把状态信息写到父窗体的文本区域内所必需的 当然 这是一个受限的解决方案 因为MyButton被局限于只能与TrackEvent一起使用 这种情况有时称为 高耦合 代码
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    Swing组件一览

    按钮
    Swing提供了许多类型的按钮 所有的按钮 包括复选框 单选按钮 甚至菜单项 都是从AbstractButton继承而来 很快你就会看到菜单项的使用 下面的例子演示了几种按钮
    在这里插入图片描述

    按钮组
    要想让单选按钮表现出某种 排它 行为 必须把它们加入到一个 按钮组(ButtonGroup) 中 不过 正如下面的例子所演示的 任何AbstractButton对象都可以加入到按钮组中
    为了避免重复编写大量的代码 下面这个例子使用了反射功能来产生几组不同类型的按钮 注意makeBPanel()方法 它用来创建一个按钮组和一个JPanel 此方法的第二个参数是一个字符串数组 针对其中每个字符串 将创建一个由第一参数所代表的按钮实例然后将此按钮加入到JPanel中
    在这里插入图片描述
    在这里插入图片描述

    图标
    可以在JLable或者任何从AbstractButton(包括JButton JCheckBox JRadioButton以及几种不同JMenuItem)继承的组件中使用Icon 和JLable一起使用Icon的做法非常直接 下面的例子还研究了与按钮(或者从按钮继承的组件)搭配使用图标的所有方式
    可以使用任何想用的GIF文件 要打开一个文件并且得到图形 只需创建一个ImageIcon对象并把文件名传递给它即可 然后 就能在程序中使用得到的图标了
    在这里插入图片描述
    在这里插入图片描述

    工具提示
    前面的例子给按钮添加了一个 工具提示 用来创建用户接口的类 绝大多数都是从JComponet派生而来的 它们包含了一个setToolTipText(String)方法 所以 对于要放置在窗体上的组件 基本上所要做的就是(对于任何JComponet派生类的对象jc)像这样编写
    在这里插入图片描述
    当鼠标停留在这个JComponet上经过一段预先指定的时间之后 在鼠标旁边弹出的小方框里就会出现你所设定的文字

    文本域
    下面的例子演示了JTextField组件具有的其他功能
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    边框
    JComponet有一个setBorder()方法 它允许你为任何可视组件设置各种边框 下面的例子使用showBorder()方法演示了一些可用的边框 此方法先创建一个JPanel 然后设置相应的边框 此外 它还使用RTTI(运行时类型识别)来得到正在使用的边框名称(去掉了路径信息) 然后把这个名称放进面板中间的一个JLable中
    在这里插入图片描述
    在这里插入图片描述

    一个迷你编辑器
    JTextPane控件可以毫不费事地支持许多编辑操作 下面的例子是对这个组件的简单应用 其中忽略了该组件所能提供的其他的大量功能
    在这里插入图片描述

    复选框
    复选框提供了一种做出 选中 或 不选 单一选择的方式 它包含了一个小方框和一个标签 这个方框中通常是有一个 x 标记(或者其他能表明 选中 的标记)或者为空 这取决于复选框是否被选中
    通常会使用接受标签作为参数的构造器来创建JCheckBox 可以获取和设置状态 也可以获取和设置其标签 甚至可以在JCheckBox对象已经建立之后改变标签
    当JCheckBox被选中或清理选中时 将发生一个事件 你可以用与对付按钮相同的方式来捕获这个事件 使用ActionListener 在下面的例子中 将枚举所有被选中的复选框 然后在JTextArea里显示
    在这里插入图片描述

    单选按钮
    GUI编程中单选按钮的概念来源于电子按钮发明之前汽车上收音机使用的机械按钮 当按下其中的一个 其他被按下的按钮将被弹出 所以 单选按钮强制你在多个选项中只能选择一个
    要设置一组关联的JRadioButton 你需要把它们加入到一个ButtonGroup中(窗体上可以有任意数目的ButtonGroup) 可以选择将其中的一个按钮设置为选中(true)(在构造器的第二个参数中设置) 如果你把多个单选按钮的状态都设置为选中 那么只有最后设置的那个有效
    下面是使用了单选按钮的简单例子它展示了使用ActionListener来捕获事件
    在这里插入图片描述

    组合框
    与一组单选按钮的功能类似 组合框(下拉列表)也是强制用户从一组可能的元素中只选择一个 不过 这种方法更加紧凑 而且在不会使用户感到迷惑的前提下 改变下拉列表中的内容更容易(当然也可以动态改变单选按钮 不过这么做显然易造成冲突)
    默认状态下 JComboBox组合框与Windows操作系统下的组合框并不完全相同 后者允许从列表中选择 或者自己输入 要想得到这样的行为 必须调用setEditable()方法 使用JComboBox组合框 你能且只能从列表中选择一个元素 在下面的例子中 JComboBox组合框开始时已经有了一些元素 然后当一个按钮按下的时候 将向组合框中加入新的元素
    在这里插入图片描述

    列表框
    列表框和JComboBox组合框明显不同 这不仅仅体现在外观上 当激活JComboBox组合框时 会出现下拉列表 而JList总是在屏幕上占据固定行数的空间 大小也不会改变 如果要得到列表框中被选中的项目 只需调用getSelectedValues() 它可以产生一个字符串数组 里面是被选中的项目名称
    JList组件允许多重选择 要是按住Ctrl键 连续在多个项目上单击 那么原先被选中的项目仍旧保持选中状态 也就是说可以选中任意多的项目 如果选中了某个项目 按住 Shift 键并单击另一个项目 那么这两个项目之间的所有项目都将被选中 要从选中的项目组中去掉一个 可以按住Ctrl键在此项目上单击
    在这里插入图片描述
    在这里插入图片描述

    页签面板
    JTabbedPane允许你创建 页签式的对话框 这种对话框中沿着窗体的一边有类似文件夹的页签 当你在页签上点击时 就会向前进入到另一个不同的对话框中
    在这里插入图片描述

    消息框
    视窗环境下通常包含了一组标准的消息框 使得能够快速地把消息通知给用户 或者是从用户那里得到消息 在Swing中 这些消息框包含在JOptionPane组件里 你有许多选择(有些非常高级) 但最常用的可能就是消息对话框和确认对话框 它们分别可以通过调用静态的JOptionPane.showMessageDialog()和JOptionPane.showConfirmDialog()方法得到 下面的例子演示了JOptionPane中一些可用的消息框
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    菜单
    每个能够持有菜单的组件 包括JApplet JFrame JDialog以及它们的子类 它们都有一个setJMenuBar()方法 它接受一个JMenuBar对象(某个特定组件只能持有一个JMenuBar对象)作为参数 你先把JMenu对象添加到JMenuBar中 然后把JMenuItem添加到JMenu中 每个JMenuItem都能有一个相关联的ActionListener 用来捕获菜单项被选中时所触发的事件
    在Java和Swing中 必须在源代码中构造所有的菜单 下面是个非常简单的菜单例子
    在这里插入图片描述

    下面是一个更复杂的创建菜单的例子 这里仍然是冰激凌口味的例子 这个例子还演示了层叠式菜单 键盘快捷键 JCheckBoxMenuItem 以及动态改变菜单的方法
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    弹出式菜单
    要实现一个JPopupMenu 最直接的方法就是创建一个继承自MouseAdapter的内部类 然后对每个希望具有弹出式行为的组件 都添加一个该内部类的对象
    在这里插入图片描述
    在这里插入图片描述

    绘图
    如果使用好的GUI框架 绘图应该非常简单 Swing库正是如此 对于任何绘图程序 问题在于决定绘图位置的计算通常比对绘图功能的调用要复杂得多 并且这些计算程序常常与绘图程序混在一起 所以看起来程序的接口比实际需要的要更复杂
    为了简化问题 考虑一个在屏幕上表示数据的问题 在这里 数据将由内置的Math.sin()方法提供 它可以产生数学上的正弦函数 为了使事情变得更有趣一些 也为了进一步演示Swing组件使用起来有多么简单 我们在窗体底部放置了一个滑块 用来动态控制所显示的正弦波周期的个数 此外 如果调整了视窗的大小 你会发现正弦波能够自动调整 以适应新的视窗
    尽管在任何JComponent上都可以绘图 而且正因为如此 可以把它们当作画布 但是 要是你只是想有一个可以直接绘图的平面的话 典型的做法是从JPanel继承 唯一需要覆盖的方法就是paintComponent() 在组件必须被重新绘制的时候调用它(通常不必为此担心 因为何时调用由Swing决定) 当此方法被调用时 Swing将传入一个Graphics对象 然后就可以使用这个对象绘图了 或在平面上绘制了
    在下面的例子中 所有与绘制动作相关的代码都在SineDraw类中 SineWave类只是用来配置程序和滑块控制 在SineDraw中 setCycles()方法提供了一个钩子(hook) 它允许其他对象(在这个例子中就是滑块控制)控制周期的个数
    在这里插入图片描述
    在这里插入图片描述

    对话框
    对话框是从视窗弹出的另一个窗口 它的目的是处理一些具体问题 同时又不会使这些具体细节与原先窗口的内容混在一起 对话框通常应用于视窗编程环境中
    如果要编写一个对话框 就需要从JDialog继承 它只不过是另一种类型Window 与JFrame类似 JDialog具有一个布局管理器(默认情况下为BorderLayout) 并且要添加事件监听器来处理事件 下面是个简单的例子
    在这里插入图片描述
    在这里插入图片描述
    一旦创建了JDialog以后 必须调用setVisible(true)方法来显示和激活它 当对话框被关闭时 你必须通过调用display()来释放该对话框使用的资源
    下面的例子更加复杂 对话框由一个网格构成(使用GridLayout) 并且添加了一种特殊按钮 它由ToeButton类定义 按钮将先在自己周围画一个边框 然后根据状态的不同 在中央显示 空吧 x 或者 o 开始时的按钮状态为 空白 然后根据每一轮单击 变成 x 或者 o 而且 当你在非 空白 的按钮上单击的时候 它将在 x 和 o 之间翻转 以提供一种有趣的三值翻转(tic-tac-toe)概念的变体 此外 通过改变在主应用视窗中的数字 可以为对话框设置任意的行数和列数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    文件对话框
    某些操作系统具有大量特殊的内置对话框 它们可以处理诸如选择字体 颜色 打印机等操作 基本上所有的图形操作系统都支持打开和保存文件 所以Java提供了JFileChooser 它封装了这些操作 使文件操作变得更加方便
    下面的程序演练了两个JFileChooser对话框 一个用来打开文件 一个用来保存文件 所有有趣的行为都集中在处理两个按钮单击事件的动作监听器中
    在这里插入图片描述
    在这里插入图片描述

    Swing组件上的HTML
    任何能接受文本的组件都可以接受HTML文本 且能根据HTML的规则来重新格式化文本 也就是说 可以很容易地在Swing组件上加入漂亮的文本 例如
    在这里插入图片描述

    滑块与进度条
    滑块能令用户通过前后移动滑点来输入数据 在某些情况下这显得很直观(比如音量控制) 进度条能以从 空 到 满 的动态方式显示数据 这也能给用户以直观的感受 下面的示例还展示了ProgressMonitor 这是一种功能更完备的弹出式对话框
    在这里插入图片描述
    把两个组件联系到一起的关键在于让它们共享一个模型 就像下面这一行一样
    在这里插入图片描述
    当然 也可以使用监听器进行控制 不过在简单的情况下这种方法更直接 ProgressMonitor并没有模型 因此需要使用监听器方式 注意 ProgressMonitor只能向前移动 并且一旦移动到底就会关闭

    选择外观
    可插拔外观 使你的程序能够模仿不同的操作系统的外观 你甚至可以在程序运行期间动态改变程序的外观 不过 通常只会在以下二者中选择一个 要么选择 跨平台 的外观(即Swing的 金属 外观) 要么选择程序当前所在系统的外观 这样你的Java程序看起来就好像是为该系统专门设计的(在大多数情况下 这的确是最好的选择 而且可以避免误导用户) 实现这两种行为的代码都很简单 不过你要确保在创建任何可视组件之前先调用这些代码 这是因为组件是根据当前的外观而创建的 而且创建之后就不会改变(很少会在程序运行的时候改变程序的外观 这个过程相当复杂)
    实际上 如果要使用跨平台的 金属 外观(它是Swing程序的特征) 那么什么都不用做 因为它是默认外观 不过要想使用当前操作系统的外观 那么加入下列代码即可 一般把这些代码添加在main()的开头 至少也要在添加任何组件之前
    在这里插入图片描述
    在catch子句中你什么也不用做 因为一旦你的设置代码失败了 UIManager默认将设置成跨平台的外观 不过在调试的时候 异常非常有用 所以你也许希望通过catch子句看看所发生的问题
    下面的程序能通过命令行参数选择外观 这里选择了几种组件 演示了它们在选择不同的外观时的表现
    在这里插入图片描述
    在这里插入图片描述

    树 表格和剪贴板

    JNLP与Java Web Start
    出于安全目的 我们可以为applet签名 经过签名的applet功能强大 能够有效取代应用程序 不过它们只能在浏览器中运行 这就需要在客户机上运行浏览器 从而增加了额外的开销 同时 它也限制了applet的用户界面 常常带来视觉上的混乱 因为Web浏览器有自己的菜单和工具条 它们会显示在applet的上方
    Java网络发布协议(Java Network Launch Protocol JNLP)在保持applet优点的前提下 解决了这个问题 通过一个JNLP程序 你可以在客户机上下载和安装单机版的Java应用程序 你可以通过命令行 桌面图标 或者与你的JNLP实现一起安装的应用程序管理器来执行这个程序 应用程序甚至可以从其最初被下载的网站上运行

    创建一个JNLP应用程序并不困难 要先编写一个标准应用程序 然后把它打包到一个JAR文件中 这时 需要提供一个启动文件 它是一个简单的XML文件 用来告诉客户端系统下载和安装这个程序所需的所有信息 如果你不准备为JAR文件签名 那么对于你将要在客户机上访问的每种类型的资源 必须使用JNLP API提供的服务进行访问
    下面是FileChooser Test.java的一种变体 它使用了JNLP服务来打开对话框 所以这个类可以被打包进未经签名的JAR文件 然后作为JNLP应用程序部署
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在JnlpFileChooser.java中 被注释的jar命令将产生必须的JAR文件 下面有一个为上面的例子准备的合适的启动文件
    在这里插入图片描述

    jnlp标记的另一个有用的子元素是security标记 这里并没有演示它 下面是一个security标记的例子
    在这里插入图片描述

    要想启动这个程序 你需要一个下载页面 它包含了一个链接到这个.jnlp文件的超文本链接 下面是这个页面的大致内容(没有第一行和最后一行)
    在这里插入图片描述

    Swing与并发

    长期运行的任务
    在使用图形化用户界面编程时最容易犯的错误之一 就是意外地使用了事件分发线程来运行长任务 下面是一个简单的示例
    在这里插入图片描述
    在这里插入图片描述
    当按下b1时 事件分发线程会突然被占用去执行这个长期运行的任务 此时你会看到 这个按钮甚至不会马上弹起来 因为正常情况下会重绘屏幕的事件分发线程现在处于忙状态 并且你也不能做其他任何事 例如按下b2 因为这个程序在b1的任务完成 从而使得事件分发线程再次可用之前 是不会做出响应的 b2中的代码是一种有缺陷的尝试 试图通过中断事件分发线程来解决这个问题
    解决之道当然是在单独的线程中执行长期运行的任务 下面是使用了单线程的Executor 它会自动将待处理的任务排队 然后每次执行其中的一个
    在这里插入图片描述
    在这里插入图片描述
    这个程序有了一些改进 但是当你按下b2时 它会在ExecutorService上调用shutdownNow() 从而会禁用它 如果你试图添加更多的任务 那么就会得到异常 因此 按下b2会使程序不起作用 我们想要的是在关闭当前任务(并放弃待处理任务)的同时 不会停止任何其他的事物 在前几节中描述的Java SE5的Callable/Future机制正是我们所需要的 我们将定义一个被称为TaskManager的新类 它包含多个元组 这些元组持有表示任务的Callable和从Callable中返回的Future 必须使用元组的原因是因为它使得我们可以跟踪最初的任务 这样就可以获取从Future中无法获得的额外信息 下面是示例
    在这里插入图片描述

    TaskManager可以当作通用使用工具来使用
    在这里插入图片描述
    在这里插入图片描述

    现在 它可以用在我们的示例中去管理长期运行的任务了
    在这里插入图片描述
    在这里插入图片描述

    为最终用户提供某种可视线索 以表示任务正在运行以及其执行进度 经常是一种非常重要的工具 这通常可以使用JProgressBar或ProgressMonitor来实现 下面的示例使用了ProgressMonitor
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    可视化线程机制
    下面的例子使一个实现了Runnable接口的Jpanel类可以在其自身上绘制不同的颜色 程序设定为从命令行接受参数 以决定颜色块的数目和颜色变化的时间间隔(线程休眠的时间) 尝试用不同的值运行程序 你可能会发现一些在你的平台之上的多线程实现的有趣的 难以言表的特性
    在这里插入图片描述
    在这里插入图片描述

    可视化编程与JavaBean

    JavaBean是什么

    1. 对于一个名称为xxx的属性 通常你要写两个方法 getXxx()和setXxx() 任何浏览这些方法的工具 都会把get或set后面的第一个字母自动转换为小写 以产生属性名 get方法返回的类型要与set方法里参数的类型相同 属性的名称与get和set所依据的类型毫无关系
    2. 对于布尔型属性 可以使用以上get和set的方式 不过也可以把get替换成is
    3. Bean的普通方法不必遵循以上的命名规则 不过它们必须是public的
    4. 对于事件 要使用Swing中处理监听器的方式 这与前面所见到的完全相同 addBounceListener(BounceListener)和removeBounceListener(BounceListener)用来处理BounceEvent事件 大多数情况下 内置的事件和监听器就能够满足需求了 不过也可以自己编写事件和监听器接口
      我们可以使用这些规则编写一个简单的Bean
      在这里插入图片描述

    使用Introspector抽取出BeanInfo
    JavaBean模式的最关键部分之一 表现在当你从选用区拖动一个Bean 然后把它放置到窗体上的时候 IDE构建工具必须能够创建这个Bean(如果有默认构造器就可以创建) 然后在不访问Bean的源代码的情况下抽取出所有必要信息 以创建属性和事件处理器的列表
    Java的反射机制能发现未知类的所有方法 对于解决JavaBean的这个问题 这是个完美的方案 你不用像其他可视化编程语言那样使用任何语言附加的关键字 实际上 Java语言里加入反射机制的主要原因之一就是为了支持JavaBean(尽管反射也支持对象序列化和远程方法调用) 所以 你也许会认为IDE构建工具的编写者将使用反射来抽取Bean的方法 然后在方法里面查找出Bean的属性和事件
    这当然是可行的 不过Java的设计者希望提供一个标准工具 不仅要使Bean用起来简单 而且对于创建更复杂的Bean也能够提供一个标准方法 这个工具就是Introspector(内省器)类 这个类最重要的就是静态的getBeanInfo()方法 向这个方法传递一个Class对象引用 它能够完全侦测这个类 然后返回一个BeanInfo对象 可以通过这个对象得到Bean的属性 方法和事件
    通常 你不用关心这些问题 也许能直接从货架上获得大多数想用的Bean 而不必知道底层的细节 你只需把Bean拖动到窗体上 配置它们的属性 然后为感兴趣的事件编写处理程序即可 不过 使用Introspector来显示Bean的信息是个值得学习的练习 下面就是这个工具
    在这里插入图片描述
    在这里插入图片描述

    在启动之后 程序强制评估frogbean.Frog 下面是程序输出 这里移除了不必要的额外细节
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    一个更复杂的Bean
    下面的例子稍微复杂一些(虽然它并不是很重要) 它是一个JPanel 当鼠标移动的时候 可以在鼠标周围绘制小圆圈 当你按下鼠标 单词 Bang 将出现在屏幕的中央 并且触发一个动作监听器
    你可以改变的属性包括 圆圈的大小 当按下鼠标时所显示的单词的颜色 大小和文本 BangBean类还具有addActionListener()和removeActionListener() 所以你可以自己编写监听器并与之关联 当用户在BangBean上单击的时候 你的监听器就会被触发 你应该能够识别它们支持的属性和事件
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    下面是测试Bean的BangBeanTest类
    在这里插入图片描述

    JavaBean与同步
    当创建Bean的时候 必须要假设它可能会在多线程环境下运行 也就是说

    1. 尽可能地让Bean中的所有公共方法都是synchronized(同步)的 当然 这将导致synchronized的运行时开销(在近几个版本的JDK中 这个开销已经大大降低了) 如果这么做会有问题 那么对那些不会导致临界区域问题的方法 可以考虑不同步 但要记住 这些方法并非总是这么容易做出判断 进行同步的方法应该尽可能短(比如下面例子中的getCircleSize()方法) 并且(或者)是 原子的 原子性是指 在调用含有这一小段代码的方法时 对象不能被改变 不同步这样的方法也许不会对程序的执行速度有明显的效果 所以最好是同步Bean的所有公共方法 只有在确实会有明显效果并且你可以安全地移除时 才在一个方法上移除synchronized关键字
    2. 当一个多路事件触发了一组对该事件感兴趣的监听器时 你必须假定 在你遍历列表进行通知的同时 监听器可能会被添加或移除
      第一点很容易处理 但第二点就需要做更多的思考 前面版本的BangBean.java通过忽略synchronized关键字并使用单路事件处理方式 回避了并发问题 下面是修改后的版本 它可以在多线程环境下工作 而且使用了多路事件处理方式
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

    把Bean打包
    在把JavaBean加入到某个支持Bean的IDE之前 必须把它置于一个Bean容器中 Bean容器 也就是一个JAR文件 它里面包含了Bean的所有.class文件以及能表明 这是一个Bean 的 清单(manifest)文件 清单文件是一个文本文件 它遵循特定的格式 对于BangBean 它的清单文件看起来像这样
    在这里插入图片描述

    唯一需要技巧的部分是 要确保在 Name 字段写上正确的路径 回过头看看前面的BangBean.java 就会发现它处于bangbean包中(也就是一个名为 bangbean 的子目录 它不包括在classpath中) 清单文件的名称字段必须含有这个包信息 此外 必须把清单文件放在包的根目录的上层目录中 这里就是把清单文件放在 bangbean 目录的父目录中 然后需要在清单文件所在的目录下调用jar工具 如下所示
    在这里插入图片描述

    你可能会奇怪 当我编译完BangBean.java之后 生成的其他.class文件在哪呢 其实 它们都在bangbean子目录下 上面jar命令行的最后一个参数就是bangbean目录名 当你把目录名传递给jar工具时 它将把整个目录打包进JAR文件(在本例中 包括了BangBean.java源文件 你也许不会选择在自己的Bean中包括源代码) 此外 如果你把刚才生成的JAR文件解包 就会发现里面并没有你指定的清单文件 jar工具创建了自己的清单文件(部分根据你提供的信息)MANIFEST.MF 这个文件放在 META-INF(元信息)目录下 打开这个文件 就会看到jar为每个文件都添加了数字签名信息 如下所示
    在这里插入图片描述

    对Bean更高级的支持

    你可以针对属性提供高级功能 前面的例子只演示了单一属性 但也可以使用数组来表示多重属性 这称为索引属性 只要提供恰当的方法(也就是根据命名规则给方法命名) Introspector将识别出索引属性 这样你的应用程序构建工具就可以正确工作
    属性可以被绑定 即它们能通过PropertyChangeEvent事件通知其他对象 这些被通知的对象可以根据Bean上的变化来决定如何改变自己
    属性可以被约束 即如果属性的改变是不可接受的 其他对象可以否决这个改变 这些对象也是通过PropertyChangeEvent事件得到通知的 而且能抛出PropertyVetoException异常来阻止属性的改变 然后恢复属性的旧值
    还可以改变Bean在设计阶段的表示方式

    1. 可以为自己的Bean提供一个自定义的属性表 对于所有其他Bean 将使用通常的属性表 但当你的Bean被选中的时候 将自动激活你提供的表
    2. 还可以为特定的属性提供自定义的编辑器 对于其他属性 将使用普遍编辑器 但是当你的特殊属性被编辑的时候 将自动激活的提供的编辑器
    3. 可以为你的Bean提供一个自定义的BeanInfo类 它可以产生与默认情况下由Introspector所提供的信息不同的信息
    4. 还可以把每一个FeatureDescriptor里的 专家 模式打开或者关闭 这样就能够区分简单功能和复杂功能

    Swing的可替代选择
    尽管Swing类库是Sun支持的GUI 但它决不是创建图形化用户界面的唯一方式 有两种重要的可替代选择 即用于Web之上的客户端GUI的使用MacroMedia的Flex编程系统的MacroMedia Flash 以及用于桌面应用的开源的Eclipse标准工具包(Standard Widget Toolkit SWT)类库

    用Flex构建Flash Web客户端

    Hello Flex
    请观察下面的MXML代码 它定义了一个用户界面
    在这里插入图片描述

    ActionScript是ECMAScript或JavaScript的某个版本 它看上去与Java很相似 并且除了支持动态脚本机制之外 还支持类和强类型 通过向本例中添加脚本 我们可以引入行为 在下面的示例中 MXML Script控件被用来直接将ActionScript放置到MXML文件中
    在这里插入图片描述

    编译MXML

    为了将MXML文件编译为Flash字节码 你有两个选择

    1. 你可以将MXML文件放在Java Web应用程序中 与JSP和HTML同处一个WAR文件中 然后在浏览器请求MXML文档的URL时 在运行时编译所请求的.mxml文件
    2. 你可以用Flex命令行编译器mxmlc编译MXML文件

    当你在命令行中调用Flex的mxmlc编译器时 就会产生SWF文件 可以按照你的意愿部署它们 mxmlc可执行程序位于Flex安装的bin目录下 调用它时不提供任何参数可以将有效的命令行选项列出来 通常 你需要指定Flex客户端组件库的位置 来作为-flexlib命令行选项 但是在像前面看到的两个非常简单的示例中 Flex编译器将假设组件库的位置 因此可以像下面这样编译前面的两个示例
    在这里插入图片描述
    这将产生一个helloflex2.swf文件 它可以在Flash中运行 或者与HTML一起置于任何HTTP服务器之上(一旦Flash被加载到Web浏览器中 你通常只需在SWF文件上双击就可以在浏览器中启动它)
    对于helloflex2.swf 你可以看到下面这个运行在Flash Player中的用户界面
    在这里插入图片描述
    在更复杂的应用程序中 你可以通过引用在外部ActionScript文件中的函数 来将MXML和ActionScript分离开 在MXML 可以使用下面用于Script控件的语法
    在这里插入图片描述
    这行代码使得MXML控件可以引用位于名为MyExternalScript.as的文件中的函数 就好像这些函数位于MXML文件中一样

    MXML与ActionScript
    MXML是ActionScript类的声明式快捷方式 无论你看到何种MXML标签 都有一个同名的ActionScript类与之对应 当Flex编译器解析MXML时 它首先将XML转换为ActionScript 并加载所引用的ActionScript类 然后将这些ActionScript编译链接为SWF

    容器与控制
    Flex组件库的可视化核心是一个容器集合 这些容器管理着布局 以及在容器中的控件数组 容器包括面板 垂直和水平箱体 瓦片 折叠夹 分格箱体以及网格等 控件是用户界面上的小部件 例如按钮 文本区域 滑块 日历和数据网格等等
    展示一个可以显示和排序音频文件列表的Flex应用程序 这个应用程序演示了容器 控件 以及如何从Flash连接到Java
    现在我们开始编写MXML文档 将一个DataGrid控件(更加复杂的Flex控件之一)放置到一个Panel容器中
    在这里插入图片描述
    在这里插入图片描述

    效果与样式
    Flash Player使用向量来呈现图形 因此它可以在运行时执行极富表现力的转换 Flex效果可以让你浅尝这些动画类型 效果是指可以通过使用MXML语法而应用到控件和容器上的转换

    通过Flex对层叠样式表(Cascading Style Sheets CSS)的支持 我们还可以进行样式标准化操作 如果你将一个CSS文件附着到MXML文件上 那么Flex控件将都遵循其中的样式 例如 songStyles.css包含下面的CSS声明
    在这里插入图片描述

    事件
    用户界面是一种状态机 它在状态发生变化时执行动作 在Flex中 这些变化是通过事件来管理的 Flex类库包含大量各种不同的控件 它们具有大量的事件 覆盖了鼠标移动和键盘点击的所有方面

    连接到Java
    在MXML文件末尾的RemoteObject标签设置了到外部Java类gui.flex.SongService的连接 Flex客户端将使用这个Java类中的getSongs()方法来为DataGrid获取数据 为了实现这一点 它必须看起来像是一个服务 一个用户可以用来交换消息的端点 在RemoteObject标签中定义的服务有一个source属性 它指示的是RemoteObject的Java类 并且还指定了一个在Java方法返回时被调用的ActionScript回调函数onSongs() 潜逃的methods标签声明了getSongs()方法 它可以使Java方法对Flex应用程序中的其他部分都是可访问的

    现在可以使用ActionScript从Flash中调用getSongs()方法了
    在这里插入图片描述
    根据MXML的配置 这将调用SongService类中的getSongs()方法
    在这里插入图片描述
    在这里插入图片描述
    每个Song对象都只是一个数据容器
    在这里插入图片描述

    下面是ActionScript的列表 在MXML文件中用Script控件将其导入
    在这里插入图片描述
    在这里插入图片描述
    为了处理对DataGrid单元格的选中操作 我们在MXML文件的DataGrid声明中添加了cellPress事件属性
    在这里插入图片描述
    当用户在DataGrid中点击一首歌时 将会调用上面的ActionScript中的selectSong()

    数据模型与数据绑定

    在最简单的数据绑定形式中 控件可以直接引用数据而不需要用粘合代码把数据复制到控件中 当数据更新时 引用它的控件也会自动更新 而不需要任何程序员的干预 Flex基础设施会恰当地响应数据变化事件 并且更新所有绑定到该数据的控件
    下面是数据绑定语法的简单示例
    在这里插入图片描述

    构建和部署
    在使用前面的示例时 你在命令行中可以不提供-flexlib标签 但是为了编译这个程序 必须使用-flexlib标签指定flex-config.xml文件的位置 对我的安装来说 下面的命令是可以工作的 但是你必须将其修改为适应你自己的配置(命令是单行的 即中间被包装的那一行)
    在这里插入图片描述

    另外 你必须配置服务器使得Flex应用程序可以成功地与Java文件对话 Flex试用包中包含一个JRun服务器 一旦你安装了Flex 就可以通过计算机菜单或者命令行来启动它
    在这里插入图片描述
    你可以通过在Web浏览器中打开http://localhost:8700/samples 并查看各种示例来验证这个服务器是否成功启动了(这还是一种熟悉Flex能力的好方式)

    创建SWT应用

    Hello SWT
    让我们以最简单的 hello world 风格的应用程序开始
    在这里插入图片描述

    为了证明Shell是主窗口 下面的程序将创建大量的Shell对象
    在这里插入图片描述

    SWT也使用了布局管理器 虽然它与Swing使用的不同 但是思想一致 下面是稍微复杂一些的示例 它接收从System.setProperties()中获得的文本 并将其添加到shell中
    在这里插入图片描述

    根除冗余代码
    有些事情是你在每个SWT应用程序中都会做的 这与Swing程序中的重复动作一样 对于SWT 你总是得创建Display 从Display中创建Shell 然后创建readAndDispatch()等等 当然 对于某些特殊情况 你可以不做这些 但是它非常普遍 绝对值得去根除这些重复代码
    我们需要强制每个应用程序遵循下面的接口
    在这里插入图片描述
    应用程序应该提交给了Composite对象(Shell是它的一个子类) 并且应该用它在createContents()内部创建其所有的内容 SWTConsole.run()会在恰当的地方调用createContents() 根据用户传递给run()的参数来设置shell的尺寸 打开shell 然后运行事件循环 最终在程序退出时释放shell
    在这里插入图片描述
    在这里插入图片描述

    我们可以创建一个DisplayProperties.java的变体 它可以用SWTConsole来显示机器环境
    在这里插入图片描述

    菜单
    为了演示基本的菜单 下面的程序将读入它自己的源代码 将其断开为单词 然后用这些单词组装菜单
    在这里插入图片描述
    在这里插入图片描述

    页签面板 按钮和事件

    为了演示各种基本部件 下面的程序在页签面板内部放置了大量的子示例 你还将看到如何创建Composite(大体上与Swing的JPanel相同) 以实现将一些部件放到其他的部件中
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    图形
    下面是将Swing程序SineWave.java转译为SWT的版本
    在这里插入图片描述
    在这里插入图片描述

    SWT中的并发
    尽管SWT/Swing是单线程的 但是如果产生非确定性的程序 那么仍旧有可能违反这种单线程性 基本上 你不会想要用多线程来编写显示功能 因为如果这样的话 它们就会以令人惊讶的方式互相改写了
    SWT根本不允许这样 如果你试图用多个线程来编写显示功能 那么它就会抛出异常 这可以防止程序员新手因不注意而犯此类错误 从而在程序中引入难以发现的缺陷
    下面是Swing版本的ColorBoxes.java程序转移为SWT的版本
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    SWT还是Swing
    在许多场合 SWT是一种比Swing更加简单的编写代码方式 但是采用SWT的GUI编程仍旧很复杂 因此你使用SWT的动机可能应该是 首先 为用户在使用你的应用系统时提供一种更加透明的体验(因为你的应用程序的感官与在用户平台上的其他应用程序相同) 其次 如果认为SWT提供的可响应性很重要 否则 Swing就可能是恰当的选择

    展开全文
  • 作者 Naysawn Naderi 译者 ...无论使用的是桌面的,Web的或是手机上的应用程序,用户界面都在其中起着一个重要的作用。...,用户界面设计的思...

    作者 Naysawn Naderi 译者 曹如进 发布于 2009年11月24日 下午6时0分

     

    无论使用的是桌面的,Web的或是手机上的应用程序,用户界面都在其中起着一个重要的作用。Alan Cooper ,用户界面设计的思想领袖,在他的《About Face 》一书中给出了一些有趣和有用的建议来帮助应用程序创建UI。下面是该书中的部分重要思想。

    为中级用户(Intermediates Users)设计

    Cooper认为,不管什么软件产品,它的绝大多数用户都可以归类为中级用户——即那些基本了解如何使用产品和一般使用产品重复进行同样操作的用户。他估计,这样的用户约占使用产品总人数的80%,而剩下的20%平均分布在初学者和高级用户当中。

    尽管如此,他饶有兴趣地指出,虽然中级用户是产品的主要使用群体,但是他们的需求却往往在UI设计中被忽视。他指出,由于管理层经常与初学者打交 道,因此 都倾向于满足初学者大部分的需求。而开发人员,应该定义为专家用户,他们在思考时都倾向于将终端用户也看作为专家用户。Cooper认为,虽然中级用户是 产品的主要使用群体,但是他们在这样的混乱情况下都显得很迷惑。

    他解释到,三种类型用户的需求由他们熟悉产品程度的不同而不同。专家用户想要快速地干活并且可以定制应用程序,而初学者更多关注的是基本工具的概况。中级用户则倾向于使用工具来重复地做同样的事情,并且不希望界面妨碍他们的工作流程。

    使用工具来帮助初学者成为中级用户

    虽然只有10%的产品用户是初学者,但是所有的产品用户都要经历初学者的阶段。因此要特别注意去设计一些界面来帮助新手用户学会应用程序的基础。

    虽然UI设计的目标是帮助初学者转移到中级用户,但是Coppe很快指出,一旦一名用户从最初状态过渡到了下一个状态,那么那些帮助都不再有太大用处。事实上,它可能会妨碍用户的工作流程。

    大量产品可以帮助培养初学者,但同时它们也干扰着该产品的中级用户。例如在VS2008中,我们在生成的单元测试代码里加入注释来帮助初学者理解一 个单元 测试的各个组成部分。虽然它们很好地达到了目的,但是一旦组成部分被理解后,它们就会妨碍用户。因此我们在VS2010中将它们移除了。

    想要帮助用户成为中级用户,Cooper建议,可以在UI上添加一些概要信息,来帮助用户如何使用产品。他指出,当用户想要关闭这些信息时,它们应当可以被移除。这可以采用经典的“入门指南”视频的形式来概述产品的功能。

    Cooper认为,在线帮助和工具提示实际上更好地满足了中级用户而并非初学者,因为他们的需求略有不同。他提到,中级用户会在帮助文档中查找参考资料,而初学者往往对文档的概要信息更为感兴趣。

    少即是多

    我喜欢一句话叫做:“不管你的界面有多酷,少一些会更好”。

    有时在UI设计中,我觉得我们忘记了用户是将产品当做一个工具在完成特定的目标。有时我们也被很酷的导航工具所左右,将注意力过多地放在它上面。一个在黑暗中可以闪闪发光的紫色锤子,如果不能够有效的把钉子钉进木头的话,它仍然是毫无用处的。

    Cooper是一个简约设计的主张者:在简约设计中每一个选项都应该有目的并且是直接的。Cooper主张减少用户界面中过多的直接选项。他认为,在一个精心设计的UI中,用户界面对用户几乎是透明的,因为它自然地符合了用户的思维模式。

    为最可能的操作进行设计,为可能的操作提供方法

    Cooper主张在设计产品和通过应用程序对主线流程进行优化时,应当按照用户的思维模式来做。虽然这常常意味着要在窗口中去掉很多程序员看来很舒服的选项,但是这并不意味着没有方法来做那些复杂精细的事情,而是说访问它们要困难一点。

    Cooper认为,不管用户在应用程序工作流中的哪一个步骤,总会执行一两种活动类型。因此,界面应当被优化来帮助用户找到这些活动,并且通过工作 流来指 导他们,而并非每次仅仅列出所有的选项。此外对于用户而言,也应当有方法来让他们访问到非主流的活动,但是UI不应当为此而进行优化。

    我认为Office Ribbon在这方面做得很出色,它为最可能的操作进行了优化并且还允许其他可能进行的操作。在Ribbon中,用户最喜爱使用的条目只需要1次点击,使 用较少的条目需要2次点击,而那些不太可能用到的条目需要3次或更多的点击。可以很清楚地看到,他们为最可能的操作优化了UI。

    移除错误提示和确认对话框

    按照遵循用户的思维模式和移除所有妨碍中级用户工作流程的原则,Cooper主张移除掉产品中所有的错误和确认对话框。他认为从统计学角度来看,应用程序应该有着很好的正确率,它要做的是为用户提供一个撤销他们操作的选项。

    Cooper说,用户都想看到对他们可用的选项,然后进行操作,并希望执行完后可以得到一个确认来告知刚刚的操作已经成功执行。如果一个操作结束后 如果没 有确认的话,用户会想知道他们的操作是否已经真正地执行。为了能够与用户交流操作的结果,相比较使用一个弹出的提示框,Cooper更提倡使用内嵌的状态 提示和积极的听觉反馈。

    虽然我同意移除所有环境下的确认对话框很理想化,但是要将他它们全部移除显得太过于鲁莽,这不是我的风格。想要实现Cooper的建议,开发者需要 让用户 执行的每一个操作可逆,这样做不仅代价高,而且在我来看也不是很必要。我喜欢尽全力地移除确认对话框和让操作可逆,但是万一碰到暴力的(drastic) 操作,例如磁盘格式化和删除文件,弹出一个确认对话框看起来会更好,实际上这也是用户所期待的。

    另一方面,我同意Cooper关于移除所有错误对话框的观点。尽管错误对话框对程序员来说很有意义,但是它们对终端用户的意义却很小。我看到很多用 户在他 们的工作流被错误对话框破坏后变得非常泄气。Cooper提到,虽然它们可以用来标志代码的某个部分发生了错误,但是用户往往都将其理解为“我做错了什么 事情”。当用户被反复告知出错的话,他们会开始讨厌你的产品。我最近在和Mac UI打交道,并且很惊讶地看到它们似乎不断地吃掉了自己的异常。显然,出现了问题是不应该呈现给用户看的。

    关于作者

    Naysawn Naderi是微软Visual Studio测试小组的程序经理(Program Manager )。目前他正专注于创建正确的用户体验,来让手工测试人员可以更好地进行测试以及更有效地与软件小组人员进行合作。他拥有麦吉尔大学(McGill University)的电气工程学士学位。他的blog是Testmundo

    转载于:https://www.cnblogs.com/ajuanabc/archive/2009/12/14/2463301.html

    展开全文
  • 图形化用户界面,这个属于前端,一般由专门的工具实现。 Java的优势在后端,so,略过

    图形化用户界面,这个属于前端,一般由专门的工具实现。

    Java的优势在后端,so,略过


    展开全文
  • 8.1 图形用户界面概述 8.1.1 程序的用户界面 程序设计领域,一个程序的用户界面指的是程序中与用户进行交互的部分,用户通过UI向程序输入数据或者请求程序执行特定任务,而程序通过UI向用户显示各种信息。 GUI能够...
    8.1 图形用户界面概述
    8.1.1 程序的用户界面
    1. 程序设计领域,一个程序的用户界面指的是程序中与用户进行交互的部分,用户通过UI向程序输入数据或者请求程序执行特定任务,而程序通过UI向用户显示各种信息。
    2. GUI能够大大增强程序的用户友好性,提高用户使用计算机的效率,因此是设计中的重要技术。
    8.1.2 图形界面的组成
    1. 图形界面由多种图形元素组成,这些图形元素称为构件(widget)。
    2. 像窗口这样的能够容纳其他构件的构件,一般称为容器。
    3. GUI工具包一般提供布局管理器用于布局设计。
    8.1.3 事件驱动
    1. 事件是针对应用程序所发生的事情,并且应用程序需要对这种事情作出响应。
    2. 程序对事件的响应其实就是调用预先编制好的代码来对事件进行处理,这种代码称为事件处理程序(event handler)。
    3. 事件驱动编程就是针对“程序的执行由事件决定”的应用的一种编程范型。
    4. 事件驱动的程序一般都有一个主循环或称事件循环,该循环不停地做两件事:事件监测和事件处理。
    8.2 GUI编程
    8.2.1 GUI编程概述
    1. GUI编程一般需要如下几个步骤:
    • 设计界面外观
    • 为每个构件定义事件处理程序
    • 编写应用程序地启动和总控部分。
    1. GUI工具包一般都利用面向对象技术实现的,即构件都是对象,具有属性和方法。构件对象的属性用来记录构件的各种数据特性,构件对象的方法实现构件的行为特性。
    8.2.2 初识Tkinter
    • 构件对象属性
    • 根窗口的标题和大小
    • 父构件与子构件
    8.2.3 常见GUI构件的用法
    • 标签
      标签用于在窗口中显示文本信息,Tkinter定义了类Label来支持标签构件的创建。
    • 按钮
      按钮也叫命令按钮,是界面中的一个矩形区域(通常长度大于高度),矩形内部是描述性的标题。
    • 勾选钮
      勾选钮也称为勾选框和复选框,用于向用户提供一个选项,用户对该选项有“选中”或“不选”两种选择。
    • 单选框
      单选框也是列出选项供用户选择,并且通常也是由若干个单选钮构成一组来提供多个相关的选项。
    • 文本编辑区
      文本编辑区是允许用户输入和编辑数据的区域,用户使用键盘在这个区域中输入文本,输入过程中随时可以进行编辑。
    • 框架
      框架是一种容器,其主要用途是将一组相关的基本构件组合成为一个“复合”构件。
    • 菜单
      菜单构件是一个由许多菜单项组成的列表,每个菜单项表示一条命令或一个选项。
    • 顶层窗口
      顶层窗口跟根窗口一样,可以独立地移动和改变大小,并且不需要像其他构件那样必须在根窗口中进行布局后才显示。
    8.2.4 布局
    1. 布局指的是界面元素在界面中的位置安排。
    • Pack布局管理器
      Pack布局管理器以紧凑的方式将构件在窗口中“打包”,调用构件的pack方法既可以在这种方式布局。
    • Grid布局管理器
      Grid布局管理器将窗口或框架视为一个由行和列构成的二维表格,并将构件放入行列交叉处的单元格中。
    • Place布局管理器
      Place布局管理器直接指定构件在父构件中的位置坐标。
    • 对话框
      对话框是独立的顶层窗口,通常是在程序执行过程中根据需要而“弹出”的窗口,用于从用户获取输入或者向用户显示消息。
      对话框分为两种类型:模态和非模态对话框。模态对话框在关闭之前阻止程序其他窗口的操作,而非模态对话框则不会阻止程序其他窗口的操作。
    8.3 Tkinter事件驱动编程
    8.3.1 事件和事件对象
    1. 事件是针对应用程序所发生的事情,并且需要应用程序对它做出响应或进行处理。
    2. Tkinter事件可以用特定形式的字符串来描述,称为事件模式。事件模式的一般形式是:
      <modifier-type-detail>
      其中类型符type指定事件类型,最常用的类型有分别鼠标事件和键盘事件的Button和Key;修饰符modifier用于描述鼠标键或键盘的双击、组合等情况;细节符detail指定具体的鼠标键或键盘按键,如鼠标的左中右三个键分别用1、2、3表示,键盘按键用相应字符或按键名称表示。modifier和detail是可选的,而且事件模式经常可以使用简化形式。
    8.3.2 事件处理
    1. GUI应用程序的核心是对各种交互事件的处理程序。应用程序一般在完成建立图形界面等初始化工作后都会进入一个事件循环,等待事件发生并触发相应的事件处理程序。
    • 键盘事件与焦点
    • 绑定到多个事件
    • 绑定层次
    • 协议处理
    • 虚拟事件
    8.4 模型-视图设计方法
    8.4.1 将GUI应用程序封装成对象
    8.4.2 模型与视图
    1. 复杂应用程序经常可以分解成两个部分:核心逻辑和用户界面。
      在这里插入图片描述
    8.4.3 编程案例:汇率换算器
    • 程序规格
    • 明确候选对象
    • 实现模型
    • 基于文本的用户界面
    • 实现GUI
    8.5 练习

    书籍下载地址:https://download.csdn.net/download/qq_37590544/10959608

    展开全文
  • 等级考试二级JAVA考点分析之编写图形用户界面2006年8月14日来源:233网校网校课程 ...(3)Java采用构件思想开发图形用户界面。(4)编写图形用户界面的基本步骤:首先确定界面中的构件如何放置,然后使构件响应用户的操...
  • 使用线程处理的动机之一就是创建有响应的用户界面 从一个文件按行读取数据并把它们打印到控制台上 每行显示完成之后会休眠 sleeping 挂起 暂停执行当前线程 一秒钟 这个过程不会检查用户输入 所以用户界面是无响应...
  • 软件界面设计思想方法

    万次阅读 2015-03-04 10:16:28
    15.1什么是好的软件界面 简而言之,好的软件界面应当是易用的和美观的。易用是交互设计的主要目标,美观是视觉设计的主要目标,交互设计和视觉设计完成后,最终靠编程来实现可...之后出现了图形用户界面,例如Windo
  • 图形用户界面

    2012-11-23 13:28:42
    一:常用的GUI构件 JButton, JCheckBox, JRadioButton, JComboBox, JList, JTextField, JTextArea, JPanel JSlide; 二:基于GUI程序设计的一些基本思想
  • 在实现计算器程序的时候要使用用户界面与业务逻辑分离的思想。 界面与逻辑 基本程序架构一般包含: - 用户界面模块(UI):接受用户输入及呈现数据; -业务逻辑模块(Business Logic) :根据用户需求处理数据 ...
  • /* * Java编程思想 第4版 * 第22章 图形化用户界面 * 练习28:(7)创建一个骰子类(只是一个类,没有GUI),然后创建五个骰子并重复地掷骰子。 * 画出一条表示每次掷骰子的点数总和的曲线,然后在你掷骰子的...
  • 火龙果软件工程技术中心 复杂用户界面开发(本文用作UI开发),为敏捷开发提供了某些独特的挑战。与那些功能大部分是自动运行业务规则或计算薪水的项目不同,因为由用户培训或生产力缺失引起的各种问题,UI开发项目...
  • chapter12 通过异常处理错误 12.2基本异常 12.3捕获异常 12.4创建自定义异常 try catch finally 使用finally进行清理 ...chapter22 图形化用户界面 applet swing基础
  • 文章目录1 计算器用户界面与业务逻辑的分离1.1 分析1.2 代码实现 1 计算器用户界面与业务逻辑的分离 1.1 分析 界面与逻辑: 基本程序架构一般包含: 用户界面模块(UI):接受用户输入及呈现数据。 业务逻辑模块...
  • TraitsUI——轻松制作用户界面

    千次阅读 2018-07-31 15:58:21
    在开发科学计算程序时,我们希望快速实现一个够用的界面,让用户能够交互式的处理数据,而又不...TraitsUI是一套建立在Traits库基础上的用户界面库。它和Traits紧密相连,如果你已经设计好了一个继承于HasTraits的类...
  • 1 说明:=====1.1 TraitsUI是一套建立在Traits库基础上的用户界面库。1.2 系统将会使用TraitsUI自动生成一个界面,以供用户交互式地修改对象的trait属性。1.3 以traits为基础、以Model-View-Controller为设计思想的...
  • 另外就是要有要有如下架构思想:在服务器端 用一个HashMap,socket> 维护所有用户相关的信息,从而能够保证和所有的用户进行通讯。 客户端的动作: (1)连接(登录):发送userName 服务器的对应动作:1)界面显示...
  • 主要说明用户界面编程的基本思想、不同界面的数据传递问题。
  • 智能Agent越来越多地应用于用户界面设计。通过使用Agent,用户界面(也包括整个系统)就被分解为不同的可以互相通信的Agent。界面Agent作为用户与系统之间的中介,使得用户的输入不再是方法的调用,而是以通信的方式...
  • 针对Web应用系统中应用环境和用户需求易于变化的问题,将柔性软件思想与Web界面的设计结合起来,提出了一个具有动态重配置能力的基于构件的柔性Web用户界面模型。该模型把描述构件显示样式的模板和适应业务数据结构...
  • MATLAB图形用户界面

    2017-09-02 17:23:06
     编程的基本思想是:我执行一个操作,程序做出一个反应。“一个操作”包括点击鼠标、拖动滑块、填写数据、选择选项等;“做出一个反应”包括计算、储存在哪里、贴个图出来、显示在哪里。  简言之:当点击按钮A时...
  • 下载本文所附源代码 1、概述测试优先是测试驱动开发(Test-DrivenDevelopment,TDD)的核心思想,它要求在编写产品代码前先编写基于产品代码的测试代码。在测试驱动开发的单元测试中,对GUI应用实施自动测试应该是测试...
  • 程序员如何进行用户界面设计

    千次阅读 2013-02-01 09:26:21
    程序员设计的用户界面,其实大多数并不是一个好...我认为主要是程序员编码的思想影响到了对用户界面的设计,很多程序员都有钻牛角尖、猎奇的倾向,喜欢所有东西都DIY,这也就促使他们在设计软件时,希望它非常灵活,很
  • LabVIEW 虚拟仪器软件广泛应用于测控系统的开发...在阐述了菜单形式用户界面基本设计思想和几个主要函数 的基础上, 详述了两种不同的菜单式友好用户界面实现方 法。文章所述方法在实际课题中的运用取得了良好的效 果。
  • 测试优先是测试驱动开发(Test-Driven Development, TDD)的核心思想,它要求在编写产品代码前先编写基于产品代码的测试代码。在测试驱动开发的单元测试中,对GUI应用实施自动测试应该是测试驱动开发的软肋之一。由于...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,215
精华内容 1,286
关键字:

思想用户界面