10.3在组件中显示信息
在java中窗体实际上设计为组件的容器
在JFrame中有四层窗格。
其中的根窗格,层级窗格,玻璃窗格人们并不太关心它们要用来组织菜单栏和内容窗格以及实现观感。
Swing程序员最关心的是内容窗格
添加到窗体的所有组件都会自动添加到内容窗格中
在java中所有的绘制都必须通过Graphics对象完成,其中包含了绘制图案,图像和文本的方法。
示例代码:
public class SwingPractice
{
public static void main(String[] args) {
//事件分派线程
EventQueue.invokeLater(()->
{
//设置一个窗体
var frame = new SimpleFrame();
//窗体的标题为Three
frame.setTitle("Three");
//设置窗体退出响应动作
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗体可见
frame.setVisible(true);
});
}
}
class SimpleFrame extends JFrame
{
//构造方法
public SimpleFrame() {
//获得用户屏幕的尺寸
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int height=screenSize.height;
int width=screenSize.width;
//设置窗体尺寸
setSize(width/2,height/2);
//设置窗体图标
setIconImage(new ImageIcon("E:\\Java代码练习文件\\src\\cn\\SwingPractice\\派大星.gif").getImage());
//创建容器 组件
var pane= new NotePane();
//添加容器 组件给窗体
add(pane());
}
}
class NotePane extends JComponent
{
public static final int DEFAULT_X = 75;
public static final int DEFAULT_Y=200;
public void paintComponent(Graphics g) {
//设置文本的字体
g.setColor(Color.GREEN);
//设置一段文本文字在窗体中显示
g.drawString("Three can do it",DEFAULT_X,DEFAULT_Y);
}
}
无论何种原因,只要窗口需要重新绘制,事件处理器就会通知组件,从而引发执行所有组件的paintComponent方法。不要人为调用这个方法
当用户扩大窗口时,或者极小化时窗口又恢复到大小时,就会引发绘制,如果用户弹出另一个窗口,并且这个窗口覆盖了一个已有的窗口,然后这个窗口消失,此时被覆盖的那个窗口已被破坏,需要重新绘制
如果需要强制重新绘制屏幕,需要调用repaint方法而不是paintComponent方法,repaint方法将引发采用适当的配置的Graphics对象调用所有组件的paintComponent方法
从java版本1.0以来,Graphics类就包含绘制直线,矩形,椭圆等方法。但是这些绘制图形的能力十分有限,我们将使用JavaGraphics2D库的图形类
要想使用Java2D库绘制图形,需要获得Graphics2D类的一个对象,这个类是Graphics的一个子类,从Java1.2版本以来,paintComponent等方法会自动接受一个Graphics2D类的对象。只需要使用一个类的强制转换
public void paintComponent(Graphics g)
{
Graphics2D g2=(Graphics2D)g;
...
}
Java2D库提供了表示直线,矩形。椭圆的类
LInea2D
Rectangle2D
Ellipse2D
这些类都实现了shape接口 java2D库支持复杂的图形,例如圆弧,二次曲线,三次曲线和通用路径
要想绘制一个图形,首先要创建一个实现了shape接口的类的对象,然后调用Graphics2D类的draw方法例如:
Rectangl2D rec=...;
g2.draw(rec);
当构造一个Rectangle2D.Float对象时,提供Float参数
当构造一个Rectangle2D.Double对象时,提供double参数
例如:
var floatRectangle=new Rectangle2D.Float(10.0F,25.0F,22.5F,20.0F);
var floatRectangle=new Rectangle2D.Float(10.0,25.0,22.5,22.0);
构造参数表示矩形的左上角位置,宽和高
Rectangle2D和Ellipse2D类都是由公共超类RectangularShape继承来的
RectangularShape类定义了20多个关于图形操作的通用方法
常用的有
getWidth //返回闭合矩形的宽
getHeight //返回闭合矩形的高
getCenterX //返回闭合矩形的中心坐标X
getCenterY //返回闭合矩形的中心坐标Y
Rectangle2D 和Ellipse2D很容易构造。需要指定
- 左上角的x和y坐标
- 宽和高
对于椭圆,这些表示外接矩形。例如:
var e=new Ellipse2D.Double(150,200,100,50);
这会构造一个椭圆,它的外接矩形左上角位于(150,200),宽为100,高为50.
构造椭圆时候通常会知道椭圆的中心,宽和高,而不是外接矩形的四角顶点(它们甚至不在椭圆上)。
setFrameFromeCenter方法使用中心点,但还是要给出四个顶点中的一个因此通常以下方式构造圆:
var ellipse=new Ellipse2D.Double(centerX-width/2,centerY-height/2,width,height);
想要构造一条直线需要提供起点和终点,这两个点既可以用Point2D对象表示也可以用一对值表示
var line=new Line2D.Double(start,end);
或者
var line=new Line2D.Double(startX,startY,endX,endY);
代码示例:
package cn.SwingPractice;
import javafx.scene.shape.Circle;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Arrays;
/**
* @ClassName: SwingPractice
* @Description: swing
* @Author Three
* @Date 2021/1/18
*/
public class SwingPractice
{
public static void main(String[] args) {
//事件分配线程
EventQueue.invokeLater(()-> {
//创建视图
var frame = new SimpleFrame();
//创建窗口的标题
frame.setTitle("Three");
//让程序关闭的响应动作
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//让窗口可见
frame.setVisible(true);
});
}
}
class SimpleFrame extends JFrame
{
public SimpleFrame(){
//获取用户屏幕尺寸
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
//高
int height=screenSize.height;
//宽
int width=screenSize.width;
setSize(width/2,height/2);
//设置窗体的图标
setIconImage(new ImageIcon("E:\\Java代码练习文件\\src\\cn\\SwingPractice\\派大星.gif").getImage());
//设置窗体大小
setSize(Toolkit.getDefaultToolkit().getScreenSize().width/2,Toolkit.getDefaultToolkit().getScreenSize().height/2);
//将一个给定的组件添加到该窗体的内容格中,并返回这个组件
add(new Pane2D());
}
}
class Pane2D extends JComponent
{
public static final double DEFAULT_X = 100;
public static final double DEFAULT_Y = 100;
public static final double DEFAULT_HEIGHT = 200;
public static final double DEFAULT_WIDTH = 150;
public void paintComponent(Graphics g)
{
var g2=(Graphics2D) g;
//draw a rectangle(矩形)
Rectangle2D.Double rec = new Rectangle2D.Double(DEFAULT_X,DEFAULT_Y,DEFAULT_HEIGHT,DEFAULT_WIDTH);
//设置颜色为绿色
g2.setPaint(Color.red);
//在窗体中插入这个矩形
g2.draw(rec);
//draw a ellipse(椭圆)
var ellipse = new Ellipse2D.Double(rec.getX(),rec.getY(),rec.getWidth(),rec.getHeight());
//置指定的矩形值位置的 Shape框架矩形尺寸。
ellipse.setFrame(rec);
//设置颜色
g2.setPaint(Color.GREEN);
//用当前颜色填充一个封闭图形
g2.fill(ellipse);
//把ellipes插入到窗体中
g2.draw(ellipse);
//draw a line(线)
var line = new Line2D.Double(DEFAULT_X,DEFAULT_Y,DEFAULT_X+DEFAULT_HEIGHT,DEFAULT_Y+DEFAULT_WIDTH);
//设置线颜色为黑色
g2.setPaint(Color.black);
//把线插入到窗体中
g2.draw(line);
var line2 = new Line2D.Double(DEFAULT_X, DEFAULT_Y + DEFAULT_WIDTH, DEFAULT_Y + DEFAULT_HEIGHT, DEFAULT_Y );
g2.setPaint(Color.black);
g2.draw(line2);
//draw a circle
//返回闭合举行的中心
double centerX = rec.getCenterX();
double centerY = rec.getCenterY();
//半径
double radius = 150;
var circle = new Ellipse2D.Double();
//设置框架用指定的中心点坐标和角点坐标
circle.setFrameFromCenter(centerX,centerY,centerX+radius,centerY+radius);
//设置颜色为蓝色
g2.setPaint(Color.BLUE);
//设置背景色
g2.setBackground(new Color(0,128,128));//breakPoint!
//把椭圆插入到窗体
g2.draw(circle);
}
}
方法名 | 方法作用 |
---|---|
double getCenterX() | 返回闭合矩形的中心坐标X |
double getCenterY() | 返回闭合矩形的中心坐标Y |
double getMinX() | 返回闭合矩形的最小坐标值X |
double getMinY() | 返回闭合矩形的最小坐标值Y |
double getMaxX() | 返回闭合矩形的最大坐标值X |
double getMaxY() | 返回闭合矩形的最大坐标值Y |
double getWidth | 返回闭合矩形的宽 |
double getHeight() | 返回闭合矩形的高 |
double getX() | 返回闭合矩形的坐标值X |
double getY() | 返回闭合矩形的坐标值Y |
Rectangle2D.Double(double x,double y,double w,double h) | 利用给定的左上角,宽和高构造一个矩形 |
Ellipse2D.Double(double x,double y,double w,double y) | 利用给定的左上角,宽和高构造一个外接矩形,椭圆 |
Point2D.Double | 利用给定的坐标构造一个点 |
Line2D.Double(Point2D start,Point2D end) | 使用给定的起点和终点,构造一条直线 |
Line2D.Double(double startX,double StartY,double endX,double endY) | 使用给定的起点和终点,构造一条直线 |
使用Graphics2D类的setPaint方法可以为图形上下文上的所有后续的绘制操作选择颜色
例如:
g2.setPaint(Color.RED);
g2.drawString("Three",100,100);
可以用一种颜色填充一个封闭图形(例如:矩形,椭圆)的内部,为此只需要调用draw替换为fill
Rectangle2D rect=...;
g2.setPaint(Color.RED);
g2.fill(rect);
!!要想用多种颜色绘制,就需要选择一个颜色,绘制图形,再选择另外一种颜色,再绘制图形
Color类用于定义颜色在java.awt.Color类中定义了13个预定义的常量,它们分别表示13种标准颜色。
BLACK,BLUE,CYAN,DARK_GRAY,GRAY,GREEN,LIGHT_GRAY,
MAGENTA,ORANGE,PINK,RED,WHITE,YELLOW
可以提供三色分量来创建Color对象,从而指定一个定制颜色,红,绿,和蓝三种颜色,取值为0~255之间的整数:
g2.setPaint(new Color(0,128,128));
g2.drawString("Welcome!",75,125);
要设置背景颜色需要使用Compon类中的setBackground方法。Component类是JComponent类的祖先
var component=new MyComponent();
componenet.setBackground(Color.PINK);
另外,还有一个setForeground方法,它用来指定在组件上进行绘制时使用的默认颜色
方法名 | 方法作用 |
---|---|
Color(int r,int g,int b) | 用给定的红,绿,蓝分量取值范围(0~255)创建一个颜色对象 |
Paint getPaint() | 获取设置这个图形上下文的绘制属性 |
void setPaint() | 绘制颜色 |
void fill(Shapes) | 用当前颜色填充图形 |
Color getForeground | 返回前景颜色 |
Color getBackground | 返回背景颜色 |
void setForeground | 设置前景颜色 |
void setBackground | 设置背景颜色 |
在本章开始的”Hello World“程序中默认用的字体显示了一个字符串。有时候你可能希望用不同的字体显示文本,可以通过字体名,指定一种字体。字体名由字体族名,和一个可选的后缀组成,例如,"Helvetica"和”Helvetiaca Bold“都属于名为”Helvetica“字体族的名字。
要想知道特定计算机上的可用的字体,可以调用GraphicsEnvironment类的getAvailableFontFamilyName方法,这个方法将返回以一个字符串数组其中包含了所有可用的字体名,GraphicsEnvironment类描述了用户系统的图形上下文,为了的到这儿类的对象需要调用静态的 getLocal GraphicsEnvironment方法。下面这个程序会打印所有的字体名:
import java.awt.*;
public class ListFonts
{
public static void main(String[] args)
{
String[] fontNames=GraphicsEnviroment.getLocalGrapgicsEnvironment().getAvailableFontFamilyName();
for(String FontName:fontNames)
{
System.out,println(FontName);
}
}
}
AWT定义了5个逻辑字体名
- SansSerif
- Serif
- Monospaced
- Dialog
- DialogInput
要想使用某种字体绘制字符,必须首先创建一个Font类的对象,需要指定字体名,字体风格和字体大小,下面是构造一个Font对象的例子:
var sansbold14=new Font("SansSerif",Font.BOLD,14);
第三个参数是字体大小第二个参数是字体的风格(常规,加粗,斜体或者粗斜体);
- Font.PLAIN
- Font.BOLD
- Font.ITALIC
- Font.BOLD+Font.ITALIC
常规字体的字体大小为1点,可以使用deriveFont方法获得所需字体的大小
//字体名称为SansSerif,加粗14号
var sansbold14=new Font("SansSerif",Font.BOLD,14);
g2.setFont(sansbold14);
var message="Hello World"!;
g2.drawString(message,75,100);
要将字符串居中,而不是任意位置,需要知道字符串占据的宽和高的像素数,这两个值取决于下面的三个因素:
- 使用的字体(在这个例子中为sans serif,加粗,14点)
- 字符串(在这个例子中为”hello world“)
- 绘制字体的设备(在这个例子中为用户屏幕)
要想得到表示屏幕设备字体属性的对象,需要调用Graphics2D类中的getFontRenderContext方法,它将返回一个FontRenderContext类的对象。可以将这个对象传递给Font类的getStringBounds 方法:
FontRenderContext context=g2.getFontRenderContext();
Reectangle2D bounds=sansbold14.getStringBounds(message,context);
getStringBounds方法返回包围字符串的矩形
术语 基线(baseline)是一条虚构的线,例如,字母”e“所在的底线。上坡度(ascent)是从基线到坡顶(ascent)的距离(坡顶是”b“”k“或大写字母的上面部分)。下坡度(descent)是从基线到坡底(descenter)的距离(坡底是”p“或”g“等字母的下面部分)
行间距是某一行的坡底与其下一行的坡顶之间的空隙(这个术语源于打字机分隔行的间隔带)。字体的高度是连续两个基线之间的距离,它等于下坡度+行间距+上坡度。
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();
代码示例:
package cn.SwingPractice;
import javafx.scene.shape.Circle;
import javax.swing.*;
import java.awt.*;
import java.awt.font.LineMetrics;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Arrays;
/**
* @ClassName: SwingPractice
* @Description: swing
* @Author Three
* @Date 2021/1/18
* @Version 1.0
*/
public class SwingPractice
{
public static void main(String[] args) {
//事件分配线程
EventQueue.invokeLater(()-> {
//创建视图
var frame = new SimpleFrame();
//创建窗口的标题
frame.setTitle("Three");
//让程序关闭的响应动作
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//让窗口可见
frame.setVisible(true);
});
}
}
class SimpleFrame extends JFrame
{
public SimpleFrame(){
//获取用户屏幕尺寸
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
//高
int height=screenSize.height;
//宽
int width=screenSize.width;
setSize(width/2,height/2);
//设置窗体的图标
setIconImage(new ImageIcon("E:\\Java代码练习文件\\src\\cn\\SwingPractice\\派大星.gif").getImage());
//设置窗体大小
setSize(Toolkit.getDefaultToolkit().getScreenSize().width/2,Toolkit.getDefaultToolkit().getScreenSize().height/2);
//将一个给定的组件添加到该窗体的内容格中,并返回这个组件
add(new NotePane());
}
}
class NotePane extends JComponent
{
public static final int DEFAULT_Y = 75;
public static final int DEFAULT_X=200;
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
//设置为绿色
g.setColor(Color.GREEN);
var font = new Font("SansSerif", Font.ITALIC + Font.BOLD, 34);
setFont(font);
var message = "Three can do it!";
//获得宽度测量工具
var metr=g.getFontMetrics(font);
//获得上下文的字体获取FontRenderContext类 ,而FontRenderContext的作用是正确测量文本所需的信息容器
var fontRender=g2.getFontRenderContext();
//获得这个字符串的宽度的一个度量对象
LineMetrics lineMetrics = font.getLineMetrics(message, fontRender);
//获得包围这个字符串的测量工具
Rectangle2D stringBounds = font.getStringBounds(message, fontRender);
//字符串宽
double width = stringBounds.getWidth();
//字符串的高
double height = lineMetrics.getHeight();
//获得包围这个字符串的宽度
double rectWidth = stringBounds.getWidth();
//获得包围这个字符串的高度
double rectHeight = stringBounds.getHeight();
//坐标X
double x = (getWidth() - width) / 2;
//坐标Y
double y = (getHeight() - height )/ 2;
//上坡度
double ascent = -lineMetrics.getAscent();
//下坡度
double descent = lineMetrics.getDescent();
//插入字符串
g.drawString(message,(int)x,(int)y);
// set a rectangle
var recttangle=new Rectangle2D.Double();
recttangle.setRect(x,y+ascent,stringBounds.getWidth(),stringBounds.getHeight());
g2.draw(recttangle);
//set a Line
var line = new Line2D.Double(x,y,x+width,y);
g2.draw(line);
}
}
class Pane2D extends JComponent
{
public static final double DEFAULT_X = 100;
public static final double DEFAULT_Y = 100;
public static final double DEFAULT_HEIGHT = 200;
public static final double DEFAULT_WIDTH = 150;
public void paintComponent(Graphics g)
{
var g2=(Graphics2D) g;
//draw a rectangle(矩形)
Rectangle2D.Double rec = new Rectangle2D.Double(DEFAULT_X,DEFAULT_Y,DEFAULT_HEIGHT,DEFAULT_WIDTH);
//设置颜色为绿色
g2.setPaint(Color.red);
//在窗体中插入这个矩形
g2.draw(rec);
//draw a ellipse(椭圆)
var ellipse = new Ellipse2D.Double(rec.getX(),rec.getY(),rec.getWidth(),rec.getHeight());
//置指定的矩形值位置的 Shape框架矩形尺寸。
/*ellipse.setFrame(rec);*/
//设置颜色
g2.setPaint(Color.GREEN);
//用当前颜色填充一个封闭图形
g2.fill(ellipse);
//把ellipes插入到窗体中
g2.draw(ellipse);
//draw a line(线)
var line = new Line2D.Double(DEFAULT_X,DEFAULT_Y,DEFAULT_X+DEFAULT_HEIGHT,DEFAULT_Y+DEFAULT_WIDTH);
//设置线颜色为黑色
g2.setPaint(Color.black);
//把线插入到窗体中
g2.draw(line);
var line2 = new Line2D.Double(DEFAULT_X, DEFAULT_Y + DEFAULT_WIDTH, DEFAULT_Y + DEFAULT_HEIGHT, DEFAULT_Y );
g2.setPaint(Color.black);
g2.draw(line2);
//draw a circle
//返回闭合举行的中心
double centerX = rec.getCenterX();
double centerY = rec.getCenterY();
//半径
double radius = 150;
var circle = new Ellipse2D.Double();
//设置框架用指定的中心点坐标和角点坐标
circle.setFrameFromCenter(centerX,centerY,centerX+radius,centerY+radius);
//设置颜色为蓝色
g2.setPaint(Color.BLUE);
//设置背景色
g2.setBackground(new Color(0,128,128));//breakPoint!
//把椭圆插入到窗体
g2.draw(circle);
}
}
方法名 | 方法作用 |
---|---|
Font(String name,int style,int size) | 创建一个新字体对象,第一个参数是字体名,第二个是字体的风格例如加粗,第三个是字体的大小 |
String getFontName() | 获得字体名 |
String getFamily() | 获得字体族名 |
String getName() | 如果采用逻辑字体就获得逻辑字体名,否则获得字体名 |
Rectangle2D getStringBounds(String s,FontRenderContext context) | 返回包围这个字符串的矩形,矩形的起点为基线。矩形顶端的y坐标,等于上坡度的负值。矩形的高度等于上坡度,下坡度和行距之和,宽度等于字符串的宽度 |
LineMetrics getLineMetrics(String s,FontRenderContxt context) | 返回确定字符串宽度的一个度量对象 |
Font deriveFont(int style) | 返回一个新字体用给定的字体风格 |
Font deriveFont(float size ) | 返回一个字体大小的字体 |
Font deriveFont(int style,float size ) | 返回一个有给定的大小和字体风格 |
float getAscent() | 获得字体的上坡度———从基线到大写字母顶端的距离 |
float getdescent() | 获得字体的下坡度———从基线到坡底的距离 |
float getLeading() | 获得字体的行间距———从一行文本低端到下一行文本顶端之间的空隙 |
float getHeight() | 获得字体的高度———两条文本基线之间的距离(下坡度+行间距+上坡度) |
FontRenderContext getFontRenderContxt() | 获得这个图形上下文中指定的字体特征的字体绘制上下文) |
void drawString(String str,float x,float y) | 采用当前的字体和颜色绘制一个字符串) |
FontMetrics getFontMetrics(Font f) | 获得给定字体的字体度量对象,FontMetrics类是LineMetrics类的前身 |
FontRrenderConttext() | 返回字体绘制上下文 |
显示图像
可以使用 ImageIcon类从文件读取图像
Image image=new ImageIcon(filename).getImage;
现在变量image包含了一个封装了的图像数据的对象的引用,可以使用Graphics类的drawImage方法显示这个图象
public void paintComponent(Graphics g) { ... g.draw(image,x,y,null); }
可以在进一步,在一个窗口中平铺显示图像,采用paintComponent方法实现平铺显示。首先在左上角显示图像的一个副本,然后使用使用copyArea调用将其复制到整个窗口:
for(int i=0;i*imageWidth<=getWidth();i++)
{
for(int j=0;j*imageHeight<=getHeight();j++)
{
if(i+j>0)
{
g.copyArea(0,0,imageWidth,imageHeight,i*imageWidth,j*imageHeight);
}
}
}
方法名 | 方法作用 |
---|---|
boolean drawImage(Image img,int x,int y,ImageObserver observer) | 绘制一个不缩放或者缩放的图像 |
boolean drawImage(Image img,int x,int y,ImageObserver observer) | 注意!这个调用可能会在图像绘制完毕前就返回。向image Observer对象通知绘制进展。这在很久以前是一个很有用的特性,不过现在只需要传递null作为观察者就可以了 |
void copyArea(int x,int y,int width,int height,init dx,int dy) | 复制屏幕的一个区域,dx和dy是原始区域到目标区域的距离 |