57、Java 小程序开发:AWT 可移植性与图形绘制

Java 小程序开发:AWT 可移植性与图形绘制

1. 使用 AWT 实现可移植性

在 Java 小程序开发中,使用 Swing 组件的小程序可能与某些浏览器不兼容。若要确保小程序能在所有支持 Java 的浏览器中兼容,可使用 AWT 组件替代 Swing 组件。

Java 提供了两个用于创建 GUI 组件的类库,即 AWT 和 Swing。AWT 是 Java 最早版本就包含的原始类库,而 Swing 是 Java 2 引入的改进类库。之前学习的 GUI 应用程序和本章的小程序大多使用 Swing 类来创建组件。

部分浏览器不直接支持小程序中的 Swing 类,运行使用 Swing 组件的小程序需要安装插件。不过,安装 Sun JDK 时会自动安装该插件。若已安装 JDK,编写并运行使用 Swing 的小程序通常不会有问题。但如果编写的小程序要供他人在其计算机上运行,不能保证他们都有所需插件,此时应使用 AWT 类创建小程序组件。幸运的是,AWT 组件类与 Swing 类非常相似,若已掌握 Swing 的使用,学习 AWT 并不困难。

以下是一些常见的 AWT 类及其对应的 Swing 类:
| AWT 类 | 描述 | 对应的 Swing 类 |
| — | — | — |
| Applet | 用作所有小程序的超类,与 JApplet 对象不同,Applet 对象没有内容面板 | JApplet |
| Frame | 创建可作为窗口显示的框架容器,与 JFrame 对象不同,Frame 对象没有内容面板 | JFrame |
| Panel | 创建面板容器 | JPanel |
| Button | 创建可点击的按钮 | JButton |
| Label | 创建显示文本的标签 | JLabel |
| TextField | 创建用户可输入的单行文本字段 | JTextField |
| Checkbox | 创建可选择或取消选择的复选框 | JCheckBox |

Swing 类的构造函数和方法设计得与 AWT 对应类相似,且事件处理方式相同,这使得使用这两类类都很方便,无需学习不同的语法。例如,下面是一个将 TempConverter 小程序重写为使用 AWT 组件的示例代码:

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

/**
 * The AWTTempConverter class is an applet that converts
 * Fahrenheit temperatures to Celsius.
 */
public class AWTTempConverter extends Applet {
    private Panel fPanel;          // To hold a text field
    private Panel cPanel;          // To hold a text field
    private Panel buttonPanel;     // To hold a button
    private TextField fahrenheit;  // Fahrenheit temperature
    private TextField celsius;     // Celsius temperature

    /**
     * init method
     */
    public void init() {
        // Build the panels.
        buildFpanel();
        buildCpanel();
        buildButtonPanel();

        // Create a layout manager.
        setLayout(new GridLayout(3, 1));

        // Add the panels to the applet.
        add(fPanel);
        add(cPanel);
        add(buttonPanel);
    }

