JAVA的绘图功能 JAVA的绘图功能非常丰富,绘图包括字体、颜色、图形,以下我们将分技术专题来讲。 一、关于JAVA的绘图机制。 JAVA中的任何一个图形组件,小到文本框、标签,大到一个FRAME,一个DIALOG,都有一个专门负责显示其界面的函数,这个函数名称是固定的:paint,它的原型为: public void paint(Graphics g) { …… } 每当组件大小、位置、组件内容发生变化时,该函数即负责生成新的图形界面显示。由于该函数可以被子类继承,因此,继承的子类有能力修改该函数。如果子类中 没有出现该函数,则表示其行为完全继承自父类。则不管是组件中是否添加了新的内容,是否发生了大小的改变,是否发生了位移,父类都要有一个专门的线程,来 负责描绘变化以后的组件界面。 下面就paint的运行机制给大家讲两个例子: 例子代码 在窗体上显示窗口的大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class
GraExp1 extends
JFrame
{
public
GraExp1()
{
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(new
Dimension(400,300));
show();
}
public
void
paint(Graphics g)
{
Dimension d=getSize();
int
x=(int
)d.getWidth();
int
y=(int
)d.getHeight();
g.clearRect(0,0,x,y);
String s=Integer.toString(x);
String s1=Integer.toString(y);
g.drawString(s,100,100);
g.drawString(","
+s1,140,100);
}
public
static
void
main(String[] args)
{
GraExp1 ge=new
GraExp1();
}
}
程序分析: (1)通过一次对比说明(把黑体字部分去掉,保留黑体字部分)默认的paint函数包含两部分:一是擦除,二是重画。 两次运行的图如下: 无黑体字部分的显示 有黑体字部分的显示 很明显可以看出,第一个图形是在原来画板的基础上进行了覆盖式描绘,第二个图形是擦除后的重新描绘。 (2)通过展示运行过程,可以看到,每次修改了窗体大小,都会自动调用paint函数一次。如果在实例化以后的界面中想重画,必须让代码出现在事件中。 (3)JAVA中的坐标轴: 例子二代码 在窗体中包含了添加的组件的paint示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class
GraExp2 extends
JFrame
{
Container c;
JTextField tf=new
JTextField(10);
public
GraExp2()
{
c=getContentPane();
c.setLayout(new
FlowLayout());
tf.setText("世界,你好!"
);
c.add(tf);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(new
Dimension(400,300));
show();
}
public
void
paint(Graphics g)
{
super.paint(g);
Dimension d=getSize();
int
x=(int
)d.getWidth();
int
y=(int
)d.getHeight();
g.clearRect(50,50,200,200);
String s=Integer.toString(x);
String s1=Integer.toString(y);
g.drawString(s,100,100);
g.drawString(","
+s1,140,100);
}
public
static
void
main(String[] args)
{
GraExp2 ge=new
GraExp2();
}
}
程序分析: (1)对比三种情况:无paint函数、有但无super.paint(g)调用、有paint函数并且也有对父类的paint函数调用,说明paint函数由父类自动维护,并且如果子类一旦重载该函数,必须自己去维护所有的界面显示。 后两者的运行图: 第二个图形中我故意让clearRect方法覆盖了文本框的一部分,其实其完整的操作画板的步骤是:首先清除窗体内容,并在窗体上真实再现文本框内容(注 意,这一部分由父类完成),其次是调用clearRect方法,清除指定部分的窗体内容;最后是在合适的位置输出窗口大小。(这两部分是由子类完成的)。 可以对比,在第一副图中,由于未调用父类的paint方法,所以添加到Frame中的文本框未被显示出来。 二、设置画笔颜色 1、颜色常识 任何颜色都是三原色组成(RGB),JAVA中支持224位彩色,即红绿蓝色分量可取值介于0..255之间。下面首先学习一个JAVA中的类Color Color中的常量: public final static Color black=new Color(0,0,0); public final static Color blue=new Color(0,0,255); ….. Color的构造函数: public Color(int r,int g,int b); 使用举例:如果想构造一个灰色对象,则用下面的句子: Color gray=new Color(205,205,205); 2、设置画笔颜色语法 g.setColor(color); //color是一个Color对象 举例三代码 演示颜色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class
GraExp3 extends
JFrame
{
public
GraExp3()
{
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(new
Dimension(400,300));
show();
}
public
void
paint(Graphics g)
{
Dimension d=getSize();
int
x=(int
)d.getWidth();
int
y=(int
)d.getHeight();
Color color=Color.white;
g.setColor(color);
g.fillRect(0,0,x,y);
g.setColor(new
Color(255,0,0));
g.drawString("红色是三原色中的首色,代表激情。"
,50,50);
g.setColor(new
Color(0,255,0));
g.drawString("绿色是三原色中的次色,代表安逸。"
,50,80);
g.setColor(new
Color(0,0,255));
g.drawString("蓝色是三原色中的末色,代表忧郁。"
,50,110);
}
public
static
void
main(String[] args)
{
GraExp3 ge=new
GraExp3();
}
}
程序运行图如下: 注意:每修改一次颜色它影响的就是下面所有的绘图语句,一直影响到再次碰到setColor函数才以新的颜色代替。 3、使用JColorChooser组件选择颜色 JAVA中有一个已经定义好的选色器,通过简单的语法我们就可以将该窗口调出来,从其中选择自己喜欢的颜色。下面的这个例子就是通过颜色选取器选取颜色,并将选择到的颜色做为窗体的背景色。 (1)JColorChooser简介 JColorChooser组件的showDialog()方法让用户从弹出的窗口中选择一个颜色,并传给Color对象。其调用语法如下: color=JColorChooser.showDialog(this,”选色”,color); 第一个参数指定调用选色器的父窗体,第二个参数指定选色器窗口标题,最后一个为接收颜色的颜色对象。 (2)举例四代码: 更改窗体背景色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class
GraExp4 extends
JFrame
{
Container c;
JButton btn=new
JButton("选背景色"
);
Color color=new
Color(200,200,200);
public
GraExp4()
{
c=getContentPane();
c.setLayout(new
FlowLayout());
c.add(btn);
btn.addActionListener(new
ActionListener(){
public
void
actionPerformed(ActionEvent e)
{
color=JColorChooser.showDialog(null,"请选择你喜欢的颜色"
,color);
if
(color==null
) color=Color.lightGray;
c.setBackground(color);
c.repaint();
}
}
);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(new
Dimension(400,300));
show();
}
public
static
void
main(String[] args)
{
GraExp4 ge=new
GraExp4();
}
}
以下是程序运行单击按钮以后的JColorChooser图和更改后的窗体图: 程序注解: 其实这里并没有什么特殊的技巧,只是告诉你一个组件的简单用法。需要注意的是调用窗体后对返回的值必须进行检验,如果用户没有选择任何颜色,而是单击了撤消直接退出,程序员在自己的代码里必须为color指定一默认值,要不然在设置背景色时会产生错误。 4、如何将一个图形(以文件存在,如JPG或者GIF)画到窗体的画布中 其实放置图形到画板中实际就是调用了画板的drawImage函数。其大致思路如下: 首先获取一个ImageIcon对象,这个对象将会从指定的文件中读取相关图象信息,它支持GIF和JPG、BMP等基本图象格式。语法如下: ImageIcon icon=new ImageIcon(GraExp5.class.getResource("1.gif")); 获取到图象的图标以后,就可以从图标中获取到绘制到画板上的实际需要的图象: Image img=icon.getImage(); 有了这个图象对象,我们就可以用画板的drawImage函数画图了。 g.drawImage(img,0,0,null); 中间两个参数是图象绘制时在画板的起始点坐标。 (1)举例五代码 将图形文件绘制到窗体中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class
GraExp5 extends
JFrame
{
ImageIcon icon;
Image img;
public
GraExp5()
{
icon=new
ImageIcon(GraExp5.class.getResource("1.gif"
));
img=icon.getImage();
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(new
Dimension(400,300));
show();
}
public
void
paint(Graphics g)
{
g.drawImage(img,0,0,null
);
}
public
static
void
main(String[] args)
{
GraExp5 ge=new
GraExp5();
}
}
程序分析: 需要注意的是,图象文件所在的位置应该和该类放在同一个目录中,这样不至于出错。 程序的运行结果图如下: 6、系统扩展,综合应用,如何为一个窗体设置背景图片。 要为一个窗体添加背景图片,必须知道绘制JComponent组件的过程。Swing轻量组件的绘制是组件和组件UI代表合作的结果。 代码六 设置窗体背景图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class
GraExp6 extends
JFrame
{
CInstead c1=new
CInstead();
Container c;
JLabel lbl1=new
JLabel("姓名:"
);
JLabel lbl2=new
JLabel("密码:"
);
JTextField tf1=new
JTextField(10),
tf2=new
JTextField(10);
public
GraExp6()
{
setContentPane(c1);
c=getContentPane();
c.setLayout(new
FlowLayout(FlowLayout.LEFT));
c.add(lbl1);
c.add(tf1);
c.add(lbl2);
c.add(tf2);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(new
Dimension(400,300));
show();
}
public
static
void
main(String[] args)
{
GraExp6 ge=new
GraExp6();
}
class
CInstead extends
JPanel
{
ImageIcon icon;
Image img;
public
CInstead()
{
icon=new
ImageIcon(GraExp6.class.getResource("1.gif"
));
img=icon.getImage();
}
public
void
paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(img,0,0,null
);
}
}
}
界面运行图如下: 程序分析: JComponent.paint先绘制组件,然后绘制组件的边框,再绘制组件的子组件。调用的顺序确保组件、边框和子组件都是可视的。如果组件有一个 UI代表,则JComponent.paintComponent调用该代表的Update方法,该方法为不透明组件擦除背景,然后绘制组件。 CInstead是一个不透明的组件,如果重载paint方法,其背景图是无法被擦除的,因此,即使更新了组件的所有包含组件,在界面上是看不到的。所以必须重载paintComponent方法,在绘制子组件前先擦除背景。 对双缓存组件,paint方法负责把组件绘制到屏外缓存中,然后把屏外缓存拷贝到组件的屏上代表中,正因为如此,我们不建议为Swing组件重载paint,如果需要重新定义如何绘制组件,那么就重载paintComponent()。 7、用可获取的字体、样式、字号修饰文字 (1)函数说明: 字形类Font用于规范组件所使用的字形大小、样式和字体等。其构造函数: public Font(String name,int style,int size); name表示本地可获取字体名称 style表示字体样式,包含Font.PLAIN,Font.BOLD,Font.ITALIC三种,分别对应平体、加粗和斜体。 一个有用的方法用来获取本地可用字体 GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fa=ge.getAvailableFontFamilyNames(); 通过从绘图环境中获取到本地可用的字体名数组。 (2)代码七 用指定的字型约束来显示窗体上的字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class
GraExp7 extends
JFrame
{
CInstead c1=new
CInstead();
Container c;
String[] zt;
String[] zx={
"平体"
,"加粗"
,"斜体"
}
;
String name="Serif"
;
int
type=0;
int
size=12;
JLabel lbl1=new
JLabel("字体:"
);
JLabel lbl2=new
JLabel("字形:"
);
JLabel lbl3=new
JLabel("字号:"
);
JLabel lbl=new
JLabel("测试用字abcABC123"
);
JComboBox cb1,
cb2=new
JComboBox(zx);
JTextField tf1=new
JTextField(25),
tf2=new
JTextField(10);
public
GraExp7()
{
//获取可用字体名称数组
GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
zt=ge.getAvailableFontFamilyNames();
cb1=new
JComboBox(zt);
setContentPane(c1);
c=getContentPane();
c.setLayout(new
FlowLayout(FlowLayout.LEFT));
c.add(lbl1);
c.add(cb1);
c.add(lbl2);
c.add(cb2);
c.add(lbl3);
c.add(tf1);
lbl.setPreferredSize(new
Dimension(200,60));
lbl.setForeground(Color.red);
c.add(lbl);
cb1.addItemListener(new
ItemListener(){
public
void
itemStateChanged(ItemEvent event)
{
int
state=event.getStateChange();
name=(String) event.getItem();
setCustomFont(name,type,size);
}
}
);
cb2.addItemListener(new
ItemListener(){
public
void
itemStateChanged(ItemEvent event)
{
int
state=event.getStateChange();
String s=(String) event.getItem();
if
(s.equals("平体"
)){
type=Font.PLAIN;
}
else
if
(s.equals("加粗"
)){
type=Font.BOLD;
}
else
{
type=Font.ITALIC;
}
setCustomFont(name,type,size);
}
}
);
tf1.addKeyListener(new
KeyAdapter(){
public
void
keyPressed(KeyEvent e)
{
int
c=e.getKeyCode();
if
(c==10)
{
String s=tf1.getText();
size=Integer.parseInt(s);
setCustomFont(name,type,size);
}
}
}
);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(new
Dimension(400,300));
show();
}
public
static
void
main(String[] args)
{
GraExp7 ge=new
GraExp7();
}
private
void
setCustomFont(String name,int
type,int
size)
{
lbl.setFont(new
Font(name,type,size));
lbl.revalidate();
}
class
CInstead extends
JPanel
{
ImageIcon icon;
Image img;
public
CInstead()
{
icon=new
ImageIcon(CInstead.class.getResource("1.gif"
));
img=icon.getImage();
}
public
void
paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(img,0,0,null
);
}
}
}