Java学习笔记(5):图形程序设计

来源:互联网 发布:相亲软件有哪些 编辑:程序博客网 时间:2024/05/17 23:26
AWT(Abstract Window Toolkit):抽象窗口工具箱,一个用于基本GUI程序设计的类库。
Swing:一个用户界面库,基于AWT架构之上。


创建框架

顶层窗口(就是没有包含在其他窗口中的窗口)被称为框架。AWT中有一个Frame类,用于描述顶层窗口。这个类的Swing版本名为JFrame,它扩展于Frame类。

JFrame示例:

import javax.swing.*;public class SimpleFrameTest{public static void main(String[] args){SimpleFrame frame = new SimpleFrame();frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);}}class SimpleFrame extends JFrame{public SimpleFrame(){setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);}public static final int DEFAULT_WIDTH = 300;public static final int DEFAULT_HEIGHT = 200;}
默认情况下,框架的大小为0*0,这种框架没有意义。需要定义一个子类,使用构造器修改框架大小。

在每个Swing程序中,有两点技术需要强调:

首先,所有的Swing组件必须由事件调度进程(event dispatch thread)进行配置,线程将鼠标点击和键盘敲击控制转移到用户接口组件。

下面的代码片段是事件调度线程中的执行代码:

EventQueue.invokeLater(new Runnable(){public void run(){statements}});
接下来,定义一个用户关闭这个框架时的响应动作。对于这个程序而言,只让程序简单地退出即可。选择这个响应动作的语句是:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
在包含多个框架的程序中,不能在用户关闭其中的一个框架时就让程序退出。默认情况下,用户关闭窗口时只是将框架隐藏起来,而程序并没有终止。

框架起初是不可见的,程序员可以在框架第一次显示之前往其中添加组件。main方法调用框架的setVisible方法以显示框架。

在初始化语句结束后,main方法退出。此时并没有终止程序,终止的只是主线程。事件调度线程保持程序处于激活状态。


框架定位

  • setLocation和setBounds方法用于设置框架的位置。
  • setIconImage用于告诉窗口系统在标题栏、任务切换窗口等位置显示哪个图标。
  • setTitle用于改变标题栏的文字。
  • setResizable利用一个boolean值确定框架的大小是否允许用户改变。

框架属性
组件类的很多方法是以获取/设置这一对操作形式出现的,例如:
public String getTitle()public void setTitle(String title)
针对get/set约定有一个例外:对于类型为boolean属性,获取类方法由is开头,例如:
public boolean isLocationByPlatform()public void setLocationByPlatform(boolean b)

决定框架大小
对于专业应用程序,应检查屏幕分辨率,并根据其分辨率编写代码重置框架大小。
为得到屏幕大小,首先需要调用Toolkit类的静态方法getDefaultToolkit得到一个Toolkit对象。然后,调用getScreenSize方法,返回一个保存着屏幕高宽的Dimension对象:
Toolkit kit = Toolkit.getDefaultToolkit();Dimension screenSize = kit.getScreenSize();int screenWidth = screenSize.width;int screenHeight = screenSize.height;
下面,将框架大小设定为上面取值的50%,然后告知系统定位框架:
setSize(screenWidth / 2, screenHeight / 2);setLocationByPlatform(true);
另外,为框架设置一个图标:
Image img = kit.getImage("icon.gif");setIconImage(img);
提示:
1. 可以通过调用下列方法将框架设置为最大:
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
2. 牢记用户定位应用程序的框架位置、重置框架大小,并且在应用程序再次启动时恢复这些内容是不错的想法。
3. 如果编写一个使用多个屏幕的应用程序,应该利用GraphicsEnviroment和GraphicsDevice类获得显示屏幕的大小。
4. GraphicsDevice类允许在全屏模式下执行应用程序。

在组件中显示信息
在JFrame中有根面板、层级面板、内容面板和玻璃面板四个面板。
其中,根面板、层级面板和玻璃面板人们并不关心。Swing程序员最关心的是内容窗格(content pane)。
在设计框架时,使用下列代码将所有的组件添加到内容窗格中:
Container contentPane = frame.getContentPane();Component c = ...;contentPane.add(c);
绘制一个组件,需要定义一个扩展JComponent的类,并覆盖其中的paintComponent方法。
paintComponent方法有一个Grpahics类型的参数,保存着用于绘制图像和文本的设置。在Java中,所有的绘制都必须使用Graphics对象。
下面代码给出了如何创建一个能够进行绘制的组件:
class MyComponent extends JComponent{public void paintComponent(Graphics g){code for drawing}}
只要窗口需要重新绘图,事件处理器就会通告组件,从而引发执行所有组建的paintComponent方法。
如果需要强制刷新屏幕,可以调用repaint方法。它将引发采用相应配置的Graphics对象调用所有组件的paintComponent方法。
显示文本是一种特殊的绘图。在Graphics类中有一个drawString方法,调用语法格式为:
g.drawString(x, y)

