Step By Step(Java 2D图形篇<一>)

    本篇将主要为您介绍Java的2D图形编程技术,然而在示例代码中还将会涉及一些简单的Swing知识,我们会尽可能的将2D中的方法进行归类和抽取,以便使您的关注点始终保持在Java2D上。该篇还会和本系列中其他篇章的风格保持一致,只是介绍必要的关键概念,更多的技术点是通过已经良好分类的有代表性的示例代码来为您展示和说明。
    1.    形状绘制:
    在Java 2D中提供了下面几个基本形状:
    1)    Line2D
    2)    Rectangle2D
    3)    RoundRectangle2D
    4)    Ellipse2D
    5)    Arc2D
    6)    QuadCurve2D
    7)    CubicCurve2D
    8)    GeneralPath
    上面列出的对象均实现了Shape接口,因此可以作为Graphics2D类的draw方法的参数来绘制不同的图形。在实际的应用中,我们都是通过构造他们的内部类(均继承自对应的外部类)并返回该外部类的方式获取一个形状对象的实例,如:
    Rectangle2D floatRect = new Rectangle2D.Float(1F,10F,2.5F,2.5F);
    Rectangle2D doubleRect = new Rectangle2D.Double(1,10,2.5,2.5);
    1)    绘制Line2D和Rectangle2D:
    这两个形状是所有形状中最为基本也最容易理解的两个形状,那么我们就从这里下手小试牛刀。

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Rectangle And Line");
4 f.setSize(360, 300);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 //在Swing中,所有的自绘操作都是通过重载各个组件的paint和paintComponent方法
15 //来实现的,如果组件部分被遮盖或整个被隐藏,在重新bringToFront的时候,repaint
16 //方法会被Swing的EDT(Event Dispatching Thread)调用,从而导致该方法被调用。
17 @Override
18 public void paintComponent(Graphics g) {
19 super.paintComponent(g);
20 paintRectangle(g);
21 paintLine(g);
22 }
23 //绘制一条直线
24 private void paintLine(Graphics g) {
25 Graphics2D g2 = (Graphics2D) g;
26 g2.setPaint(Color.gray);
27 int x = 100;
28 int y = 75;
29 g2.draw(new Line2D.Double(x, y, 250, 195));
30 }
31 //绘制一个矩形边框,填充一个矩形
32 private void paintRectangle(Graphics g) {
33 Graphics2D g2d = (Graphics2D) g;
34 g2d.setColor(new Color(212, 212, 212));
35 g2d.draw(new Rectangle2D.Float(10, 15, 90, 60));
36 g2d.setColor(new Color(31, 21, 1));
37 g2d.fill(new Rectangle2D.Float(250, 195, 90, 60));
38 }
39 }

    对于上例paintRectangle方法中的Graphics2D.draw和Graphics2D.fill方法,可以分别替换为g2d.drawRect(10,15,90,60) 和 g2d.fillRect(250,195,90,60),他们的结果是完全一致的。然而在《Java极富客户端效果开发》中,推荐使用后者,原因是通用的draw方法会由于在绘制前需要做一些和类型判断相关的工作,因此会影响效率。而后者由于直接调用和形状相关的方法,如drawRect和fillRect,这样在绘制时已经非常清楚当前的形状为矩形,见如下比较代码

 1     private void paintTest(Graphics g) {
2 final int LINE_X = 100;
3 final int RECT_X = 200;
4 final int TEXT_X = 250;
5 final int BAD_Y = 60;
6 final int GOOD_Y = 160;
7 final int ITERATIONS = 1000;
8 Graphics2D g2d = (Graphics2D) g;
9 long startTime, endTime, totalTime;
10 g.setColor(Color.WHITE);
11 g.fillRect(0, 0, getWidth(), getHeight());
12 g.setColor(Color.BLACK);
13
14 g.drawString("Bad vs. Good Primitive Rendering", 50, 20);
15 g.drawString("(" + ITERATIONS + " iterations)", 100, 35);
16 g.drawString("Bad: ", 10, BAD_Y + 30);
17 g.drawString("Good: ", 10, GOOD_Y + 30);
18 // Bad line
19 Shape line = new Line2D.Double(LINE_X, BAD_Y, LINE_X + 50, BAD_Y + 50);
20 startTime = System.nanoTime();
21 for (int i = 0; i < ITERATIONS; ++i) {
22 g2d.draw(line);
23 }
24 endTime = System.nanoTime();
25 totalTime = (endTime - startTime) / 1000000;
26 System.out.println("bad line = " + totalTime);
27 g.drawString(totalTime + " ms", LINE_X, BAD_Y + 70);
28
29 // Good line
30 startTime = System.nanoTime();
31 for (int i = 0; i < ITERATIONS; ++i) {
32 g.drawLine(LINE_X, GOOD_Y, LINE_X + 50, GOOD_Y + 50);
33 }
34 endTime = System.nanoTime();
35 totalTime = (endTime - startTime) / 1000000;
36 System.out.println("good line = " + totalTime);
37 g.drawString(totalTime + " ms", LINE_X, GOOD_Y + 70);
38 // Bad rect
39 Shape rect = new Rectangle(RECT_X, BAD_Y, 50, 50);
40 startTime = System.nanoTime();
41 for (int i = 0; i < ITERATIONS; ++i) {
42 g2d.fill(rect);
43 }
44 endTime = System.nanoTime();
45 totalTime = (endTime - startTime) / 1000000;
46 System.out.println("bad rect = " + totalTime);
47 g.drawString(totalTime + " ms", RECT_X, BAD_Y + 70);
48
49 // Good rect
50 startTime = System.nanoTime();
51 for (int i = 0; i < ITERATIONS; ++i) {
52 g.fillRect(RECT_X, GOOD_Y, 50, 50);
53 }
54 endTime = System.nanoTime();
55 totalTime = (endTime - startTime) / 1000000;
56 System.out.println("good rect = " + totalTime);
57 g.drawString(totalTime + " ms", RECT_X, GOOD_Y + 70);
58 }

    在组件的paintComponent方法中直接调用该方法可以看到运行结果。
    2)    绘制Ellipse2D和RoundRectangle2D:

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Ellipse and RoundRectangle");
4 f.setSize(360, 300);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 g.setColor(Color.red);
18 paintOval(g);
19 paintRoundRectangle(g);
20 }
21 //绘制一个椭圆
22 private void paintOval(Graphics g) {
23 Graphics2D g2 = (Graphics2D)g;
24 g2.draw(new Ellipse2D.Float(5, 15, 50, 75));
25 }
26 //绘制一个圆形边角的矩形
27 private void paintRoundRectangle(Graphics g) {
28 Graphics2D g2 = (Graphics2D)g;
29 RoundRectangle2D rr = new RoundRectangle2D.Float(100, 10, 80, 30, 15, 15);
30 //获取当前的Color属性并保存到临时变量中,更新该属性,在使用完之后,
31 //用该临时变量恢复到原有的Color属性,这种方式是比较通用的一种技巧。
32 //在很多其他的类库中也会经常被用到。
33 Color oldColor = g2.getColor();
34 g2.setColor(Color.blue);
35 g2.fill(rr);
36 g2.setColor(oldColor);
37 }
38 }

    3)    绘制园(drawOval)和弧形:
    之前的两个例子都是通过调用Graphics2D.draw方法绘制Shape接口的实现类,下面的示例将使用不同的方法来绘制另外两个基本形状--圆形和弧形。

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Circle");
4 f.setSize(360, 300);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 g.setColor(Color.red);
18 paintCircle(g);
19 paintArc(g);
20 }
21 //画圆
22 private void paintCircle(Graphics g) {
23 Graphics2D g2 = (Graphics2D)g;
24 //没有提供drawCircle方法,通过将椭圆的width和heigh参数设置为等长,
25 //即可获得一个圆形。
26 g2.drawOval(5, 15, 75, 75);
27 }
28 private void paintArc(Graphics g) {
29 Graphics2D g2 = (Graphics2D) g;
30 int x = 50;
31 int y = 70;
32 g2.setStroke(new BasicStroke(8.0f));
33 //缺省填充为饼图
34 g2.fillArc(x, y, 200, 200, 90, 135);
35 //还可以通过Arc2D构造函数中的最后一个参数来定义不同的填充类型
36 Color oldColor = g.getColor();
37 g.setColor(Color.blue);
38 g2.fill(new Arc2D.Double(200, 30, 200, 200, 90, 135,Arc2D.CHORD));
39 g.setColor(oldColor);
40 }
41 }

    4)    绘制多边形:

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Polygon");
4 f.setSize(360, 300);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 g.setColor(Color.red);
18 paintPolygon(g);
19 paintGeneralPath(g);
20 }
21 // 绘制多边形
22 private void paintPolygon(Graphics g) {
23 Polygon p = new Polygon();
24 for (int i = 0; i < 5; i++) {
25 p.addPoint((int) (100 + 50 * Math.cos(i * 2 * Math.PI / 5)),
26 (int) (100 + 50 * Math.sin(i * 2 * Math.PI / 5)));
27 }
28 g.drawPolygon(p);
29 }
30 //通过GeneralPath的方式绘制多边形
31 private void paintGeneralPath(Graphics g) {
32 Random generator = new Random();
33 Point2D[] points = new Point2D[6];
34 for (int i = 0; i < 6; i++) {
35 double x = generator.nextDouble() * getWidth();
36 double y = generator.nextDouble() * getHeight();
37 points[i] = new Point2D.Double(x, y);
38 }
39
40 g.setColor(Color.red);
41 Graphics2D g2 = (Graphics2D) g;
42 GeneralPath s = new GeneralPath();
43 s.moveTo((float) points[0].getX(), (float) points[0].getY());
44 for (int i = 1; i < points.length; i++)
45 s.lineTo((float) points[i].getX(), (float) points[i].getY());
46 s.closePath();
47 g2.draw(s);
48 }
49 }

    5)    绘制Rectangle的3D边框:
    可以想象一下Swing中的JButton控件,在缺省属性下,其显示效果为有凸出感的Rectangle。该效果在技术上的实现还是比较简单和通用的,见下例:

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("3D Rect");
4 f.setSize(360, 300);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 g.setColor(Color.red);
18 paint3DRect(g);
19 }
20 // 绘制和填充3D效果的Rectangle边框
21 private void paint3DRect(Graphics g) {
22 int thickness = 4;
23 //draw3DRect和fill3DRect函数的最后一个参数决定绘制的
24 //3D效果是raised还是sunk,我们常见的button控件的3D效
25 //果是raised。如果最后一个参数是true,则为raised。
26 g.fill3DRect(200, 10, 80, 30, true);
27 for (int i = 1; i <= thickness; i++)
28 g.draw3DRect(200 - i, 10 - i, 80 + 2 * i - 1, 30 + 2 * i - 1, true);
29
30 g.fill3DRect(200, 50, 80, 30, false);
31 for (int i = 1; i <= thickness; i++)
32 g.draw3DRect(200 - i, 50 - i, 80 + 2 * i - 1, 30 + 2 * i - 1, true);
33 }
34 }

    6)    绘制二次曲线和三次曲线:
    该示例代码中,左侧显示的是二次曲线,右侧显示的是三次曲线,中间用黑色的分隔线分隔。

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Curve Demo");
4 f.setSize(360, 300);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 paintQuadCurve(g);
18 //画分割线,其中左边是二次曲线,右边是三次曲线
19 int x = getWidth() / 2;
20 g.setColor(Color.BLACK);
21 g.drawLine(x, 0, x, getHeight());
22 paintCubicCurve(g);
23 }
24 // 绘制二次曲线
25 private void paintQuadCurve(Graphics g) {
26 Random generator = new Random();
27 Point2D[] points = new Point2D[3];
28 //二次曲线的x坐标均小于getWidth()/2,因此可以显示在JPanel左侧。
29 for (int i = 0; i < 3; i++) {
30 double x = generator.nextDouble() * getWidth() / 2;
31 double y = generator.nextDouble() * getHeight();
32 points[i] = new Point2D.Double(x, y);
33 }
34 g.setColor(Color.red);
35 Graphics2D g2 = (Graphics2D) g;
36 for (int i = 0; i < points.length; i++) {
37 double x = points[i].getX() - 10 / 2;
38 double y = points[i].getY() - 10 / 2;
39 g2.fill(new Rectangle2D.Double(x, y, 10, 10));
40 }
41 Shape s = new QuadCurve2D.Double(points[0].getX(), points[0].getY(),
42 points[1].getX(), points[1].getY(), points[2].getX(),
43 points[2].getY());
44 g2.draw(s);
45 }
46
47 // 绘制三次曲线
48 private void paintCubicCurve(Graphics g) {
49 Random generator = new Random();
50 Point2D[] points = new Point2D[4];
51 //三次曲线的x坐标均大于getWidth()/2,因此可以显示在JPanel右侧。
52 for (int i = 0; i < 4; i++) {
53 double x = generator.nextDouble() * getWidth() / 2 + getWidth() / 2;
54 double y = generator.nextDouble() * getHeight();
55 points[i] = new Point2D.Double(x, y);
56 }
57 g.setColor(Color.red);
58 Graphics2D g2 = (Graphics2D) g;
59 for (int i = 0; i < points.length; i++) {
60 double x = points[i].getX() - 10 / 2;
61 double y = points[i].getY() - 10 / 2;
62 g2.fill(new Rectangle2D.Double(x, y, 10, 10));
63 }
64 Shape s = new CubicCurve2D.Double(points[0].getX(), points[0].getY(),
65 points[1].getX(), points[1].getY(), points[2].getX(),
66 points[2].getY(), points[3].getX(), points[3].getY());
67 g2.draw(s);
68 }
69 }

    2.    区域(Area):
    Java 2D支持4中几何图形区域计算方式,分别是add、substract、intersect和exclusiveOr。
    add:组合区域包含了所有位于第一个区域或第二个区域内的点;
    substract:组合区域包含了所有位于第一个区域内的点,但是不包括任何位于第二个区域内的点;
    intersect:组合区域既包含了所有位于第一个区域内的点,又包含了所有位于第二个区域内的点;
    exclusiveOr:组合区域包含了所有位于第一个区域内,或者是位于第二个区域内的所有点,但是这些点不能同时位于两个区域内。
    下面的示例代码绘制了两个区域的四种不同组合方式。

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Area Demo");
4 f.setSize(360, 300);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 //横向和纵向各画一条分隔线,将JPanel的区域划分4个等份。
18 //然后在4个区间中画出四种不同的Area组合方式
19 int x = getWidth() / 2;
20 int y = getHeight() / 2;
21 g.setColor(Color.black);
22 g.drawLine(x, 0, x, getHeight());
23 g.drawLine(0, y, getWidth(), y);
24 paintArea(g);
25 }
26 private void paintArea(Graphics g) {
27 //1. 以add的方式组合两个Area,并绘制在JPanel的左上角
28 Shape sOne = new Ellipse2D.Double(40, 20, 80, 80);
29 Shape sTwo = new Rectangle2D.Double(60, 40, 80, 80);
30 Area areaOne = new Area(sOne);
31 Area areaTwo = new Area(sTwo);
32 areaOne.add(areaTwo);
33 Graphics2D g2 = (Graphics2D)g;
34 g2.setPaint(Color.orange);
35 g2.fill(areaOne);
36 g2.setPaint(Color.black);
37 g2.draw(areaOne);
38 //2. 以subtract的方式组合两个Area,并绘制在JPanel的右上角
39 Shape sThree = new Ellipse2D.Double(40 + getWidth()/2, 20, 80, 80);
40 Shape sFour = new Rectangle2D.Double(60 + getWidth()/2, 40, 80, 80);
41 Area areaThree = new Area(sThree);
42 Area areaFour = new Area(sFour);
43 areaThree.subtract(areaFour);
44 g2.setPaint(Color.orange);
45 g2.fill(areaThree);
46 g2.setPaint(Color.black);
47 g2.draw(areaThree);
48 //3. 以intersect的方式组合两个Area,并绘制在JPanel的左下角
49 Shape sFive = new Ellipse2D.Double(40, 20 + getHeight()/2, 80, 80);
50 Shape sSix = new Rectangle2D.Double(60, 40 + getHeight()/2, 80, 80);
51 Area areaFive = new Area(sFive);
52 Area areaSix = new Area(sSix);
53 areaFive.intersect(areaSix);
54 g2.setPaint(Color.orange);
55 g2.fill(areaFive);
56 g2.setPaint(Color.black);
57 g2.draw(areaFive);
58 //4. 以exclusiveOr的方式组合两个Area,并绘制在JPanel的右下角
59 Shape sSeven = new Ellipse2D.Double(40 + getWidth()/2, 20 + getHeight()/2, 80, 80);
60 Shape sEight = new Rectangle2D.Double(60 + getWidth()/2, 40 + getHeight()/2, 80, 80);
61 Area areaSeven = new Area(sSeven);
62 Area areaEight = new Area(sEight);
63 areaSeven.exclusiveOr(areaEight);
64 g2.setPaint(Color.orange);
65 g2.fill(areaSeven);
66 g2.setPaint(Color.black);
67 g2.draw(areaSeven);
68 }
69 }

    3.    笔划(Strokes):
    Graphics2D类的draw操作通过使用当前选定的笔划来绘制一个形状的边界。在默认情况下,笔划是一条宽度为1像素的实线。可以通过setStroke方法来选定不同的笔划。这里我们可以通过使用Java 2D已经提供的BasicStroke类来帮助我们实现自定义笔划的功能。
    1)    设置笔划的宽度,缺省为一个像素;
    2)    当宽度为多个像素时,需要设置笔划的端头采用何种风格绘制:平头样式、圆头样式、方头样式,如果两条线相交时,采用何种方式的交叉风格:斜连接、园连接和斜肩连接;
    3)    设置虚线笔划的dash模式。
    见以下示例:

 1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Strokes Demo");