    /**
     * The buildFpanel method creates a panel with a text
     * field in which the user can enter a Fahrenheit
     * temperature.
     */
    private void buildFpanel() {
        // Create the panel.
        fPanel = new Panel();

        // Create a label to display a message.
        Label message1 = new Label("Fahrenheit Temperature:");

        // Create a text field for the Fahrenheit temp.
        fahrenheit = new TextField(10);

        // Create a layout manager for the panel.
        fPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));

        // Add the label and text field to the panel.
        fPanel.add(message1);
        fPanel.add(fahrenheit);
    }

    /**
     * The buildCpanel method creates a panel that
     * displays the Celsius temperature in a
     * read-only text field.
     */
    private void buildCpanel() {
        // Create the panel.
        cPanel = new Panel();

        // Create a label to display a message.
        Label message2 = new Label("Celsius Temperature:");

        // Create a text field for the Celsius temp.
        celsius = new TextField(10);

        // Make the text field read-only.
        celsius.setEditable(false);

        // Create a layout manager for the panel.
        cPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));

        // Add the label and text field to the panel.
        cPanel.add(message2);
        cPanel.add(celsius);
    }

    /**
     * The buildButtonPanel method creates a panel with
     * a button that converts the Fahrenheit temperature
     * to Celsius.
     */
    private void buildButtonPanel() {
        // Create the panel.
        buttonPanel = new Panel();

        // Create a button with the text "Convert".
        Button convButton = new Button("Convert");

        // Add an action listener to the button.
        convButton.addActionListener(new ButtonListener());

        // Add the button to the panel.
        buttonPanel.add(convButton);
    }

    /**
     * Private inner class that handles the action event
     * that is generated when the user clicks the convert
     * button.
     */
    private class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            double ftemp, ctemp;  // To hold the temperatures

            // Get the Fahrenheit temperature and convert it
            // to a double.
            ftemp = Double.parseDouble(fahrenheit.getText());

            // Calculate the Celsius temperature.
            ctemp = (5.0 / 9.0) * (ftemp - 32);

            // Display the Celsius temperature.
            celsius.setText(String.format("%.1f", ctemp));
        }
    }
}

修改内容主要包括:
- 将 JApplet JPanel JLabel JTextField JButton 类替换为 Applet Panel Label TextField Button 类。
- 移除 import javax.swing.*; 语句。

要在浏览器中运行该小程序,需将 TempConverter.html 文件中的 APPLET 标签修改为:

<applet code="AWTTempConverter.class" width=300 height=150>
</applet>
2. 图形绘制

除了显示按钮和标签等标准组件,Java 还允许在组件上绘制线条和图形,如矩形、椭圆形和弧形。在绘制图形之前,需要了解 XY 坐标系,它用于指定图形的位置。

在组件中,每个像素的位置由 X 坐标和 Y 坐标确定,通常表示为 (X, Y) 。组件左上角像素的坐标通常为 (0, 0) ,X 坐标从左到右递增,Y 坐标从上到下递增。

每个组件都有一个继承自 Graphics 类的内部对象,该对象有许多用于在组件表面绘制图形的方法。以下是一些常用的 Graphics 类方法:
| 方法 | 描述 |
| — | — |
| void setColor(Color c) | 将此对象的绘图颜色设置为参数指定的颜色 |
| Color getColor() | 返回此对象的当前绘图颜色 |
| void drawLine(int x1, int y1, int x2, int y2) | 在组件上绘制一条从坐标 (x1, y1) 到坐标 (x2, y2) 的线,线将以当前绘图颜色绘制 |
| void drawRect(int x, int y, int width, int height) | 在组件上绘制矩形的轮廓,矩形的左上角将位于坐标 (x, y) width 参数指定矩形的宽度(以像素为单位), height 指定矩形的高度(以像素为单位),矩形将以当前绘图颜色绘制 |
| void fillRect(int x, int y, int width, int height) | 绘制填充的矩形,参数与 drawRect 方法相同,矩形将用当前绘图颜色填充 |
| void drawOval(int x, int y, int width, int height) | 在组件上绘制椭圆形的轮廓,椭圆形的形状和大小由包围它的不可见矩形确定,矩形的左上角将位于坐标 (x, y) width 参数指定矩形的宽度(以像素为单位), height 指定矩形的高度(以像素为单位),椭圆形将以当前绘图颜色绘制 |
| void fillOval(int x, int y, int width, int height) | 绘制填充的椭圆形,参数与 drawOval 方法相同,椭圆形将用当前绘图颜色填充 |
| void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) | 绘制弧形,弧形被视为椭圆形的一部分,椭圆形的形状和大小由包围它的不可见矩形确定,矩形的左上角将位于坐标 (x, y) width 参数指定矩形的宽度(以像素为单位), height 指定矩形的高度(以像素为单位),弧形从 startAngle 开始,到 arcAngle 结束,弧形将以当前绘图颜色绘制 |
| void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) | 绘制填充的弧形,参数与 drawArc 方法相同,弧形将用当前绘图颜色填充 |
| void drawPolygon(int[] xPoints, int[] yPoints, int numPoints) | 在组件上绘制封闭多边形的轮廓, xPoints 数组包含每个顶点的 X 坐标, yPoints 数组包含每个顶点的 Y 坐标, numPoints 参数指定多边形的顶点数 |
| void fillPolygon(int[] xPoints, int[] yPoints, int numPoints) | 绘制填充的多边形,参数与 drawPolygon 方法相同,多边形将用当前绘图颜色填充 |
| void drawString(String str, int x, int y) | 使用当前字体绘制传入的字符串,字符串的左下角将绘制在传入的 x y 坐标处 |
| void setFont(Font f) | 设置当前字体,供 drawString 方法使用 |