2D图形
要想使用Java 2D库绘制图形,需要获得一个Graphics2D类对象。这个类是Graphics类的子类。
paintComponent方法会自动获得一个Graphics2D类对象,只需进行一次类型转换即可:
public void paintComponent(Graphics g){Graphics2D g2 = (Graphics2D)g;}
Java2D库包含描述直线、矩形、椭圆的类(全部实现了Shape接口):
  • Line2D
  • Rectangle2D
  • Ellipse2D
绘制图形时,首先创建一个实现了Shape接口的类的对象,然后调用Graphics2D类中的draw方法:
Rectangle2D rect = ...;draw(rect);
为了避免后缀和类型转换带来的麻烦,2D库设计者为每个图形类提供两个版本(float和double):
Rectangle2D.Float
Rectangle2D.Double
当创建Rectangle2D.Float对象时,应提供float型数值坐标,而创建Rectangle2D.Double对象时,应提供double型数值的坐标:
Rectangle2D.Float floatRect = new Rectangle2D.Float(10.0F, 25.0F, 22.5F, 20.0F);Rectangle2D.Double doubleRect = new Rectangle2D.Double(10.0, 25.0, 22.5, 20.0);
Rectangle2D方法的参数和返回值均为double类型。例如,即使Rectangle2D.Float对象存储float类型的宽度,getWidth方法也返回一个double值。
Rectangle类和Point类分别扩展于Rectangle2D类和Point2D类,并用整型存储矩形和点。
Rectangle2D和Ellipse2D对象很容易构造,需要给出:
  • 左上角的x和y坐标
  • 宽和高
有时候不知道左上角位置。经常得到的是矩形两个对焦点,但这两点不一定是左上角和右下角,不能直接这样构造矩形:
Rectangle2D rect = new Rectangle2D.Double(px, py, qx - px, qy - py); // ERROR
如果p不是左上角,矩形就为空。
处理方法:首先创建一个空矩形,然后调用setFrameFromDiagonal方法:
Rectangle2D rect = new Rectangle2D.Double();rect.setFrameFromDiagonal(px, py, qx, qy);
或者,如果已知顶点分别用Point2D类型的两个对象p和q表示,就应该这样调用:
rect.setFrameFromDiagonal(p, q);
构造椭圆时,通常可以知道椭圆的中心,宽和高。setFrameFromCenter方法使用中心点,但仍要给出四个顶点中的一个。
常用做法是:
Ellipse2D ellipse = new Ellipse2D.Double(centerX - width / 2, centerY - height / 2, width, height);
构造直线,需提供起点和终点,这两个点既可以使用Point2D对象表示,也可以用一对数值表示:
Line2D line = new Line2D.Double(start, end);Line2D line = new Line2D.Double(startX, startY, endX, endY);