4 f.setSize(600, 600);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 g.setColor(Color.red);
18 g.drawLine(0, getHeight()/3, getWidth(), getHeight()/3);
19 g.drawLine(0, getHeight() * 2 /3, getWidth(), getHeight() * 2/3);
20 paintThickStroke(g);
21 paintGradientStroke(g);
22 paintDashPatternStroke(g);
23 }
24 //绘制指定宽度的笔划
25 private void paintThickStroke(Graphics g) {
26 Graphics2D g2d = (Graphics2D) g;
27 float strokeThickness = 5.0f;
28 BasicStroke stroke = new BasicStroke(strokeThickness);
29 g2d.setStroke(stroke);
30 g2d.setColor(Color.blue);
31 g2d.draw(new Rectangle(20, 20, 200, 100));
32 }
33 //绘制带有渐变着色的笔划
34 private void paintGradientStroke(Graphics g) {
35 Graphics2D g2 = (Graphics2D) g;
36 double x = 15, y = getHeight() / 3 + 50, w = 70, h = 70;
37 Ellipse2D e = new Ellipse2D.Double(x, y, w, h);
38 GradientPaint gp = new GradientPaint(75, 75, Color.white, 95, 95, Color.gray, true);
39 // 用渐变着色来填充
40 g2.setPaint(gp);
41 g2.fill(e);
42 // 实心颜色的笔划
43 e.setFrame(x + 100, y, w, h);
44 g2.setPaint(Color.black);
45 g2.setStroke(new BasicStroke(8));
46 g2.draw(e);
47 // 渐变颜色的笔划
48 e.setFrame(x + 200, y, w, h);
49 g2.setPaint(gp);
50 g2.draw(e);
51 }
52 //绘制带有dash模式和线段端头风格的笔划
53 private void paintDashPatternStroke(Graphics g) {
54 Graphics2D g2 = (Graphics2D) g;
55 float dash[] = { 10.0f };
56 //param 1: 笔划的宽度
57 //param 2: 端头样式,他是CAP_BUTT、CAP_ROUND、CAP_SQUARE三种样式中的一个
58 //param 3: 连接样式,他是JOIN_BEVEL、JOIN_MITER和JOIN_ROUND三种样式中的一个
59 //param 4: 用度数表示的角度,如果小于这个角度,斜尖连接将呈现为斜连接
60 //param 5: 虚线笔划的填充部分和空白部分交替出现的一组长度
61 //param 6: 位于笔划起始点前面的这段长度被假设为已经应用了该虚线模式
62 //在该例子中,可以将最后一个参数从0调整到10,以观察显示差异
63 g2.setStroke(new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash, 3f));
64 g2.setPaint(Color.blue);
65 Rectangle r = new Rectangle(20, getHeight() * 2 / 3 + 20, 200, 100);
66 g2.draw(r);
67 }
68 }

    4.    着色:
    当填充一个形状的时候,该形状的内部就上了颜色。使用setPaint方法,把颜色的样式设定为一个实现了Paint接口的类的对象。Java 2D API提供了三个这样的类:
    1)    Color: 实现了Paint接口,使用单色填充形状;
    2)    GradientPaint: 通过在两个给定的颜色值之间进行渐变,从而改变使用的颜色;
    3)    TexturePaint: 用一个图像重复的对一个区域进行着色。

  1     public class MyTest extends JPanel {
2 public static void main(String args[]) {
3 JFrame f = new JFrame("Paint Demo");
4 f.setSize(600, 600);
5 f.add(new MyTest());
6 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
7 f.setLocationRelativeTo(null);
8 f.setVisible(true);
9 }
10 public MyTest() {
11 setBackground(Color.white);
12 setForeground(Color.white);
13 }
14 @Override
15 public void paintComponent(Graphics g) {
16 super.paintComponent(g);
17 g.setColor(Color.red);
18 g.drawLine(0, getHeight() / 4, getWidth(), getHeight() / 4);
19 g.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
20 g.drawLine(0, getHeight() * 3 / 4, getWidth(), getHeight() * 3 / 4);
21 paintRadialGradient(g);
22 paintGradient(g);
23 paintGradientRectangle(g);
24 paintTexture(g);
25 }
26 // 绘制放射性渐变
27 private void paintRadialGradient(Graphics g) {
28 Graphics2D g2 = (Graphics2D) g;
29 double radius = getWidth() / (2 * 4);
30 Paint p = new RadialGradientPaint(new Point2D.Double(radius, radius),
31 (float) radius, new float[] { 0.0f, 1.0f }, new Color[] {
32 new Color(6, 76, 160, 127),
33 new Color(0.0f, 0.0f, 0.0f, 0.8f) });
34 g2.setPaint(p);
35 g2.fillOval(0, 0, (int) radius * 2 - 10, (int) radius * 2 - 10);
36 }
37 // 绘制普通渐变
38 private void paintGradient(Graphics g) {
39 Paint paint = new GradientPaint(0, getHeight() / 4, Color.red,
40 (float) getWidth() / 4, (float) getHeight() / 2, Color.blue);
41 Graphics2D g2 = (Graphics2D) g;
42 g2.setPaint(paint);
43 Ellipse2D circle = new Ellipse2D.Double(0, getHeight() * 0.25,
44 getWidth() / 4 - 5, getHeight() / 4 - 5);
45 g2.fill(circle);
46 }
47 // Color的填充
48 private void paintGradientRectangle(Graphics g) {
49 Graphics2D g2 = (Graphics2D) g;
50 Dimension d = getSize();
51 // 将坐标移动到中心
52 g2.translate(d.width / 2, d.height * 5 / 8);
53 Color[] colors = { Color.white, Color.lightGray, Color.gray,
54 Color.darkGray, Color.black, Color.red, Color.pink,
55 Color.orange, Color.yellow, Color.green, Color.magenta,
56 Color.cyan, Color.blue };
57
58 float size = 25;
59 float x = -size * colors.length / 2;
60 float y = -size * 3 / 2;
61
62 // 在第一行显示预定义的颜色
63 for (int i = 0; i < colors.length; i++) {
64 Rectangle2D r = new Rectangle2D.Float(x + size * (float) i, y,
65 size, size);
66 g2.setPaint(colors[i]);
67 g2.fill(r);
68 }
69 // 显示一个手工计算的线性渐变
70 y += size;
71 Color c1 = Color.yellow;
72 Color c2 = Color.blue;
73 for (int i = 0; i < colors.length; i++) {
74 float ratio = (float) i / (float) colors.length;
75 int red = (int) (c2.getRed() * ratio + c1.getRed() * (1 - ratio));
76 int green = (int) (c2.getGreen() * ratio + c1.getGreen()
77 * (1 - ratio));
78 int blue = (int) (c2.getBlue() * ratio + c1.getBlue() * (1 - ratio));
79 Color c = new Color(red, green, blue);
80 Rectangle2D r = new Rectangle2D.Float(x + size * (float) i, y,
81 size, size);
82 g2.setPaint(c);
83 g2.fill(r);
84 }
85 // 显示一个手工计算透明度的线性渐变
86 y += size;
87 c1 = Color.red;
88 for (int i = 0; i < colors.length; i++) {
89 int alpha = (int) (255 * (float) i / (float) colors.length);
90 Color c = new Color(c1.getRed(), c1.getGreen(), c1.getBlue(), alpha);
91 Rectangle2D r = new Rectangle2D.Float(x + size * (float) i, y,
92 size, size);
93 g2.setPaint(c);
94 g2.fill(r);
95 }
96 // 在3层的最外围绘制一个框
97 y -= size * 2;
98 Rectangle2D frame = new Rectangle2D.Float(x, y, size * colors.length,
99 size * 3);
100 g2.setPaint(Color.black);
101 g2.draw(frame);
102 g2.translate(-d.width / 2, -d.height * 5 / 8);
103 }
104 // 绘制带纹理的着色
105 private void paintTexture(Graphics g) {
106 Graphics2D g2 = (Graphics2D) g;
107 BufferedImage bi = new BufferedImage(5, 5, BufferedImage.TYPE_INT_RGB);
108 Graphics2D big = bi.createGraphics();
109 big.setColor(Color.blue);
110 big.drawRect(0, 0, 5, 5);
111 big.setColor(Color.lightGray);
112 big.fillOval(0, 0, 5, 5);
113 Rectangle r = new Rectangle(0, 0, 5, 5);
114 g2.setPaint(new TexturePaint(bi, r));
115 // 用带有纹理的TexturePaint绘制rect。
116 Rectangle rect = new Rectangle(5, 5 + getHeight() * 3 / 4, 100, 100);
117 g2.fill(rect);
118 big.dispose();
119 }
120 }

你可能感兴趣的:(java)