要调用这些方法,需要获取组件的 Graphics 对象引用。一种方法是重写 paint 方法,可在继承自 JApplet JFrame 或任何 AWT 类(如 Applet Frame )的类中重写该方法。 paint 方法负责在屏幕上显示或“绘制”组件,当组件首次显示或需要重新显示时会自动调用。以下是一个绘制线条的小程序示例:

import javax.swing.*;
import java.awt.*;

/**
 * This class is an applet that demonstrates how lines
 * can be drawn.
 */
public class LineDemo extends JApplet {
    /**
     * init method
     */
    public void init() {
        // Set the background color to white.
        getContentPane().setBackground(Color.white);
    }

    /**
     * paint method
     * @param g The applet's Graphics object.
     */
    public void paint(Graphics g) {
        // Call the superclass paint method.
        super.paint(g);

        // Draw a red line from (20, 20) to (280, 280).
        g.setColor(Color.red);
        g.drawLine(20, 20, 280, 280);

        // Draw a blue line from (280, 20) to (20, 280).
        g.setColor(Color.blue);
        g.drawLine(280, 20, 20, 280);
    }
}

要运行该小程序,可在 LineDemo.html 文件中添加以下标签:

<applet code="LineDemo.class" width=300 height=300>
</applet>

3. 面板上的绘制

前面的示例都是将整个 JApplet 窗口作为绘图画布,有时可能希望将绘图空间限制在窗口内的较小区域,如面板。要在面板上绘图,只需获取面板的 Graphics 对象引用,然后使用该对象的方法进行绘制,绘制的图形将仅显示在面板上。

获取 JPanel 组件的 Graphics 对象引用的方法与前面的示例类似,但对于 JPanel 对象,应重写其 paintComponent 方法,这适用于除 JApplet JFrame 之外的所有 Swing 组件。 paintComponent 方法的作用与 paint 方法相同,当组件需要重新显示时会自动调用,调用时会将组件的 Graphics 对象作为参数传入。以下是一个在面板上绘图的示例:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
 * This class displays a drawing panel and a set of
 * check boxes that allow the user to select shapes.
 * The selected shapes are drawn on the drawing panel.
 */
public class GraphicsWindow extends JApplet {
    // Declare an array of check box components
    private JCheckBox[] checkBoxes;

    // The following titles array contains the
    // titles of the check boxes.
    private String[] titles = { "Line", "Rectangle",
            "Filled Rectangle",
            "Oval", "Filled Oval",
            "Arc", "Filled Arc" };

    // The following will reference a panel to contain
    // the check boxes.
    private JPanel checkBoxPanel;

    // The following will reference an instance of the
    // DrawingPanel class. This will be a panel to draw on.
    private DrawingPanel drawingPanel;

    /**
     * init method
     */
    public void init() {
        // Build the check box panel.
        buildCheckBoxPanel();

        // Create the drawing panel.
        drawingPanel = new DrawingPanel(checkBoxes);

        // Add the check box panel to the east region
        // and the drawing panel to the center region.
        add(checkBoxPanel, BorderLayout.EAST);
        add(drawingPanel, BorderLayout.CENTER);
    }