颜色
使用Graphics2D类的setPaint方法可以为图形环境上所有后续的绘制操作选择颜色,例如:
g2.setPaint(Color.RED);g2.drawString("Warning!", 100, 100);
将draw替换为fill就可以用一种颜色填充一个封闭图形,例如:
Rectangle2D rect = ...;g2.setPaint(Color.RED);g2.fill(rect); // fills rect with red color
在java.awt.Color类中提供了13个预定义的常量,表示13种标准颜色:
BLACK, BLUE, CYAN, DARK_GRAY, GRAY, GREEN, LIGHT_GRAY, MAGENTA, ORANGE, PINK, RED, WHITE, YELLOW
可以通过红、绿和蓝三色成分来创建一个Color对象,范围0~255,调用格式为:
Color(int redness, int greenness, int blueness)
例:
g2.setPaint(new Color(0, 128, 128)); // a dull blue-greeng2.drawString("Welcome!", 75, 125);
注:如果使用Graphics对象,而不是Graphics2D对象,就需要使用setColor方法设置颜色。
要想设置背景颜色,就需要使用Component类中的setBackground方法:
MyComponent p = new MyComponent();p.setBackground(Color.PINK);
另外,还有一个setForeground方法,用来设定在组件上进行绘制时使用的默认颜色。
Color类中的brighter()方法和darker()方法分别加亮或变暗当前颜色。使用brighter方法也是加亮条目的好办法。希望达到耀眼的效果,可以使用三次:c.brighter().brighter().brighter()。
SystemColor类中预定义了很多类的名字,这个类中的常量,封装了用户系统的各个元素的颜色,例如:
p.setBackground(SystemColor.window)
它将把面板的背景颜色设定为用户界面上所有窗口使用的默认颜色。系统颜色desktop桌面背景颜色textText文本前景颜色activeCaption标题背景颜色textInactiveText非活动组件文本颜色activeCaptionText标题文本颜色textHighlight高亮度文本背景颜色activeCaptionBorder标题文本边框颜色textHighlightText高亮度文本文本颜色inactiveCaption非活动标题背景颜色control组件背景颜色inactiveCaptionText非活动标题文本颜色controlText组件文本颜色inactiveCaptionBorder非活动标题文本边框颜色controlLtHighlight组件浅高亮度颜色Window窗口背景controlHighlight组件高亮度颜色windowBorder窗口边框颜色controlShadow组件阴影颜色windowText窗口内文本颜色controlDkShadow组件暗阴影颜色menu菜单背景颜色scrollbar滚动条背景颜色menuText菜单文本颜色info帮助区文本的颜色text文字背景颜色infoText帮助区的文本颜色
为文本设定特殊字体
可以通过调用GraphicsEnviroment类中的getAvailableFontFamilyName方法返回计算机上允许使用的字体。这个方法返回一个字符型数组,包含了所有可用的字体名。为了得到GraphicsEnviroment类的对象,需要调用静态的getLocalGraphicsEnviroment方法,下面程序将打印出系统上所有字体名:
import java.awt.*;public class ListFonts{public static void main(String[] args){String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();for (String fontName : fontNames)System.out.println(fontName);}}
为了创建一个公共基准,AWT定义了五个逻辑(logical)字体名:
SansSerif
Serif
Monospaced
Dialog
DialogInput
这些字体将被映射到客户机上的实际字体。
另外,Sun JDK包含3种字体:Lucida Sans,Lucida Bright,Lucida Sans Typewriter。
要想使用某种字体绘制字符,必须首先利用指定的字体名、字体风格和字体大小来创建一个Font对象,例如:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
第三个参数是以点数目计算字体大小,每英寸包含72个点。
第二个参数可以给出字体风格(常规/加粗/斜体/加粗斜体):Font.PLAN,Font.BOLD,Font.ITALIC,Font.BOLD + Font.ITALIC。
可以通过从磁盘文件或URL读取TrueType或PostScript Type 1格式的字体文件,然后调用静态方法Font.createFont:
URL url = new URL("http://www.fonts.com/Wingbats.ttf");InputStream in = url.openStream();Font f1 = Font.createFont(font, TRUETYPE_FONT, in);
上面定义的字体为常规字体,大小为1.可以使用deriveFont方法得到希望大小的字体:
Font f = f1.deriveFont(14.0F);
注:deriveFont有两个重载版本,除了上述版本,还有一个版本接受一个int参数,用来设置字体风格,所有deriveFont(14)设置的是字体风格,不是大小。
下面代码使用系统中14号加粗的标准sans serif字体显示字符串“Hello, world”:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);g2.setFont(sansbold14);String message = "Hello, world!";g2.drawString(message, 75, 100);
接下来,将字符串绘制在面板中央,而不是任意位置。因此,需要知道字符串占据的宽和高的像素数量。这两个值取决于三个因素:
  • 使用的字体
  • 字符串
  • 绘制字体的设备
要想得到屏幕设备字体属性的描述对象,需要调用Graphics2D类中的getFontRenderContext方法。它将返回一个FontRenderContext类对象。可以直接将这个对象传递给Font类的getStringBounds方法:
FontRenderContext context = g2.getFontRenderContext();Rectangle2D bounds = f.getStringBounds(message, context);

getStringBounds方法返回包围字符串的矩形。这个矩形始于字符串的基线,矩形顶部的y坐标为负值。可以采用下面的方法获得字符串的宽度、高度和上坡度:
double stringWidth = bounds.getWidth();double stringHeight = bounds.getHeight();double ascent = -bounds.getY();
如果需要知道下坡度或行间距,可以使用Font类的getLineMetrics方法。这个方法返回一个LineMetrics类对象,获得下坡度和行间距的方法是:
LineMetrics metrics = f.getLineMetrics(message, context);float descent = metrics.getDescent();float leading = metrics.getLeading();
下面代码使用了所有这些信息,将字符串显示在包围它的组件中央:
FontRenderContext context = g2.getFontRenderContext();Rectangle2D bounds = f.getStringBounds(message, context);// (x, y) = top left corner of textdouble x = (getWidth() - bounds.getWidth()) / 2;double y = (getHeight() - bounds.getHeight()) / 2;// add ascent to y to reach the baselinedouble ascent = -bounds.getY();double baseY = y + ascent;g2.drawString(message, (int)x, (int)baseY);

注:若要在paintComponent方法外部计算布局图的尺度,应使用JComponent类的getFontMetrics方法,而后紧接着调用getFontRenderContext:
FontRenderContext context = getFontMetrics(f).getFontRenderContext();

图像
如果图像存储在本地文件中,就应该调用:
String filename = "...";Image image = ImageIO.read(new File(filename));
否则,应提供URL:
String urlname = "...";Image image = ImageIO.read(new URL(urlname));
如果图像不可用,read方法将抛出一个IOException。
可以使用Graphics类的的drawImage方法将图像显示出来:
public void paintComponent(Graphics g){...g.drawImage(image, x, y, null);}

原创粉丝点击