第7章 图形程序设计
本章内容:
* Swing概述
* 创建框架
* 框架定位
* 在组件中显示信息
* 处理2D图形
* 使用颜色
* 文本使用特殊字体
* 显示图像
- 本章主要讲述如何编写定义屏幕上的窗口大小和位置的程序;如何在窗口中采用多种字体显示文本;如何显示图像等。
7.1 Swing概述
- 在Java 1.0刚刚出现的时候,包含了一个用于基本GUI程序设计的类库,Sun将它称为抽象窗口工具箱(Abstract Window Toolkit,AWT)。基本AWT库采用将处理用户界面元素的任务委派给每个目标平台(Window、Solaris、Macintosh等)的本地GUI工具箱的方式,由本地GUI工具箱负责用户界面元素的创建和动作。在不同的平台上,操作行为存在着一些微妙的差别。有的图形环境(如X11/Motif)并没有像Window或Macintosh这样丰富的用户界面组件集合。在不同平台上的AWT用户界面库中存在着不用的bug。研发人员必须在每一个平台上测试应用程序。
- 在1996年,Netscape创建了一种称为IFC(Internet Foundation Class)的GUI库,它采用了与AWT完全不同的工作方式。它将按钮、菜单这样的用户界面元素绘制在空白窗口上,面对等体只需要创建和绘制窗口。因此,Netscape的IFC逐渐在程序运行的所有平台上的外观和动作都一样。Sun与Netscape合作完善了这种方式,创建了一个名为Swing的用户界面库。Swing可作为Java 1.1的扩展部分使用,现已成为Java SE 1.2标准库的一部分。
- Swing没有完全替代SWT,而是基于AWT架构之上。Swing仅仅提供了能力更加强大的用户界面组件。尤其在采用Swing编写的程序中,还需要使用基本的AWT处理事件。从现在开始,Swing是指“被绘制的”用户界面类;AWT是指像事件处理这样的窗口工具箱的底层机制。
- 在用户屏幕上显示基于Swing用户界面的元素要比显示AWT的基于对等体组件的速度慢一些。鉴于以往的经验,对于任何一台现代的计算机来说,微小的速度差别无房大碍。另外,由于下列几点无法抗拒的原因,人们选择Swing:
- Swing拥有一个丰富、便捷的用户界面元素集合。
- Swing对底层平台依赖的很少,因此与平台相关的bug很少。
- Swing给予不用平台的用户一致的感觉。
7.2 创建框架
- 在Java中,顶层窗口(就是没有包含在其他窗口中的窗口)被称为框架(frame)。在AWT库中有一个称为Frame的类,用于描述顶层窗口。这个类的Swing版本名为JFrame,它扩展于Frame类。JFrame是极少数几个不绘制在画布上的Swing组件之一。因此,它的修饰部件(按钮、标题栏、图标等)由用户的窗口系统绘制,而不是由Swing绘制。
- 所有的Swing组件必须由时间分派线程(event dispatch thread)进行配置,线程将鼠标点击和按键控制转移到用户接口组件。
- 许多Swing程序并没有在事件分配线程中初始化用户界面。在主线程中完成初始化时通常采用的方式。
7.3 框架定位
- JFrame类本身包含若干个改变框架外观的方法。当然,通过继承,从JFrame的各个超类中继承了许多用于处理框架大小和位置的方法。其中最重要的有下面几个:
- setLocation和setBounds方法用于设置框架的位置。
- setIconImages用于告诉窗口系统在标题栏、任务切换窗口等位置显示哪个图标。
- setTitle用于该表标题栏的文字。
- setResizable利用一个boolean值确定框架的大小是否允许用户改变。
- Component类找你哥的setLocation方法是重定位组件的一个方法。Complement中的setBounds方法可以实现一步重定位组件大小和位置的操作。
- 对于框架来说,setLocation和setBounds中的坐标均相对于整个屏幕。
7.3.1 框架属性
- 从概念上讲,title是框架的一个属性。当设置这个属性时,希望这个标题能够改变用户屏幕上的显示。当获取这个属性时,希望能够返回已经设置的属性值。
- 针对get/set约定有一个例外:对于类型为boolean的属性,获取方法由is开头。
7.3.2 确保合适的框架大小
- 要记住:如果没有明确地指定框架的大小,所有框架的默认值为0x0像素。
- 为了得到屏幕的大小,需要按照下列步骤操作。调用Toolkit类的静态方法getDefaultToolkit得到一个Toolkit对象(Toolkit类包含很多与本地窗口系统打交道的方法)。然后,调用getScreenSize方法,这个方法以Dimension对象的形式返回屏幕的大小。Dimension对象同时用公有实例变量width和height保存着屏幕的宽度和高度。
- 下面是为了处理框架给予的一些提示:
- 如果框架中只包含标准的组件,如按钮和文本框,那么可以通过调用pack方法设置框架大小。框架将被设置为刚好能够防止所有组件的大小。在通常情况下,将程序的主框架尺寸设置为最大。正如Java SE 1.4,可以通过调用下列方法将框架设置为最大。
frame.setExtendedState(Frame.MAXINIZED_BOTH)
; - 牢记用户定位应用程序的框架位置、重置框架大小,并且在应用程序再次启动时恢复这些内容是一个不错的想法。
- 如果缩写一个使用多个显示屏幕的应用程序,应该利用GraphicsEnvironment和Graphics Device类获得显示屏幕的大小。
- GraphicsDevice类允许在全屏模式下执行应用程序。
- java.awt.Component 1.0
- boolean isVisible()
- void setVisible(boolean b)
获取或设置visible属性。组件最初是可见的,但JFrame这样的顶层组件例外。 - void setSize(int width,int height) 1.1
使用给定的宽度和高度,重新设置组件的大小。 - void setLocation(int x,int y) 1.1
将组件移到一个新的位置上。如果这个组件不是顶层组件,x和y坐标(或者p.x和p.y)是容器坐标;否则是屏幕坐标(例如:aJFram)。 - void setBounds(int x,int y,int width,int height) 1.1
移动并重新设置组件的大小。 - Dimension getSize() 1.1
- void setSize(Dimension d) 1.1
获取或设置当前组件的size属性。
- java.awt.Window 1.0
- void toFront()
将这个窗口显示在其他窗口前面。 - void toBack()
将这个窗口移到桌面窗口栈的后面,并重新排列所有的可见窗口。 - boolean isLocationByPlatform() 5.0
- void setLocationByPlatform(boolean b) 5.0
获取或设置locationByPlatform属性。这个属性在窗口显示之前被设置,由平台选择一个合适的位置。
- java.awt.Frame 1.0
- boolean isResizable()
- void setResizable(boolean b)
获取或设置resizable属性。这个属性设置后,用户可以重新设置框架的大小。 - String getTitle()
- void setTitle(String s)
获取或设置title属性,这个属性确定框架标题栏中的文字。 - Image getIconImage()
- void setIconImage(Image image)
获取或设置iconImage属性,这个属性确定框架的图标。窗口系统可能会将图标作为框架装饰或其他部位的一部分显示。 - boolean isUndecorated() 1.4
- void setUndecorated(boolean b) 1.4
获取或设置undecorated属性。这个属性设置后,框架显示中将没有标题栏或关闭按钮这样的装饰。早框架显示之前,必须调用这个方法。 - int getExtendedState() 1.4
- void setExtendedState(int state) 1.4
获取或设置窗口状态。状态是下列值之一。Frame.NORNAL、Frame.ICONIFIED、Frame.MAXIMIZED_HORIZ、Frame.MAXIMIZED_VERT、Frame.MAXIMIZED_BOTH。
- java.awt.Toolkit 1.0
- static Toolkit getDefaultToolkit()
返回默认的工具箱。 - Dimension getDcreenSize()
返回用户屏幕的尺寸。
- javax.swing.ImageIcon 1.2
- ImageIcon(String filename)
构造一个图标,其图像存储在一个文件中。 - Image getImage()
获取该图标的图像。
7.4 在组件中显示信息
- 在JFrame中有四层面板。其中的根面板、层级面板和玻璃面板人们并不太关心;它们是用来组织菜单栏和内容窗格以及实现观感的。Swing程序员最关心的是内容窗格(content pane)。在设计框架的时候,将所有的组件添加到内容窗格中。
- 绘制一个组件,需要定义一个扩展JComponent的类,并覆盖其中的paintComponent方法。paintComponent方法有一个Graphics类型的参数,这个参数保存着用于绘制图像和文本的设置。在Java中,所有的绘制都必须使用Graphics对象,其中包含了绘制图案、图像和文本的方法。
- 无论何种原因,只要窗口需要重新绘图,事件处理器就会通告组件,从而引发执行所有组件的paintComponent方法。
- 一定不要自己调用paintComponent方法。在应用程序需要重新绘图的时候,这个方法将被在佛那个地调用,不要人为的干预这个自动的处理过程。
- 如果需要强制刷新屏幕,就需要调用repaint方法,而不是paintComponent方法。它将引发采用相应配置的Graphics对象调用所有组件的paintComponent方法。
- 在框架中添加一个或多个组件时,如果只想使用它们的首选大小,可以调用pack方法而不是setSize方法。
- 有些程序员更喜欢扩展JPanel,而不是JComponent。JPanel是一个可以包含其他组件的容器(container),但同样也可以在其上面进行绘制。有一点不同之处是,面板不透明,这意味着需要在面板的边界内绘制所有的像素。最容易实现的方法是,在每个面板子类的paintComponent方法中调用super.paintComponent来用背景色绘制面板。
- javax.swing.JFrame 1.2
- Container getContentPane()
返回这个JFrame的内容窗格对象。 - Component add(Component c)
将一个给定的组件添加到改框架的内容窗格中(在Java SE 5.0以前的版本中,这个方法将抛出一个异常)。
- java.awt.Component 1.0
- void repaint()
“尽可能快地”重新绘制组件。 - Dimension getPreferredSize()
要覆盖这个方法,发挥这个组件的首选大小。
- javax.swing.JComponent 1.2
- void paintComponent(Grphics g)
覆盖这个方法来描述应该如何绘制自己的组件。
- java.awt.Window 1.0
- void pack()
调整窗口大小,要考虑到其组件的首选大小。
7.5 处理2D图形
- Graohics类包含绘制直线、矩形和椭圆等方法。但是,这些绘制图形的操作能力非常有限。例如,不能改变先的粗细,不能旋转这个图形。
- Java SE 1.2引入了Java 2D库,这个库实现了一组功能强大的图形操作。
- 要想使用Java 2D库绘制图形,需要获得一个Graphics2D类对象。这个类是Graphics类的子类。自从Java SE 2版本依赖,paintComponent方法就会自动地获取一个Graphics2D类对象,只需要进行一次类型转换就可以了。
- Java 2D库采用面向对象的方式将几何图形组织起来。包括描述直线、矩形的椭圆的类:LINE2D、Rectangle2D、Ellipse2D这些类都实现了Shape接口。
- 要想绘制图形,首先要创建一个实现了Shape接口的类的对象,然后调用Graphics2D类中的draw方法。
- 使用Java 2D图形或许会增加一些复杂度。在1.0的绘制方法中,采用的是整型像素坐标,而Java 2D图形采用的是浮点坐标。在很多情况下,用户可以使用更有意义的形式(例如,微米或英寸)指定图形的坐标,然后再将其转换成像素,这样做很方便。在Java 2D库中,内部的很多浮点极端都采用单精度float。毕竟,几何计算的最终目的是要设置屏幕或打印机的像素,所以单精度完全可以满足要求了。只要舍入误差限制在一个像素的范围内,视觉效果就不会受到任何影响。另外,在某些平台上,float计算的速度比较快,并且只占据double值的一般存储量。
- 由于后缀和类型转换都有点麻烦,所以2D库的设计者决定为每个图形类提供两个版本:一个是为那些节省空间的程序员提供的float类型的坐标;另一个是为那些懒惰的程序员提供的double类型的坐标。
- Rectangle2D方法的参数和返回值均为double类型。
- 直接使用Double图形类可以避免处理float类型的值,然后如果需要创建上千个图形对象,还是应该考虑使用Float类,这样可以节省存储空间。
- Rectangle2D和Ellipse2D类都是由公共超类RectangularShape继承来的。
- RectangularShape类定义了20多个有关图形操作的通用方法,其中比较常用的方法有getWidth、getHeight、getCenterX、getCenterY等。
- 从Java 1.0遗留下来的两个类也被放置在图形类的继承层次中。它们是Rectangle和Point类,分别扩展于Rectangle2D和Point2D类,并用整型坐标存储矩形和点。
- Rectangle2D和Ellipse2D对象很容易构造,需要给出
- java.awt.geom.RectangularShape 1.2
- double getCenterX()
- double getCenterY()
- double getMinX()
- double getMinY()
- double getMaxX()
- double getMaxY()
返回闭合矩形的中心,以及最小、最大x和y坐标值。 - double getWidth()
- double getHeight()
返回闭合矩形的宽和高。 - double getX()
- double getY()
返回闭合矩形左上角的x和y坐标。
- java.awt.geon.Rectengle2D.Double 1.2
- Rectangle2D.Double(double x,double y,double w,double h)
利用给定的左上角、宽和高,构造一个矩形。
- java.awt.geom.Rectangle2D.Float 1.2
- Rectangle2D.Float(float x,float y,float w,float h)
利用给定的左上角、宽和高,构造一个矩形。
- java.awt.geom.Ellipse2D.Double 1.2
- Ellopse2D.Double(double x,double y,double w,double h)
利用给定的左上角、宽和高的外接矩形,构造一个椭圆。
- java.awt.geom.Point2D.Double 1.2
- Point2D.Double(double x,double y)
利用给定坐标构造一个点。
- java.awt.geom.Line2D.Double 1.2
- Line2D.Double(Point2D start,Point2D end)
- Line2D.Double(double startX,double startY,double endX,double endY)
使用给定的起点和终点,构造一条直线。
7.6 使用颜色
- 使用Graphics2D类的setPaint方法可以为图形环境上的所有后续的绘制操作选择颜色。
- 只需要将调用dra替换为调用fill就可以用一种颜色填充一个封闭图形的内部。
- 要想绘制多种颜色,就需要按照选择颜色、绘制图形、再选择另外一种颜色、再绘制图形的过程实施。
- fill方法会在右侧和下方少绘制一个像素。
- Color类用于定义颜色。在java.awt.Color类中提供了13个预定义的常量,它们分别表示13种标准颜色。BLANK、BLUE、CYAN、DARK_GRAY、GRAY、LIGHT_GRAY、MAGENTA、ORANGE、PINK、RED、WHITE、WELLOW。
- 可以通过提供红、绿和蓝三色成分来创建一个Color对象,以达到定制颜色的目的。
- 要想设置背景颜色,就需要使用Component类中的setBackground方法。Component类是JComponent类的祖先。
- setForeground方法是用来设定在组件上进行绘制时使用的默认颜色。
- Color类中的brighter()方法和darker()方法的功能,它们分别加亮或变暗当前的颜色。使用brighter方法也是加亮条目的好办法。实际上,brighter()只微微地加亮一点。要达到耀眼的效果,需要调用三次这个方法:
c.brighter().brighter().brighter()
。 - java在SystemColor类中与定义了很多颜色的名字。在这个类中的常量,封装了用户系统的各个元素的颜色。
- 系统颜色
desktop | 桌面的背景颜色 | textText | 文本的前景颜色 |
---|
activeCaption | 标题的背景颜色 | textInactiveText | 非活动组件的文本颜色 |
activeCaptionText | 标题的文本颜色 | textHighlight | 高亮度文本的背景颜色 |
activeCaptionBorder | 标题文本的边框颜色 | textHighlightText | 高亮度文本的文本颜色 |
inactiveCaption | 非活动标题的背景颜色 | control | 组件的背景颜色 |
inactiveCaptionText | 非活动标题的文本颜色 | controlText | 组件的文本颜色 |
inactiveCaptionBorder | 非活动标题的边框颜色 | controlLtHightlight | 组件的浅高亮度颜色 |
Window | 窗口的背景 | controlHightlight | 组件的高亮度颜色 |
windowBorder | 窗口边框的颜色 | controlShadow | 组件的阴影颜色 |
windowText | 窗口内的文本颜色 | controlDkShadow | 组件的暗阴影颜色 |
menu | 菜单的背景颜色 | scrollbar | 滚动条的背景颜色 |
menuText | 菜单的文本颜色 | info | 帮助区文本的颜色 |
text | 文本的背景颜色 | infoText | 帮助区的文本颜色 |
- java.awt.Color 1.0
- Color(int r,int g,int b)
创建一个颜色对象。
参数: r 红色值(0-255)、g 绿色值(0-255)、b 蓝色值(0-255)。
- java.awt.Graphics 1.0
- Color getColor()
- void setColor(Color c)
获取或改变当前的颜色。所有后续的绘图操作都是用这个新颜色。
参数:c 新颜色
- java.awt.Graphics2D 1.2
- Paint getPaint()
- void setPaint(Paint p)
获取或设置这个图形环境的绘制属性。Color类实现了Paint接口。因此,可以使用这个方法将绘制属性设置为纯色。 - void fill(Shape s)
用当前的颜料填充该图形。
- java.awt.Component 1.0
- Color getBackground()
- void setBackground(Color c)
获取或设置背景颜色。
参数:c 新背景颜色 - Color getForeground()
- void setForeground(Color c)
获取或设置前景颜色。
参数:c 新前景颜色。
7.7 文本使用特殊字体
- 可以通过字体名(font dace name)指定一个字体。字体名由”Helvetica”这样的字体家族名(font family name)和一个可选的”Bold”后缀组成。
- 要想知道某台特定计算机上允许使用的字体,就需要调用GraphicsEnvironment类中的getAvailableFontFamilyNames方法。这个方法将返回一个字符型数组,其中包了所有可用的字体名。GraphicsEnvironment类描述了用户系统的图形环境,为了得到这个类的对象,需要调用静态的getLocalGraphicsEnvironment方法。
- 点数目是排版中普遍使用的表示字体大小的单位,每英寸包含72个点。
- 字体风格值:Font.PLAIN、Font.BOLD、Font.ITALIC、Font.BOLD+Font.ITALIC。
- 要想得到屏幕设备字体属性的描述对象,需要调用Graphics2D类中的getFontRenderContext方法。它将返回一个FontRenderContext类对象。
- 基线(baseline)是一条虚构的线。上坡度(ascent)是从基线到坡顶(ascenter)的距离。下坡度(descent)是从基线到坡顶(descenter)的距离。
- 行间距(leading)是某一行的坡底与其下一行的坡顶之间的空隙。字体的高度是连续两个基线之间的距离,它等于下坡度+行间距+上坡度。
- getStringBounds方法返回的矩形宽度是字符串水平方向的宽度。矩形的高度是上坡度、下坡度、行间距的总和,如果需要知道下坡度或行间距,可以使用Font类的getLineMetrics方法,这个方法将返回一个LineMetrics类对象。
- 为了能够获得中央的位置,可以使用getWidth()得到组件的宽度。使用bounds.getWidth()得到字符串的宽度。前者减去后者就是两侧应该剩余的空间,因此,每侧剩余的空间应该是这个差值的一般。高度也一样。
- java.awt.Font 1.0
- Font(String name,int style,int size)
创建一个字体对象。
参数: name 字体名。不是字体名(例如,“Helvetica Bold”),就是逻辑字体名(例如,“Serif”、“SansSerif”)。style 字体风格(Font.PLAIN、Font.BOLD、Font.ITALIC或Font.BOLD+Font.ITALIC)。size 字体大小(例如,12)。 - String getFontName()
返回字体名,例如,“Helvetica Bold”。 - String getFamily()
返回字体家族名,例如,“Helvetica”。 - String getName()
如果采用逻辑字体名创建字体,将返回逻辑字体,例如,“SansSerif”;否则,返回字体名。 - Rectangle2D getStringBounds(String s,FontRenderContext context) 1.2
返回包围这个字符串的矩形。矩形的起点为基线。矩形顶端的y坐标等于上坡度的负值。矩形的高度等于上坡度、下坡地和行间距之和。宽度等于字符串的宽度。 - LineMetrics getLineMetrics(String s,FontRenderContext context) 1.2
返回测定字符串宽度的一个线性metrics对象。 - Font deriveFont(int style) 1.2
- Font deriveFont(float size) 1.2
- Font deriveFont(int style,float size) 1.2
返回一个新字体,除给定大小和字体风格外,其余与原字体一样。
- java.awt.font.LineMetrics 1.2
- float getAscent()
返回字体的上坡度—从基线到大写字母顶端的距离。 - float getDescent()
返回字体的下坡度—从基线到坡底的距离。 - float getLeading()
返回字体的行间距—从一行文本底端到下一行文本顶端之间的空隙。 - float getHeight()
返回字体的总高度—两条文本基线之间的距离(下坡度+行间距+上坡度)。
- java.awt.Graphics 1.0
- Font getFont()
- void setFont(Font font)
获取或设置当前的字体。这种字体将被应用于后续的文本绘制操作中。
参数:font 一种字体。
- void drawString(String str,int x,int y)
参数:str 将要绘制的字符串。x 字符串开始的x坐标。y 字符串基线的y坐标。
- java.awt.Graphics 1.2
- FontRenderContext getFontRenderContext()
返回这个图形文本中,指定字体特征的字体绘制环境。 - void drawString(String str,float x,float y)
采用当前的字体和颜色绘制一个字符串。
参数:str 将要绘制的字符串。 x 字符串开始的x坐标。y 字符串基线的y坐标。
- javax.swing.JComponent 1.2
- FontMetrics getFontMetrics(Font f) 5.0
获取给定字体的度量。FontMetrics类是LineMetrics类的早先版。
- java.awt.FontMetrics 1.0
- FontRenderContext getFontRenderContext() 1.2
返回字体的字体绘制环境。
7.8 显示图像
- 使用Graphics类的drawImage方法将图像显示出来。
- java.awt.Graphics 1.0
- boolean drawImage(Image img,int x,int y,ImageObserver observer)
绘制一副非比例图像。注意:这个调用可能会在图像还没有绘制完毕就返回。
参数:img 将要绘制的图像。x 左上角的x坐标。y 左上角的y坐标。observer 绘制进程中以通告为目的的对象(可能为null)。 - boolean drawImage(Image img,int x,int y,int width,int height,ImageObserver observer)
绘制一副比例图像。系统按照比例将图像放入给定宽和高的区域。注意:这个调用可能会在图像还没有绘制完毕就返回。
参数:img 将要绘制的图像。x 左上角的x坐标。y 左上角的y坐标。width 描述图像的宽度。height 描述图像的高度。observer 绘制进程中以通告为目的的对象(可能为null)。 - void copyArea(int x,int y,int width,int height,int dx,int dy)
拷贝屏幕的一块区域。
参数:x 原始区域左上角的x坐标。y 原始区域左上角的y坐标。width 原始区域的宽度。 height 原始区域的高度。dx 原始区域到目的区域的水平距离。dy 原始区域到目的区域的数值距离。