    /**
     * The buildCheckBoxPanel method creates the array of
     * check box components and adds them to a panel.
     */
    private void buildCheckBoxPanel() {
        // Create the panel.
        checkBoxPanel = new JPanel();
        checkBoxPanel.setLayout(new GridLayout(7, 1));

        // Create the check box array.
        checkBoxes = new JCheckBox[7];

        // Create the check boxes and add them to the panel.
        for (int i = 0; i < checkBoxes.length; i++) {
            checkBoxes[i] = new JCheckBox(titles[i]);
            checkBoxes[i].addItemListener(
                    new CheckBoxListener());
            checkBoxPanel.add(checkBoxes[i]);
        }
    }

    /**
     * A private inner class to respond to changes in the
     * state of the check boxes.
     */
    private class CheckBoxListener implements ItemListener {
        public void itemStateChanged(ItemEvent e) {
            drawingPanel.repaint();
        }
    }
}

import javax.swing.*;
import java.awt.*;

/**
 * This class creates a panel that example shapes are
 * drawn on.
 */
public class DrawingPanel extends JPanel {
    // Declare a check box array.
    private JCheckBox[] checkBoxArray;

    /**
     * Constructor
     */
    public DrawingPanel(JCheckBox[] cbArray) {
        // Reference the check box array.
        checkBoxArray = cbArray;

        // Set the background color to white.
        setBackground(Color.white);

        // Set the preferred size of the panel.
        setPreferredSize(new Dimension(300, 200));
    }

    /**
     * paintComponent method
     * @param g The panel's Graphics object.
     */
    public void paintComponent(Graphics g) {
        // Call the superclass paintComponent method.
        super.paintComponent(g);

        // Draw the selected shapes.
        if (checkBoxArray[0].isSelected()) {
            g.setColor(Color.black);
            g.drawLine(10, 10, 290, 190);
        }
        if (checkBoxArray[1].isSelected()) {
            g.setColor(Color.black);
            g.drawRect(20, 20, 50, 50);
        }
        if (checkBoxArray[2].isSelected()) {
            g.setColor(Color.red);
            g.fillRect(50, 30, 120, 120);
        }
        if (checkBoxArray[3].isSelected()) {
            g.setColor(Color.black);
            g.drawOval(40, 155, 75, 50);
        }
        if (checkBoxArray[4].isSelected()) {
            g.setColor(Color.blue);
            g.fillOval(200, 125, 75, 50);
        }
        if (checkBoxArray[5].isSelected()) {
            g.setColor(Color.black);
            g.drawArc(200, 40, 75, 50, 0, 90);
        }
        if (checkBoxArray[6].isSelected()) {
            g.setColor(Color.green);
            g.fillArc(100, 155, 75, 50, 0, 90);
        }
    }
}

在这个示例中, GraphicsWindow 类创建了一个包含复选框的面板和一个绘图面板,当用户选择复选框时,会在绘图面板上绘制相应的图形。通过调用 repaint 方法可以强制调用 paint paintComponent 方法,以更新绘图。

综上所述,Java 提供了丰富的功能来开发小程序和进行图形绘制,通过合理使用 AWT 和 Swing 组件以及 Graphics 对象,可以创建出功能强大且美观的应用程序。

4. 图形绘制的更多细节

4.1 绘制不同形状的示例分析

在前面的内容中,我们已经了解了使用 Graphics 对象绘制各种基本图形的方法,下面对更多具体示例进行分析。

矩形绘制

RectangleDemo 类展示了如何绘制矩形,包括绘制黑色轮廓矩形和红色填充矩形。代码如下:

import javax.swing.*;
import java.awt.*;

/**
 * This class is an applet that demonstrates how
 * rectangles can be drawn.
 */
