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

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-green
g2.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 text
double x = (getWidth() - bounds.getWidth()) / 2;
double y = (getHeight() - bounds.getHeight()) / 2;

// add ascent to y to reach the baseline
double 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);
}

猜你喜欢

转载自blog.csdn.net/angel_lys/article/details/8590275
今日推荐