待更新
概述
在Java初期,包含了一个用于基本GUI
程序设计的类库,称为AWT (Abstract Window Toolkit)
。基本AWT
库采用将处理用户界面元素的任务为派给每个目标平台的本地GUI
工具箱的方式,由本地工具箱负责用户界面元素的创建和动作。
问题在于,虽然可以运行与多平台,但是不同平台的观感却很难统一。更坏的是,不同平台上的AWT
用户界面库中存在着不同的bug,因而在每个平台上都必须测试应用程序。
后来,创建了IFC
库,通过在空白窗口上绘制用户界面元素来统一在不同平台上的外观和动作。后来,经过合作完善,发展成了现在的Swing
库。
现在,Swing
是指“被绘制的”用户界面类,AWT
是指像事件处理这样的窗口工具箱的底层机制。
在编写Swing
程序时,可以为程序指定专门的观感。此外,还有Metal
这种独立于平台的观感。
创建框架
顶层窗口被称为框架(Frame)。在AWT
库中有一个称为Frame
的类,用于描述顶层窗口。在Swing
中这个库名为JFrame
。
绝大多数
Swing
组件都以“J”开头,例如JButton
、JFrame
等。
代码
import java.awt.*;
import javax.swing.*;
public class FrameTest {
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() { //分派线程
@Override
public void run() {
SimpleFrame frame = new SimpleFrame();
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE
); //关闭动作
frame.setVisible(true); //可见
}
});
}
}
class SimpleFrame extends JFrame{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public SimpleFrame(){ //300*200
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
}
讲解
定义了一个子类SimpleFrame
,构造器将框架大小设置为300*200像素。
在main
方法中,构造了一个SimpleFrame
对象使它可见。
所有的Swing
组件都必须由事件分派线程进行配置。
要定义一个用户关闭这个框架时的响应动作。
框架定位
setLocation
和setBounds
方法用于设置框架的位置。
setLocationByPlatform(true)
可以让窗口系统控制窗口的位置。
setIconImage
用于告诉窗口系统在标题栏、任务切换窗口等位置显示哪个图标。
setTitle
用于改变标题栏的文字。
setResizalbe
利用一个布尔值确定框架的大小是否允许用户改变。
框架属性
组件类的很多方法是通过获取/设置的方法对形式出现的。
这样获取/设置方法对被称为一种属性。属性包含属性名或类型。将get
或set
之后的第一个字母改为小写就可以得到相应的属性名。
确定合适的框架大小
如果没有明确指定大小,所有框架的默认值为0*0像素。对于专业应用程序来说,应该检查屏幕分辨率,并且根据其分辨率编写代码重置框架大小。
为了得到屏幕的大小,需要按照下列步骤操作。调用Toolkit
类的静态方法getDefaultToolkit
得到一个Toolkit
对象。然后调用getScreenSize
方法,这个方法以Dimension
对象的形式返回屏幕的大小。Dimension
对象同时用共有实例变量width
和height
保存屏幕的高度和宽度。
Toolkit kit = Toolkit.getDefaultToolkit();
// 获取系统工具箱
Dimension screenSize = kit.getScreenSize();
// 获取屏幕分辨率
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
setSize(screenWidth/2, screenHeight/2);
// 根据屏幕分辨率设置窗口
setLocationByPlatform(true);
// 由系统设置窗口位置
Image img;
// 新建Image对象
img = new ImageIcon("1.png").getImage();
setIconImage(img);
// 设置图标
这里有个小问题,我不知道怎么在macOS
中取出图片作为图标。
一些提示
- 如果框架中只包含标准的组件,如按钮和文本框,那么可以通过调用
pack
方法设置框架大小。框架将被设置为刚好能够放置所有组件的大小。在通常情况下,将程序的主框架尺寸设置为最大。
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
- 记住用户定位应用程序的框架位置、重置框架大小,并且在应用程序再次启动时回复这些内容是个不错的想法。
- 如果编写一个使用多个显示屏幕的程序,应该利用
GraphicsEnvironment
和Graphics Device
类获得显示屏幕的大小。 -
GraphicsDevice
类允许在全屏模式下执行应用程序。
完整示例
package SwingText;
import javax.swing.*;
import java.awt.*;
/**
* Create Date: 2017/3/10
* User: Wzh
* Project: Java_code
* Create For: Swing Test Program
*/
public class SizedFrameTest {
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new SizedFrame();
frame.setTitle("oujitsune");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class SizedFrame extends JFrame{
public SizedFrame(){
Toolkit kit = Toolkit.getDefaultToolkit(); // 获取系统工具箱
Dimension screenSize = kit.getScreenSize(); // 获取屏幕分辨率
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
setSize(screenWidth/2, screenHeight/2); // 根据屏幕设置窗口占四分之一
setLocationByPlatform(true); // 由系统设置窗口位置
Image img; // 新建Image对象
img = new ImageIcon("1.png").getImage();
setIconImage(img); // 设置图标
}
}
常用方法
java.awt.Component
boolean isVisible()
void setVisible(boolean b)
void setSize(int width, int height)
void setLocation(int x, int y)
void setBounds(int x, int y, int width, int height)
Dimension getSize()
void setSize(Dimension)
java.wat.Window
void toFront() // 置顶
void toBack() // 放在最后,并且重排所有可见窗口
boolean isLocationByPlatform()
void setLocationByPlatform(boolean b)
// 获取或设置locationByPlatform属性,由系统选择一个合适的位置
java.awt.Frame
boolean isResizable()
void setResizable(boolean b)
// 获取或设置resizable属性
String getTitle()
void setTitle(String s)
// 获取或设置Title属性
Image getIconImage()
void setIconImage(Image image)
// 获取或设置iconImage属性
boolean isUndecorated()
void setUndecorated(boolean b)
// 获取或设置undecorated属性,这个属性设置后,框架显示中将没有标题栏或关闭按钮这样的装饰。
int getExtendedState()
void setExtendedState(int state)
// 获取或设置窗口状态,为下列值之一
// Frame.NORMAL
// Frame.ICONIFIED
// Frame.MAXIMIZED_HORIZ
// Frame.MAXIMIZED_VERT
// Frame.MAXIMIZED_BOTH
java.awt.Toolkit
static Toolkit getDefaultToolkit()
// 获取默认工具箱
Dimension getScreenSize()
// 返回用户屏幕尺寸
javax.swing.ImageIcon
ImageIcon(String filename)
// 构造一个图标,将其图像存储在一个文件中
Image getImage()
// 获取该图标的图像
在组件中显示信息
在框架中显示信息可以将消息字符串直接绘制在框架中,但这不是一种好的编程习惯。在Java中,框架被设计为放置组件的一种容器,可以将菜单栏和其他的用户界面元素放置在其中。在通常情况下,应该在另一个组件上绘制信息。
JFrame
有四层面板,其中根面板、层级面板和玻璃面板是用来组织菜单栏和内容创客以及实现观感的。内容窗格才是最让人关心的。
在设计框架的时候,要使用下列代码将所有的组件添加到内容窗格中:
Container contentPane = frame.getContentPane();
Component c = ...;
contentPane.add(c);
paintComponent
方法有一个Graphics
类型的参数,这个参数保存着用于绘制图像和文本的设置,例如设置的字体或当前的颜色。在Java中,所有的绘制都必须使用Graphics
对象,其中包含了绘制图案、图像和文本的方法。
创建绘制组件
class Mycomponent extends JComponent{
puiblic void paintComponent(Graphics g){
// Code For Drwaing
}
}
无论何种原因,只要窗口需要重新绘图,事件处理器就会通告组件,从而引发执行所有组件的paintComponent
方法。
一定不要自己调用paintComponent
方法。在应用程序需要重新绘图的时候,这个方法将会自动地被调用,不要人为地干预这个自动的处理过程。
如果需要强制刷新屏幕,就需要调用repaint
方法。
paintComponent
方法只有一个Graphics
类型的参数。对于屏幕显示来说,Graphics
对象的度量单位是像素。
显示文本是一种特殊的绘图。在Graphics
类中有一个drawString
方法,调用的语法格式为:g.drawString(text, x, y)
完整示例
public class SizedFrameTest {
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new WordsFrame();
frame.setTitle("oujitsune");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class WordsFrame extends JFrame{
public WordsFrame(){
add(new WordsComponent()); // 添加组件
pack(); // 考虑组件大小调整窗口大小
}
}
class WordsComponent extends JComponent{
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public void paintComponent(Graphics g){ // 覆盖这个方法来描述如何绘制自己的程序
g.drawString("Hello world", MESSAGE_X, MESSAGE_Y);
}
public Dimension getPreferredSize(){ // 覆盖这个方法,返回首选宽度和高度
return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
}
常用方法
javax.swing.JFrame
Container getContentPane()
// 返回这个JFrame的内容窗格对象
Component add(Component c)
// 讲一个给定的组件添加到该框架的内容窗格中
java.awt.Component
void repaint()
// 重新绘制组件
Dimension getPreferredSize()
// 返回这个组件的首选大小
javax.swing.JComponent
void paintComponent(Graphics g)
// 覆盖这个方法来描述应该如何绘制自己的程序
javax.awt.Window
void pack()
// 调整窗口大小,要考虑到其组件的首选大小
2D绘图
使用Java 2D库绘制图形,需要获得一个Graphics 2D
类对象。这个类是Graphics
类的子类。paintComponent
方法会自动获得一个Graphics 2D
类的对象,我们只需要进行一次类型转换就可以了。
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
}
要想绘制图形,首先要创建一个实现了Shape
接口的类的对象,然后调用Graphics2D
类中的draw
方法。
在Java 2D库中,内部的很多浮点计算都采用单精度float
。因为集合计算的最终目的是要设置屏幕或打印机的像素,所以单精度完全可以满足要求了。然而有时候程序员处理float
不太方便,所以每个图形类都有两个版本,一个是节省空间的float
类型的坐标,另一个是double
类型的坐标。
除了构造图像对象时,一般不需要使用烦人的内部类。
矩形和椭圆
Rectangle2D
和Ellipse2D
类都是由公共超类RecktangularShape
继承来的。
构造时需要左上角位置和矩形的宽和高。
有时候并不知道左上角的位置,经常得到的是矩形的两个对角点,二这两个对角不一定是左上角和右下角,所以不能直接构造。
更好的方法是先创建一个恐惧小,然后调用setFrameFromDiagonal
方法。
在构造椭圆时通常可以知道椭圆的中心、宽和高,而不是外接矩形的顶点。setFrameFrontCenter
方法使用中心点,但仍然要给出四个顶点中的一个。因此,通常采用下列方式构造椭圆:
Line2D line = new Line2D.Double(start, end);
Line2D line = new Line2D.Double(startX, startY, ednX, endY);
完整实例
public class SizedFrameTest {
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new DrawFrame();
frame.setTitle("oujitsune");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class DrawFrame extends JFrame{
public DrawFrame(){
add(new DrawComponent());
pack();
}
}
class DrawComponent extends JComponent{
private static final int DEFAULT_WIDTH = 400;
private static final int DEFAULT_HEIGHT = 400;
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g; // 重置Graphics对象
double leftX = 100; // 顶点坐标
double topY = 100;
double width = 200; // 宽
double height = 150; // 高
Rectangle2D rect = new Rectangle2D.Double(leftX, topY, width, height);
g2.draw(rect);
Ellipse2D ellipse = new Ellipse2D.Double();
ellipse.setFrame(rect); // 设置大小
g2.draw(ellipse); // 绘图
g2.draw(new Line2D.Double(leftX, topY, leftX+width, topY+height));
double centerX = rect.getCenterX(); // 圆心
double centerY = rect.getCenterY();
double radius = 150; // 半径
Ellipse2D circle = new Ellipse2D.Double();
circle.setFrameFromCenter(centerX, centerY, centerX+radius, centerY+radius);
g2.draw(circle);
}
public Dimension getPreferredSize(){
return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
}
使用颜色
使用Graphics2D
类的setPaint
方法可以为图形环境上的所有后续的绘制操作选择颜色。
g2.setPaint(Color.RED);
g2.drawString("Warning!", 100, 100);
只需要将调用draw
替换为调用fill
就可以用一种颜色填充一个封闭图形的内部。
Rectangle2D rect = ...;
g2.setPaint(Color.RED);
g2.fill(rect);
要设置背景颜色需要使用Component
类中的setBackground
方法。Component
类是JComponent
类的祖先。
// 不知为何无效
JFrame frame = new DrawFrame(); frame.setBackground(Color.BLUE);
文本使用字体
有时希望选用不同的字体显示文本。人们可以通过字体名指定一种字体,字体名由字体家族名和一个可选的后缀组成。
需要知道某台特点计算机上允许使用的字体,就需要调用GraphicsEnvironment
类中的getAvailableFontFamilyNames
方法。这个方法返回一个字符型数组,包含所有可用的字体名。GraphicisEnvironment
类描述了用户系统的图形环境,为了得到这个类的对象,需要调用静态的getLocalGraphicsEnvironment
方法。
创建字体对象
AWT
中定义了五个逻辑字体名,这些字体将会被映射到客户机上的时机字体。要想使用某种字体绘制字符,必须首先利用指定的字体名、字体风格和字体大小来创建一个Font
对象。
Font sansbold14 = new Font(“SansSerif”, Font.BOLD, 14);
构造器第二个参数可以指定字体的风格(常规、加粗、斜体或加粗体)。
Font.PLAIN
Font.BOLD
Font.ITALIC
Font.BOLD + Font.ITALIC
读取字体文件
可以读取TrueType
或PostScriot Type 1
格式的字体文件。这需要一个字体输入流,通常从磁盘文件或者URL读取,然后调用静态方法Font.createFont
:
URL url = new URL("http://www.font.com/Wingbats.ttf");
InputStream in = url.openStream();
Font f1 = Font.createFont(Font.TRUETYPE_FONT, in);
初始添加的字体为常规字体,大小为1。使用deriveFont
方法得到希望大小的字体:Font f = f1.deriveFont(14.0f);
deriveFont
方法有两个重载版本。一个(float
版本)设置大小,一个(int
版本)设置风格。所以f.deriveFont(14)
设置的是字体风格,而不是大小。
字体使用
使用字体只需调用它Graphics
实例的setFont
方法。
要想得到屏幕设备字体属性的描述对象,需要调用Graphics2D
类的getFontRenderContext
方法。它将会返回一个FontRenderContext
对象。可以直接将这个对象传递给Font
类的getStringBounds
方法:
FontRenderContext context = g.getFontRenderContext();
Rectangle2D bounds = sansbold14.getStringBounds(message, context);
getStringBounds
方法将会返回包围这个字符串的矩形。
常用方法
java.atw.Font
Font(String name, int style, int size)
// 创建一个新字体对象
String getFontName()
// 返回字体名
String getFamily()
// 返回字体家族名
String getName()
// 如果采用逻辑字体名创建字体,将返回逻辑字体
Rectangle2D getStringBounds(String s, FontRenderContext context)
// 返回包围这个字符串的矩形
// 这个矩形起点为极限,顶端y坐标等于上坡度的负值
// 矩形的高度等于上坡度、下坡度和行间距之和。宽度等于字符串的宽度
LineMetrics getLineMetrics(String s, FontRenderContext context)
// 返回测定字符串宽度的一个线性metrics对象
Font deriveFont(int style)
Font deriveFont(float size)
Font deriveFont(int style, float size)
// 返回一个新字体,除给定大小和字体风格之外和原字体一样
java.awt.font.LineMetrics
float getAscent()
// 返回字体的上坡度——从基线到大写字母顶端的距离
float getDescent()
// 返回字体的下坡度——从基线到坡底的距离
float getLeading()
// 返回字体的行间距——从一行文本地段到下一行文本顶端之间的空隙
float getHeight()
// 返回字体的总高度——两条文本极限之间的距离
java.awt.Graphics
Font getFont()
void setFont(Font font)
void drawString(String str, int x, int y)
// 采用当前字体和颜色绘制一个字符串
java.awt.Graphics2D
FontRenderContext getFontRenderContext()
// 返回这个图形文本中,指定字体特征的字体绘制环境
void drawString(String str, int x, int y)
javax.swing.JComponent
FontMetrics getFontMetrics(Font f)
// 获取给定字体的度量,LineMetrics类的前身
java.awt.FontMetrics
FontRenderContext getFontRenderContext()
// 返回字体的字体绘制环境
显示图像
可以读取本地图像。读取有很多种方法,比如之前使用过的ImageIcon
类。