public class RectangleDemo extends JApplet {
    /**
     * init method
     */
    public void init() {
        // Set the background color to white.
        getContentPane().setBackground(Color.white);
    }

    /**
     * paint method
     * @param g The applet's Graphics object.
     */
    public void paint(Graphics g) {
        // Call the superclass paint method.
        super.paint(g);

        // Draw a black unfilled rectangle.
        g.setColor(Color.black);
        g.drawRect(20, 20, 120, 120);

        // Draw a red filled rectangle.
        g.setColor(Color.red);
        g.fillRect(160, 160, 120, 120);
    }
}

在这个示例中, drawRect 方法用于绘制矩形的轮廓, fillRect 方法用于绘制填充的矩形。通过设置不同的颜色和坐标,可以绘制出不同样式的矩形。

椭圆形绘制

OvalDemo 类展示了如何绘制椭圆形,包括绘制黑色轮廓椭圆形和绿色填充椭圆形。代码如下:

import javax.swing.*;
import java.awt.*;

/**
 * This class is an applet that demonstrates how
 * ovals can be drawn.
 */
public class OvalDemo extends JApplet {
    /**
     * init method
     */
    public void init() {
        // Set the background color to white.
        getContentPane().setBackground(Color.white);
    }

    /**
     * paint method
     * @param g The applet's Graphics object.
     */
    public void paint(Graphics g) {
        // Call the superclass paint method.
        super.paint(g);

        // Draw a black unfilled oval.
        g.setColor(Color.black);
        g.drawOval(20, 20, 120, 75);

        // Draw a green filled oval.
        g.setColor(Color.green);
        g.fillOval(80, 160, 180, 75);
    }
}

绘制椭圆形时,需要指定一个包围它的不可见矩形的左上角坐标、宽度和高度。通过调整这些参数,可以绘制出不同形状和大小的椭圆形。

弧形绘制

ArcDemo 类展示了如何绘制弧形,包括绘制未填充和填充的弧形。代码如下:

import javax.swing.*;
import java.awt.*;

/**
 * This class is an applet that demonstrates how
 * arcs can be drawn.
 */
public class ArcDemo extends JApplet {
    /**
     * init method
     */
    public void init() {
        // Set the background color to white.
        getContentPane().setBackground(Color.white);
    }

    /**
     * paint method
     * @param g The applet's Graphics object.
     */
    public void paint(Graphics g) {
        // Call the superclass paint method.
        super.paint(g);

        // Draw a black unfilled arc from 0 degrees
        // to 90 degrees.
        g.setColor(Color.black);
        g.drawArc(0, 20, 120, 120, 0, 90);

        // Draw a red filled arc from 0 degrees
        // to 90 degrees.
        g.setColor(Color.red);
        g.fillArc(140, 20, 120, 120, 0, 90);

        // Draw a green unfilled arc from 0 degrees
        // to 45 degrees.
        g.setColor(Color.green);
        g.drawArc(0, 120, 120, 120, 0, 45);

        // Draw a blue filled arc from 0 degrees
        // to 45 degrees.
        g.setColor(Color.blue);
        g.fillArc(140, 120, 120, 120, 0, 45);
    }
}

绘制弧形时,除了指定包围它的矩形的信息外,还需要指定弧形的起始角度和结束角度。角度以度为单位,0 度位于 3 点钟位置。

多边形绘制

PolygonDemo 类展示了如何绘制多边形,这里绘制了一个填充的八边形。代码如下:

import javax.swing.*;
import java.awt.*;

/**
 * This class is an applet that demonstrates how a
 * polygon can be drawn.
 */
public class PolygonDemo extends JApplet {
    /**
     * init method
     */
    public void init() {
        // Set the background color to white.
        getContentPane().setBackground(Color.white);
    }

    /**
     * paint method
     * @param g The applet's Graphics object.
     */
    public void paint(Graphics g) {
        int[] xCoords = {60, 100, 140, 140, 100, 60, 20, 20 };
        int[] yCoords = {20, 20, 60, 100, 140, 140, 100, 60 };

        // Call the superclass paint method.
        super.paint(g);

        // Set the drawing color.
        g.setColor(Color.red);

        // Draw the polygon.
        g.fillPolygon(xCoords, yCoords, 8);
    }
}

绘制多边形时,需要提供两个数组,分别包含每个顶点的 X 坐标和 Y 坐标,以及顶点的数量。如果最后一个点与第一个点不同,会自动连接这两个点来封闭多边形。

4.2 字符串绘制与字体设置

在 Java 中,可以使用 drawString 方法绘制字符串,并使用 setFont 方法设置字符串的字体。 GraphicStringDemo 类展示了如何绘制字符串并创建一个类似停车标志的效果。代码如下:

import javax.swing.*;
import java.awt.*;

/**
 * This class is an applet that demonstrates how a
 * string can be drawn.
 */
public class GraphicStringDemo extends JApplet {
    /**
     * init method
     */
    public void init() {
        // Set the background color to white.
        getContentPane().setBackground(Color.white);
    }

    /**
     * paint method
     * @param g The applet's Graphics object.
     */
    public void paint(Graphics g) {
        int[] xCoords = {60, 100, 140, 140, 100, 60, 20, 20 };
        int[] yCoords = {20, 20, 60, 100, 140, 140, 100, 60 };

        // Call the superclass paint method.
        super.paint(g);

        // Set the drawing color.
        g.setColor(Color.red);

        // Draw the polygon.
        g.fillPolygon(xCoords, yCoords, 8);

        // Set the drawing color to white.
        g.setColor(Color.white);

        // Set the font and draw "STOP".
        g.setFont(new Font("SansSerif", Font.BOLD, 35));
        g.drawString("STOP", 35, 95);
    }
}

在这个示例中,首先绘制了一个八边形,然后设置字体为加粗的 35 点无衬线字体,并在八边形上绘制字符串 “STOP”。

5. 强制重绘与图形绘制总结

5.1 强制重绘

在某些情况下,可能需要强制调用组件的 paint paintComponent 方法。可以通过调用 repaint 方法来实现,其方法签名如下:

public void repaint()

repaint 方法会清除组件的表面,然后调用 paint paintComponent 方法。例如,在 GraphicsWindow 类的 CheckBoxListener 类中,当用户点击复选框时,调用 drawingPanel.repaint() 方法来更新绘图面板上的图形。

5.2 图形绘制总结

在 Java 中进行图形绘制,主要步骤如下:
1. 获取组件的 Graphics 对象引用,可以通过重写 paint 方法(适用于 JApplet JFrame 或 AWT 类)或 paintComponent 方法(适用于 JPanel 等 Swing 组件)。
2. 在重写的方法中,首先调用父类的相应方法,确保组件正确显示。
3. 使用 Graphics 对象的各种方法绘制图形,如 drawLine drawRect fillRect 等。
4. 可以使用 setColor 方法设置绘图颜色,使用 setFont 方法设置字体。
5. 如果需要强制更新图形,可以调用 repaint 方法。

以下是一个简单的流程图,展示了图形绘制的基本流程:

graph TD;
    A[获取Graphics对象引用] --> B[调用父类方法];
    B --> C[设置绘图颜色和字体];
    C --> D[绘制图形];
    D --> E{是否需要强制更新};
    E -- 是 --> F[调用repaint方法];
    E -- 否 --> G[结束];
    F --> D;

综上所述,Java 提供了丰富的工具和方法来实现小程序的可移植性和图形绘制。开发者可以根据具体需求选择合适的组件和方法,创建出功能丰富、界面美观的应用程序。无论是使用 AWT 组件确保兼容性,还是使用 Graphics 对象进行图形绘制,都需要深入理解其原理和使用方法,才能充分发挥 Java 的优势。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值