Java GUI基础实战:手绘诺基亚手机图形
简介:本实例“Java简单画图实例_画一个诺基亚手机”是为Java初学者设计的一个图形绘制练习,旨在帮助掌握Java GUI基础绘图技巧。通过使用Java的Applet类和Graphics绘图对象,开发者可以重写paint()方法实现自定义图形绘制,包括矩形、线条、圆形等元素,从而构建出一个诺基亚手机的图形界面。虽然Applet已逐渐被现代Web技术取代,但其绘图机制对理解Java图形编程仍具有重要学习价值。项目适合用于Java图形界面入门教学,并为后续Swing或JavaFX开发打下基础。
1. Java GUI绘图基础
Java图形用户界面(GUI)编程是Java应用开发的重要组成部分,尤其在早期版本中,Applet技术曾广泛用于实现嵌入网页的交互式图形界面。本章将介绍Java GUI的基本概念、图形绘制的开发环境搭建以及Applet与Application的区别,为后续深入学习打下坚实基础。
Java GUI编程主要依赖于AWT(Abstract Window Toolkit)和Swing库。AWT是Java最早的GUI工具包,而Swing则是在AWT基础上构建的更为灵活和强大的图形界面库。它们共同构成了Java平台的图形交互能力。
在开发环境方面,推荐使用Eclipse或IntelliJ IDEA等现代IDE,并配置JDK 8或更高版本。对于Applet开发,还需配置HTML测试页面,并使用 appletviewer 或兼容的浏览器进行调试。
Applet与Application的主要区别在于运行环境:
- Applet 是嵌入在网页中运行的小程序,由浏览器调用Java插件执行。
- Application 是独立运行的Java程序,通常通过 main() 方法启动。
本章将为后续章节的图形绘制、事件处理和交互设计奠定基础。
2. Applet类的使用与生命周期
Java Applet 是 Java 最早用于实现图形界面和嵌入式交互的一种技术,尤其在 Web 应用初期,它曾广泛应用于网页中的动画、游戏和交互式功能。虽然随着 HTML5 和 JavaScript 的兴起,Applet 逐渐被取代,但其在 Java GUI 发展史中具有不可忽视的地位。理解 Applet 类的使用方式及其生命周期,不仅有助于回顾 Java GUI 的发展轨迹,也能帮助开发者更好地理解 Java 程序在浏览器中的运行机制。
2.1 Applet类的基本结构
Java Applet 的核心是 java.applet.Applet 类,它是所有 Applet 程序的基类。Applet 是一种特殊的 Java 程序,它运行在浏览器中,依赖于 HTML 文件进行加载和展示。
2.1.1 Applet类的继承关系
Applet 类继承自 java.awt.Panel ,而 Panel 又继承自 Container ,最终继承自 Component 。这种继承关系使得 Applet 可以直接作为容器使用,支持图形绘制和组件嵌套。
public class MyApplet extends Applet {
// Applet 代码
}
继承关系图示(mermaid 流程图):
classDiagram
Component <|-- Container
Container <|-- Panel
Panel <|-- Applet
Applet <|-- MyApplet
class Component {
+paint(Graphics g)
}
class Container {
+add(Component comp)
}
class Panel {
+Panel()
}
class Applet {
+void init()
+void start()
+void stop()
+void destroy()
}
class MyApplet {
+void init()
}
说明:
- Component 是 AWT 的基本组件类,定义了绘制、事件处理等基础功能。
- Container 是可以容纳其他组件的容器类。
- Panel 是一个轻量级容器,用于组织界面元素。
- Applet 是专为浏览器嵌入设计的类,提供生命周期管理方法。
- MyApplet 是开发者自定义的 Applet 类,通常需要重写 init() 、 start() 、 stop() 和 destroy() 方法。
2.1.2 常见方法与用途
Applet 类提供了多个方法用于控制其行为和生命周期:
| 方法名 | 用途说明 |
|---|---|
init() |
Applet 初始化时调用,用于加载资源、设置布局等。 |
start() |
Applet 开始执行时调用,适用于启动线程或动画。 |
stop() |
Applet 停止时调用,用于暂停耗资源操作,如线程。 |
destroy() |
Applet 销毁时调用,用于释放资源,如关闭线程或文件流。 |
paint(Graphics g) |
绘图方法,用于在 Applet 窗口中绘制图形。 |
示例代码:重写 init() 和 paint() 方法
import java.applet.Applet;
import java.awt.Graphics;
public class HelloWorldApplet extends Applet {
public void init() {
// 设置 Applet 的大小
setSize(400, 300);
}
public void paint(Graphics g) {
// 绘制字符串
g.drawString("Hello, Java Applet!", 100, 100);
}
}
代码逻辑分析:
- init() 方法中调用 setSize() 设置 Applet 的显示尺寸为 400x300 像素。
- paint() 方法中使用 Graphics 对象的 drawString() 方法,在坐标 (100, 100) 处绘制字符串 “Hello, Java Applet!”。
- Graphics 对象由浏览器自动传递给 paint() 方法,无需手动创建。
2.2 Applet的生命周期管理
Applet 的生命周期由浏览器控制,主要包括四个阶段:初始化、启动、停止和销毁。开发者可以通过重写相应的生命周期方法来执行特定操作。
2.2.1 init()方法与初始化操作
init() 方法是 Applet 生命周期的第一个阶段,当浏览器加载 Applet 时会自动调用该方法。这个阶段通常用于:
- 设置 Applet 的大小
- 加载图像资源
- 创建 GUI 组件
- 初始化变量
示例:加载图片并初始化界面
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import java.net.URL;
public class ImageApplet extends Applet {
private Image img;
public void init() {
try {
// 获取图片资源路径
URL url = new URL(getDocumentBase(), "logo.png");
img = getImage(url); // 加载图片
} catch (Exception e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
if (img != null) {
g.drawImage(img, 50, 50, this); // 绘制图片
} else {
g.drawString("Image not found", 50, 50);
}
}
}
参数说明:
- getDocumentBase() :获取当前 HTML 文件所在的 URL。
- getImage(URL url) :从指定 URL 加载图片。
- g.drawImage() :将图片绘制到指定坐标位置。
2.2.2 start()与stop()方法控制运行状态
start() 方法在 Applet 初始化完成后调用,用于启动动画、线程等操作; stop() 方法在用户离开包含 Applet 的网页时调用,用于暂停这些操作。
示例:启动和停止动画线程
import java.applet.Applet;
import java.awt.Graphics;
public class AnimationApplet extends Applet implements Runnable {
private Thread animator;
private int x = 0;
public void init() {
setSize(400, 300);
}
public void start() {
if (animator == null) {
animator = new Thread(this);
animator.start(); // 启动线程
}
}
public void stop() {
if (animator != null) {
animator = null; // 停止线程
}
}
public void run() {
while (Thread.currentThread() == animator) {
x += 2;
if (x > 400) x = 0;
repaint(); // 重绘界面
try {
Thread.sleep(50); // 控制动画速度
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void paint(Graphics g) {
g.fillOval(x, 100, 20, 20); // 绘制一个圆形动画
}
}
逻辑分析:
- start() 方法中创建并启动线程 animator 。
- run() 方法中不断更新圆的位置 x ,并调用 repaint() 强制重绘。
- stop() 方法中将线程引用设为 null,结束动画。
2.2.3 destroy()方法与资源释放
destroy() 方法是 Applet 生命周期的最后一个阶段,用于释放所有资源,如关闭线程、释放图像内存等。
示例:释放线程资源
public void destroy() {
animator = null; // 显式释放线程资源
System.out.println("Applet destroyed.");
}
注意事项:
- destroy() 方法通常由浏览器自动调用,开发者应确保资源释放。
- 如果 Applet 中使用了数据库连接、网络资源或文件流,应在 destroy() 中关闭。
2.3 在HTML中嵌入Applet
Applet 程序必须嵌入在 HTML 文件中才能运行。HTML 使用 <applet> 标签加载 Applet,并可通过 <param> 标签传递参数。
2.3.1 标签的基本使用
<applet code="HelloWorldApplet.class" width="400" height="300">
</applet>
参数说明:
- code :指定 Applet 的类名。
- width 和 height :设置 Applet 显示区域的大小。
2.3.2 参数传递与安全性限制
通过 <param> 标签可以向 Applet 传递参数:
<applet code="GreetingApplet.class" width="300" height="100">
<param name="username" value="John">
</applet>
Applet 获取参数:
public void init() {
String user = getParameter("username");
if (user == null) user = "Guest";
message = "Welcome, " + user + "!";
}
安全性限制:
- Applet 不能访问本地文件系统(除非签名)
- Applet 不能与外部服务器通信(同源策略限制)
- 需要 JVM 插件支持,现代浏览器已逐步弃用 Applet
2.4 Applet与现代浏览器的兼容性问题
2.4.1 Applet技术的局限性
尽管 Applet 曾是 Java 在 Web 中的重要应用,但其存在以下问题:
- 需要安装 Java 插件,用户体验差
- 安全性问题频发,导致浏览器逐步淘汰
- 启动慢、资源占用高
- 与现代 Web 技术(如 HTML5、WebAssembly)不兼容
2.4.2 向JavaFX或Swing迁移的建议
对于需要图形界面的桌面应用,推荐使用:
- JavaFX :现代 Java GUI 框架,支持 FXML、CSS 样式、动画等。
- Swing :传统但稳定的 GUI 框架,适合轻量级桌面应用。
JavaFX 示例:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class HelloJavaFX extends Application {
@Override
public void start(Stage primaryStage) {
Label label = new Label("Hello, JavaFX!");
Scene scene = new Scene(label, 300, 100);
primaryStage.setScene(scene);
primaryStage.setTitle("JavaFX App");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
优势:
- 无需浏览器插件
- 更好的性能和视觉效果
- 支持多线程、动画、3D 图形等高级特性
本章系统讲解了 Applet 类的基本结构、生命周期管理、HTML 嵌入方式及其在现代浏览器中的兼容性问题。Applet 虽已逐渐退出主流,但其作为 Java GUI 技术的重要一环,值得开发者深入理解其原理与演变。下一章将深入探讨 Java GUI 中最核心的方法之一: paint() 与 Graphics 对象的使用。
3. paint()方法与Graphics对象
Java GUI编程中, paint() 方法与 Graphics 对象构成了图形绘制的核心机制。 paint() 作为图形绘制的入口,负责触发绘图操作;而 Graphics 对象则提供了丰富的绘图工具,用于实现形状绘制、颜色填充、文本显示等功能。本章将深入解析 paint() 方法的调用机制、 Graphics 对象的获取与使用,并探讨如何通过双缓冲技术解决图形闪烁问题,提升绘图质量。
3.1 paint()方法的作用与调用机制
Java的图形绘制流程中, paint() 方法是绘图逻辑的起点。它定义在 java.awt.Component 类中,所有继承自 Component 的组件(如 Applet 、 Panel 、 Canvas 等)都可以通过重写该方法来实现自定义绘图。
3.1.1 绘图触发条件
paint() 方法并非由程序员主动调用,而是由系统根据特定条件自动触发。以下是常见的触发时机:
| 触发条件 | 描述 |
|---|---|
| 窗口首次显示 | 当窗口首次加载或被创建时,会触发一次完整的绘制 |
| 窗口大小调整 | 窗口尺寸变化时,组件需要重新绘制 |
| 窗口被遮挡后恢复 | 窗口被其他窗口遮挡后重新露出,会触发局部或全部重绘 |
| 显式调用repaint() | 调用 repaint() 方法请求重绘,系统会将请求加入事件队列并异步执行 |
public void repaint() {
repaint(0, 0, getWidth(), getHeight());
}
上述代码是 repaint() 的默认实现,它调用了一个带有四个参数的版本,表示要重绘整个组件区域。系统最终会调用 paint() 方法进行实际绘制。
3.1.2 重写paint()方法的注意事项
在自定义绘图类中,重写 paint() 方法时需要注意以下几点:
- 调用父类方法 :建议在绘图开始前调用
super.paint(g),以确保组件背景正确清除,防止残留图形影响视觉效果。 - 绘图区域控制 :避免绘制超出组件边界,否则可能导致界面混乱或性能下降。
- 避免阻塞主线程 :绘图逻辑应尽量高效,避免执行耗时操作,否则可能造成界面卡顿。
示例代码如下:
public class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
super.paint(g); // 清除背景
g.setColor(Color.RED);
g.fillOval(50, 50, 100, 100); // 绘制一个红色圆形
}
}
逐行解读:
super.paint(g):调用父类的paint()方法,清除画布背景。g.setColor(Color.RED):设置当前绘图颜色为红色。g.fillOval(50, 50, 100, 100):在坐标(50,50)处绘制一个宽高均为100像素的实心圆形。
3.2 Graphics对象的功能与使用
Graphics 对象是Java图形绘制的核心接口,封装了绘制图形、文本、图像等基本功能。它通常在 paint() 方法中作为参数传入。
3.2.1 获取Graphics实例
Graphics 对象通常由系统在调用 paint() 方法时自动传入,开发者无需手动创建。其获取方式主要如下:
public void paint(Graphics g) {
// g为系统自动传入的Graphics实例
}
此外,也可以通过 getGraphics() 方法从组件中获取,但这种方式存在风险,如在组件未显示时调用会返回 null ,因此不推荐频繁使用。
3.2.2 设置绘图颜色与字体
Graphics 支持多种绘图样式设置,包括颜色、字体、线条样式等。
颜色设置
g.setColor(Color.BLUE);
g.drawString("Hello World", 100, 100);
setColor():设置当前绘图颜色。drawString():使用当前颜色绘制字符串。
字体设置
Font font = new Font("Serif", Font.BOLD, 20);
g.setFont(font);
g.drawString("Bold Text", 100, 150);
setFont():设置当前绘图字体。new Font():构造字体对象,参数分别为字体名称、样式(如BOLD、ITALIC)和字号。
3.2.3 常用绘制方法概述
Graphics 类提供了丰富的绘图方法,以下是一些常用方法的汇总:
| 方法名 | 描述 | 示例 |
|---|---|---|
drawLine(int x1, int y1, int x2, int y2) |
绘制直线 | g.drawLine(0, 0, 100, 100); |
drawRect(int x, int y, int width, int height) |
绘制矩形边框 | g.drawRect(50, 50, 100, 100); |
fillRect(int x, int y, int width, int height) |
绘制实心矩形 | g.fillRect(50, 50, 100, 100); |
drawOval(int x, int y, int width, int height) |
绘制椭圆边框 | g.drawOval(50, 50, 100, 100); |
fillOval(int x, int y, int width, int height) |
绘制实心椭圆 | g.fillOval(50, 50, 100, 100); |
drawString(String str, int x, int y) |
绘制字符串 | g.drawString("Java", 100, 100); |
这些方法构成了Java GUI绘图的基础能力,开发者可以根据需求组合使用,实现丰富的图形界面。
3.3 双缓冲绘图技术
在Java GUI绘图中,直接在组件上绘制图形容易产生“闪烁”现象。这是因为在每次重绘时,屏幕会先清除旧图像再绘制新图像,中间过程可能导致视觉上的抖动。为了解决这个问题,可以使用 双缓冲绘图技术 。
3.3.1 图形闪烁问题分析
闪烁的根本原因在于绘图操作是直接在屏幕上进行的。当图像复杂或绘制频繁时,用户可能会看到图形从无到有的绘制过程,导致视觉不适。
3.3.2 使用Image与Graphics实现双缓冲
双缓冲技术的核心思想是:先将图像绘制到一个内存中的图像缓冲区( Image 对象),再一次性将该图像绘制到屏幕上,从而减少重绘次数,避免闪烁。
以下是双缓冲绘图的实现示例:
public class DoubleBufferedCanvas extends Canvas {
private Image offScreenImage;
private Graphics offScreenGraphics;
public void update(Graphics g) {
if (offScreenImage == null) {
offScreenImage = createImage(getWidth(), getHeight());
offScreenGraphics = offScreenImage.getGraphics();
}
// 绘制到内存图像
paint(offScreenGraphics);
// 将内存图像绘制到屏幕
g.drawImage(offScreenImage, 0, 0, this);
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.GREEN);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.fillOval(100, 100, 100, 100);
}
}
逐行解读:
update(Graphics g):重写update()方法,替代默认的直接绘图方式。createImage():创建一个与当前组件大小相同的图像对象,作为双缓冲区。offScreenGraphics:获取该图像的Graphics对象,用于离屏绘制。paint(offScreenGraphics):将图形先绘制到内存图像上。g.drawImage():将内存图像一次性绘制到屏幕上,减少闪烁。
技术流程图
graph TD
A[启动绘图] --> B[检查缓冲图像是否存在]
B -->|不存在| C[创建Image与Graphics]
B -->|存在| D[清空缓冲图像]
C --> E[调用paint()绘制图形到缓冲区]
D --> E
E --> F[将缓冲图像绘制到屏幕]
F --> G[完成双缓冲绘图]
该流程图清晰地展示了双缓冲绘图的执行流程,确保图形在内存中绘制完成后一次性呈现,从而避免屏幕闪烁问题。
综上所述, paint() 方法与 Graphics 对象是Java GUI绘图的核心,开发者需深入理解其工作机制与使用技巧。通过双缓冲技术的引入,可以有效提升图形渲染的流畅性与视觉体验,为后续复杂图形界面开发奠定坚实基础。
4. 绘图方法 drawRect() 、 fillRect() 使用
在 Java 的图形绘制中, drawRect() 和 fillRect() 是 Graphics 类中最基础且最常用的绘图方法之一。它们分别用于绘制矩形的边框和填充矩形区域。本章将深入探讨这两个方法的使用方式、参数含义以及在实际项目中的应用场景,例如如何通过它们绘制一个诺基亚手机的外框,同时结合颜色与交互设计,提升图形绘制的可操作性与美观度。
4.1 绘制矩形的基本方法
Java 中的 Graphics 类提供了丰富的绘图方法,其中 drawRect() 和 fillRect() 是处理矩形图形的基础函数。理解它们的使用方式对于后续复杂图形的构建至关重要。
4.1.1 drawRect() 方法的参数解析
drawRect() 方法用于绘制一个矩形边框,其定义如下:
public void drawRect(int x, int y, int width, int height)
参数说明:
| 参数名 | 类型 | 含义 |
|---|---|---|
| x | int | 矩形左上角的横坐标(以像素为单位) |
| y | int | 矩形左上角的纵坐标 |
| width | int | 矩形的宽度 |
| height | int | 矩形的高度 |
示例代码:
public class DrawRectDemo extends Applet {
public void paint(Graphics g) {
g.drawRect(50, 50, 200, 100); // 绘制一个边框矩形
}
}
代码逻辑分析:
drawRect(50, 50, 200, 100):在画布上绘制一个从坐标 (50, 50) 开始、宽 200 像素、高 100 像素的矩形边框。- 默认颜色为黑色,边框线宽为 1 像素。
4.1.2 fillRect() 方法与填充效果
与 drawRect() 不同, fillRect() 方法会绘制一个实心矩形,其定义如下:
public void fillRect(int x, int y, int width, int height)
参数说明与 drawRect() 相同。
示例代码:
public class FillRectDemo extends Applet {
public void paint(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(50, 50, 200, 100); // 绘制一个蓝色填充矩形
}
}
代码逻辑分析:
setColor(Color.BLUE):设置当前绘图颜色为蓝色。fillRect(50, 50, 200, 100):绘制一个从 (50, 50) 开始、宽 200 像素、高 100 像素的实心矩形。- 填充区域包括边框,因此与
drawRect()不同的是,它不会只绘制边框,而是整个区域都填充颜色。
4.2 实践:绘制诺基亚手机外框
接下来我们通过一个实际例子,使用 drawRect() 和 fillRect() 绘制一个诺基亚经典手机的外形轮廓。
4.2.1 确定坐标与尺寸比例
在绘制前,我们需要对诺基亚手机的结构进行简单分析。通常,一部诺基亚手机包括:
- 外框(矩形)
- 屏幕区域(矩形)
- 按键区(多层矩形)
为了便于理解,我们假设手机尺寸为 240x320 像素,外框边距为 10 像素。
4.2.2 多层矩形的叠加绘制
示例代码:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
public class NokiaPhoneDemo extends Applet {
public void paint(Graphics g) {
// 外框
g.setColor(Color.BLACK);
g.drawRect(10, 10, 220, 300); // 主体外框
// 背景填充
g.setColor(new Color(200, 200, 200));
g.fillRect(11, 11, 218, 298); // 填充背景色
// 屏幕区域
g.setColor(Color.WHITE);
g.fillRect(60, 40, 120, 100); // 屏幕区域
// 按键区域
g.setColor(Color.GRAY);
g.fillRect(60, 160, 120, 120); // 按键区域
// 按键分隔线
g.setColor(Color.DARK_GRAY);
for (int i = 0; i < 3; i++) {
g.drawLine(60, 200 + i * 40, 180, 200 + i * 40);
g.drawLine(100, 160 + i * 40, 100, 160 + (i + 1) * 40);
}
}
}
代码逻辑分析:
drawRect(10, 10, 220, 300):绘制整个手机的外框。fillRect(11, 11, 218, 298):填充背景色,避免外框被覆盖。fillRect(60, 40, 120, 100):模拟屏幕区域。fillRect(60, 160, 120, 120):模拟按键区域。drawLine(...):绘制按键区域的分隔线,形成 3x3 键盘布局。
4.3 图形样式与颜色设置
良好的图形设计离不开颜色的搭配与样式的选择。Java 提供了 Color 类和 Graphics 的相关方法来设置绘图颜色和样式。
4.3.1 使用 Color 类设置填充色
Java 提供了多种方式创建颜色对象,包括:
- 使用预定义颜色常量(如
Color.RED、Color.BLUE) - 使用 RGB 值构造(如
new Color(255, 0, 0))
示例代码:
g.setColor(new Color(75, 192, 255)); // 设置一种浅蓝色
g.fillRect(10, 10, 100, 50);
逻辑说明:
new Color(75, 192, 255):通过 RGB 值创建一个浅蓝色。fillRect(...):用该颜色填充一个矩形区域。
4.3.2 边框与背景的协调搭配
为了提升视觉效果,边框颜色与填充颜色应协调统一。例如,在绘制一个按钮时,可以使用深色边框搭配浅色填充。
示例代码:
g.setColor(Color.DARK_GRAY);
g.drawRect(100, 100, 100, 50);
g.setColor(Color.LIGHT_GRAY);
g.fillRect(101, 101, 98, 48);
逻辑说明:
drawRect(...):绘制一个深灰色边框。fillRect(...):内部填充浅灰色,形成按钮的立体感。
4.4 鼠标交互与动态绘制
图形界面不应只是静态的,通过添加鼠标事件监听器,可以实现动态绘制功能,比如点击鼠标在指定位置绘制矩形。
4.4.1 鼠标监听器的添加
Java 提供了 MouseListener 接口用于监听鼠标事件。我们可以通过实现 mouseClicked() 方法来响应点击事件。
示例代码:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class MouseDrawDemo extends Applet implements MouseListener {
int x = -1, y = -1;
public void init() {
addMouseListener(this);
}
public void paint(Graphics g) {
if (x != -1 && y != -1) {
g.setColor(Color.RED);
g.fillRect(x - 25, y - 15, 50, 3
# 5. 绘图方法 drawOval()、fillOval() 使用
Java 的 `Graphics` 类提供了多种绘图方法,其中 `drawOval()` 和 `fillOval()` 是用于绘制圆形和椭圆图形的重要方法。本章将深入讲解这两个方法的使用方式、参数含义,并通过实际案例展示如何利用它们绘制诺基亚手机的按键等图形元素。同时,我们还将探讨如何通过组合多个圆形实现复杂图形设计,并引入图形动画的基本实现思路。
## 5.1 圆形与椭圆的绘制方法
Java 的 `Graphics` 类提供了两个关键方法用于绘制圆形和椭圆:`drawOval()` 和 `fillOval()`。它们在绘图时非常常用,尤其是在绘制按钮、图标、进度环等图形时。
### 5.1.1 drawOval() 方法参数说明
`drawOval(int x, int y, int width, int height)` 方法用于绘制一个椭圆的边框,其参数含义如下:
- `x`:椭圆左上角的 X 坐标
- `y`:椭圆左上角的 Y 坐标
- `width`:椭圆的宽度
- `height`:椭圆的高度
当 `width` 等于 `height` 时,绘制的是一个标准的圆形;否则,绘制的是椭圆。
```java
public void paint(Graphics g) {
// 绘制一个圆形,中心点为 (100, 100),半径为 50
g.drawOval(50, 50, 100, 100);
}
逐行分析:
g.drawOval(50, 50, 100, 100):绘制一个外接于左上角为 (50,50),宽高均为 100 的矩形的圆形。由于宽高相等,因此绘制的是一个圆。
5.1.2 fillOval() 实现填充效果
fillOval(int x, int y, int width, int height) 方法用于绘制并填充一个椭圆或圆形。其参数与 drawOval() 完全相同,区别在于它会用当前颜色填充椭圆区域。
public void paint(Graphics g) {
// 设置颜色为红色
g.setColor(Color.RED);
// 填充一个红色圆形
g.fillOval(50, 50, 100, 100);
}
逐行分析:
g.setColor(Color.RED):设置当前绘图颜色为红色。g.fillOval(50, 50, 100, 100):绘制并填充一个红色圆形。
5.1.3 drawOval() 与 fillOval() 的区别对比
| 方法名 | 功能描述 | 是否填充 | 是否有边框 |
|---|---|---|---|
drawOval() |
绘制椭圆或圆形边框 | 否 | 是 |
fillOval() |
绘制并填充椭圆或圆形 | 是 | 否 |
技巧提示 :可以结合使用
drawOval()和fillOval()来实现带边框的填充圆形。例如先使用fillOval()填充,再使用drawOval()添加边框。
5.2 实践:绘制诺基亚手机按键
为了更好地理解 drawOval() 和 fillOval() 的实际应用,我们通过一个实践案例来绘制诺基亚手机的经典数字键盘。
5.2.1 键盘布局分析
诺基亚手机的数字键盘通常为 4 行 3 列的布局,每行包含 3 个圆形按键。每个按键之间留有空隙,形成整齐的排列。
我们可以将整个布局划分为一个二维数组,记录每个按键的坐标位置和显示数字。
public void paint(Graphics g) {
int rows = 4, cols = 3;
int buttonSize = 40;
int spacing = 20;
int startX = 50, startY = 50;
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
int x = startX + col * (buttonSize + spacing);
int y = startY + row * (buttonSize + spacing);
// 填充圆形按键
g.setColor(Color.LIGHT_GRAY);
g.fillOval(x, y, buttonSize, buttonSize);
// 添加边框
g.setColor(Color.BLACK);
g.drawOval(x, y, buttonSize, buttonSize);
// 绘制数字
g.setColor(Color.BLACK);
g.drawString(String.valueOf(row * cols + col + 1), x + 15, y + 25);
}
}
}
逐行分析:
rows = 4,cols = 3:定义键盘的行数和列数。buttonSize = 40:每个按键的大小。spacing = 20:按键之间的间距。startX,startY:起始绘制坐标。- 双重循环遍历每一行每一列,计算每个按键的坐标。
fillOval()填充按键背景。drawOval()添加黑色边框。drawString()在按键中心绘制数字。
5.2.2 动态计算按键位置
上述代码中的按键位置是通过动态计算得出的,这种做法可以避免硬编码坐标,提高可维护性。我们也可以将每个按键的数字封装为一个数组,便于扩展。
String[][] keys = {
{"1", "2", "3"},
{"4", "5", "6"},
{"7", "8", "9"},
{"*", "0", "#"}
};
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
int x = startX + col * (buttonSize + spacing);
int y = startY + row * (buttonSize + spacing);
g.fillOval(x, y, buttonSize, buttonSize);
g.drawOval(x, y, buttonSize, buttonSize);
g.drawString(keys[row][col], x + 15, y + 25);
}
}
扩展思路 :结合鼠标监听器,可以实现按键点击效果,如颜色变化、点击反馈等。
5.3 圆形图形的组合绘制
圆形不仅可以单独使用,还可以通过组合多个圆形或与其他图形(如矩形)结合,实现更复杂的图形效果。
5.3.1 多个圆形组合实现复杂图形
例如,我们可以使用多个圆形绘制一个笑脸表情:
public void paint(Graphics g) {
// 脸部
g.setColor(Color.YELLOW);
g.fillOval(100, 100, 200, 200);
g.setColor(Color.BLACK);
g.drawOval(100, 100, 200, 200);
// 眼睛
g.fillOval(140, 140, 20, 20);
g.fillOval(220, 140, 20, 20);
// 嘴巴
g.drawArc(140, 180, 120, 60, 0, -180);
}
逐行分析:
fillOval(100, 100, 200, 200):绘制黄色的脸部。fillOval(140, 140, 20, 20):左右两个眼睛。drawArc(...):绘制一个半圆弧作为微笑的嘴巴。
5.3.2 圆形与矩形的结合应用
我们可以结合圆形和矩形绘制一个“播放按钮”图形,常用于媒体播放器界面:
public void paint(Graphics g) {
// 圆形背景
g.setColor(Color.GRAY);
g.fillOval(100, 100, 100, 100);
// 播放三角形
int[] xPoints = {130, 130, 170};
int[] yPoints = {130, 170, 150};
g.setColor(Color.WHITE);
g.fillPolygon(xPoints, yPoints, 3);
}
逐行分析:
fillOval():绘制灰色圆形作为背景。fillPolygon():绘制白色三角形表示“播放”按钮。
设计技巧 :在 GUI 设计中,组合图形可以有效提升界面美观度与交互感。建议使用
Graphics2D类以获得更丰富的绘图功能,如渐变、阴影等。
5.4 图形动画基础
Java GUI 编程支持基本的动画实现,本节将介绍如何使用 Timer 类实现圆形的简单移动与闪烁动画。
5.4.1 利用定时器实现简单动画
Java 提供了 javax.swing.Timer 类,可用于定期触发绘图刷新,从而实现动画效果。
以下是一个圆形在屏幕上水平移动的动画示例:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class OvalAnimation extends JApplet {
private int x = 50;
public void init() {
Timer timer = new Timer(50, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
x += 5;
if (x > getWidth()) x = -50;
repaint();
}
});
timer.start();
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.BLUE);
g.fillOval(x, 100, 50, 50);
}
}
逐行分析:
Timer(50, ...):每 50 毫秒触发一次绘图更新。x += 5:每次移动 5 像素。repaint():重绘整个组件,触发paint()方法。g.fillOval(x, 100, 50, 50):绘制蓝色圆形,并随x的变化而移动。
5.4.2 圆形移动与闪烁效果
我们还可以结合颜色变化实现闪烁效果:
private boolean isBright = true;
public void paint(Graphics g) {
super.paint(g);
if (isBright) {
g.setColor(Color.YELLOW);
} else {
g.setColor(Color.LIGHT_GRAY);
}
g.fillOval(x, 100, 50, 50);
}
public void init() {
Timer timer = new Timer(200, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
isBright = !isBright;
repaint();
}
});
timer.start();
}
逐行分析:
isBright:控制颜色状态。Timer每 200ms 切换一次颜色,实现闪烁效果。
动画优化建议 :动画应避免频繁调用
repaint(),建议使用paintComponent()方法继承自JPanel以获得更精细的控制。
总结
本章详细讲解了 Java 中 drawOval() 和 fillOval() 方法的使用方式,并通过实际案例演示了如何绘制诺基亚手机的数字键盘、笑脸、播放按钮等图形。同时,我们还介绍了如何通过多个圆形组合实现复杂图形设计,并初步探讨了图形动画的实现方式。
在下一章中,我们将继续学习 drawLine() 和 drawString() 方法,进一步丰富 Java GUI 绘图的能力,并结合文本与图形设计实现更完整的诺基亚手机界面。
6. 绘图方法drawLine()、drawString()使用
在Java GUI绘图中, drawLine() 和 drawString() 是两个基础但非常关键的方法。它们分别用于绘制直线和显示文本,是构建图形界面中不可或缺的组成部分。本章将深入讲解这两个方法的使用方式、参数含义,并通过实际案例(如在诺基亚手机图形中添加文字与线条)展示其在真实项目中的应用。
6.1 直线绘制与坐标控制
Java的 Graphics 类提供了 drawLine(int x1, int y1, int x2, int y2) 方法,用于在绘图区域中绘制一条从点(x1, y1)到点(x2, y2)的直线。该方法是图形绘制中用于构建边框、连接图形、指示方向等用途的重要工具。
6.1.1 drawLine()方法的参数说明
| 参数 | 类型 | 含义 |
|---|---|---|
| x1 | int | 起始点的X坐标 |
| y1 | int | 起始点的Y坐标 |
| x2 | int | 终点的X坐标 |
| y2 | int | 终点的Y坐标 |
Java的坐标系原点位于绘图区域的左上角,向右为X轴正方向,向下为Y轴正方向。
代码示例:绘制一个简单的直线
import java.applet.Applet;
import java.awt.Graphics;
public class LineExample extends Applet {
public void paint(Graphics g) {
// 绘制一条从(50, 50)到(200, 200)的直线
g.drawLine(50, 50, 200, 200);
}
}
代码逻辑分析:
drawLine(50, 50, 200, 200):绘制从点(50, 50)到点(200, 200)的一条对角线。- 由于Java的坐标系左上角为(0,0),因此这条线从左上向右下延伸。
参数说明:
x1=50,y1=50:起始点位于画布左上角偏移50像素的位置。x2=200,y2=200:终点位于画布右下方向偏移200像素的位置。
6.1.2 坐标系的理解与使用技巧
在Java绘图中,正确理解坐标系是实现精确绘图的前提。例如,在绘制矩形、圆形等图形时,往往需要计算多个点之间的坐标关系。
坐标系示意图(使用mermaid流程图)
graph TD
A[坐标原点(0,0)] --> B[向右X增大]
A --> C[向下Y增大]
D[绘图区域] --> A
D --> B
D --> C
多条直线组合绘制示例
public void paint(Graphics g) {
// 绘制一个“X”形状
g.drawLine(50, 50, 200, 200);
g.drawLine(200, 50, 50, 200);
}
逻辑分析:
- 第一条线从左上到右下;
- 第二条线从右上到左下;
- 两条线交叉形成“X”形,常用于按钮或关闭符号的绘制。
6.2 文本绘制与字体设置
除了图形绘制,Java GUI还支持在绘图区域中绘制文本内容。 drawString(String str, int x, int y) 方法用于在指定坐标位置绘制字符串。结合字体设置,可以实现丰富的文本效果。
6.2.1 drawString()方法的使用
| 参数 | 类型 | 含义 |
|---|---|---|
| str | String | 要绘制的字符串内容 |
| x | int | 起始绘制位置的X坐标 |
| y | int | 起始绘制位置的Y坐标 |
注意:Y坐标通常为文本的基线位置(baseline),而不是顶部。
代码示例:绘制基本文本
public void paint(Graphics g) {
// 在坐标(100, 100)处绘制字符串
g.drawString("Hello Java Graphics!", 100, 100);
}
逻辑分析:
drawString("Hello Java Graphics!", 100, 100):在点(100, 100)绘制字符串。- 该方法常用于显示提示信息、状态栏文字、菜单项等。
6.2.2 字体样式与大小设置
Java中使用 setFont(Font font) 方法设置当前绘图的字体样式。
Font构造函数:
new Font(String name, int style, int size)
| 参数 | 含义 |
|---|---|
| name | 字体名称(如”Serif”, “SansSerif”, “Monospaced”) |
| style | 字体样式(Font.PLAIN, Font.BOLD, Font.ITALIC) |
| size | 字号大小(如12, 14, 18) |
示例代码:
import java.awt.Font;
public void paint(Graphics g) {
// 设置字体:SansSerif,加粗,字号20
g.setFont(new Font("SansSerif", Font.BOLD, 20));
g.drawString("欢迎使用Java绘图!", 50, 150);
}
逻辑分析:
setFont():设置字体样式,影响后续所有drawString()调用。- 字体样式可提升界面美观性,如标题加粗、正文使用正常样式等。
6.3 实践:添加诺基亚手机屏幕文字
在诺基亚手机图形界面中,屏幕区域通常包含品牌Logo、菜单文字、状态信息等。我们可以通过 drawLine() 和 drawString() 方法实现这些元素的绘制。
6.3.1 屏幕区域划分
诺基亚手机屏幕通常位于手机中央矩形区域,我们可以使用 drawRect() 划定区域,再在其内添加文本。
public void paint(Graphics g) {
// 绘制屏幕边框
g.drawRect(80, 100, 200, 100);
// 设置字体
g.setFont(new Font("Monospaced", Font.BOLD, 14));
// 显示品牌LOGO
g.drawString("NOKIA", 130, 130);
// 显示菜单选项
g.drawString("1. 游戏", 100, 150);
g.drawString("2. 通讯录", 100, 170);
}
逻辑分析:
drawRect(80, 100, 200, 100):绘制一个宽200、高100的矩形作为屏幕。drawString():在屏幕内绘制品牌名称和菜单项。Monospaced字体常用于模拟诺基亚早期的点阵字体风格。
效果图示意(使用mermaid模拟)
graph LR
A[屏幕边框] --> B[NOKIA]
A --> C[1. 游戏]
A --> D[2. 通讯录]
6.4 文本与图形的交互设计
在图形界面中,文本不仅仅是静态展示,还可以与图形进行交互。例如,当鼠标悬停在某个区域时,弹出提示文字;或根据图形状态显示动态文本。
6.4.1 鼠标悬停提示实现
我们可以使用 MouseListener 和 MouseMotionListener 来检测鼠标位置,并在指定区域绘制提示信息。
import java.awt.event.*;
import java.applet.Applet;
import java.awt.*;
public class TooltipExample extends Applet implements MouseMotionListener {
private int mouseX = -1, mouseY = -1;
public void init() {
addMouseMotionListener(this);
}
public void paint(Graphics g) {
// 绘制一个圆形按钮
g.fillOval(100, 100, 50, 50);
// 如果鼠标在圆形区域内,显示提示文字
if (isInCircle(mouseX, mouseY)) {
g.setColor(Color.YELLOW);
g.fillRect(160, 100, 100, 30);
g.setColor(Color.BLACK);
g.drawString("点击开始游戏", 165, 120);
}
}
private boolean isInCircle(int x, int y) {
if (x == -1 || y == -1) return false;
int centerX = 125, centerY = 125, radius = 25;
return Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2) <= Math.pow(radius, 2);
}
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
repaint();
}
public void mouseDragged(MouseEvent e) {}
}
代码逻辑分析:
fillOval(100, 100, 50, 50):绘制一个圆形按钮。mouseMoved():监听鼠标移动事件,更新坐标。isInCircle():判断鼠标是否在圆内。- 如果在圆内,则绘制黄色提示框和提示文字。
参数说明:
e.getX(),e.getY():获取鼠标当前位置。repaint():触发重绘,确保提示信息动态更新。
6.4.2 文本与图形联动效果
文本与图形联动可以提升用户体验。例如,在绘制一个进度条时,可以同时显示当前进度百分比。
public void paint(Graphics g) {
int progress = 60; // 60%
int barWidth = 200;
int filledWidth = (progress * barWidth) / 100;
// 绘制进度条外框
g.drawRect(50, 50, barWidth, 20);
// 填充已进行部分
g.setColor(Color.GREEN);
g.fillRect(51, 51, filledWidth, 18);
// 显示进度百分比
g.setColor(Color.BLACK);
g.drawString(progress + "%", 50 + barWidth + 10, 65);
}
逻辑分析:
filledWidth = (progress * barWidth) / 100:根据百分比计算填充宽度。drawRect():绘制进度条外框。fillRect():填充已完成部分。drawString():显示当前进度百分比。
总结延伸
通过本章的学习,我们掌握了Java GUI中 drawLine() 和 drawString() 两个重要绘图方法的使用方式,并通过诺基亚手机界面案例,实践了如何将文本与图形结合进行绘制。这些技能不仅适用于基础图形绘制,也广泛应用于现代GUI开发中。
在下一章中,我们将综合运用前面所学内容,完成诺基亚手机图形的完整绘制流程,包括图形组织、方法封装与性能优化等内容。
7. 诺基亚手机图形完整绘制流程
7.1 整体结构设计与模块划分
在绘制复杂的图形如诺基亚手机界面时,合理的结构设计和模块划分是高效开发的关键。我们可以将整个界面拆解为多个图形组件,如外框、屏幕、按键、文字等。每个组件由独立的方法进行绘制,便于管理和维护。
7.1.1 分解图形元素
诺基亚手机的经典界面通常包括以下图形元素:
| 图形元素 | 描述 |
|---|---|
| 外框 | 使用 drawRect() 和 fillRect() 绘制矩形作为手机的主体边框 |
| 屏幕区域 | 使用嵌套的矩形绘制出屏幕区域,内部可绘制文字 |
| 功能键 | 使用 drawOval() 或 fillOval() 绘制圆形或椭圆形的按键 |
| 导航键 | 使用多条直线组合绘制方向键 |
| 文字内容 | 使用 drawString() 在屏幕中添加品牌名称、菜单项等 |
7.1.2 绘图顺序与图层管理
绘图顺序应遵循“从后往前”的原则,即先绘制背景元素,再绘制前景元素,避免被遮挡。例如:
- 绘制手机外框(背景)
- 绘制屏幕区域(覆盖在背景上)
- 绘制功能按键(在屏幕下方)
- 绘制导航键(位于手机底部)
- 最后绘制文字内容(确保文字可见)
7.2 绘图代码组织与优化
7.2.1 方法封装与复用策略
为了提升代码可读性和复用性,可以将每个图形元素的绘制封装为独立的方法。例如:
public void drawPhoneFrame(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(50, 50, 200, 300); // 手机主体
g.setColor(Color.LIGHT_GRAY);
g.fillRect(70, 70, 160, 260); // 内部填充
}
public void drawScreen(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(90, 90, 120, 100); // 屏幕区域
g.setColor(Color.BLACK);
g.drawString("NOKIA", 120, 120); // 屏幕文字
}
public void drawFunctionKeys(Graphics g) {
g.setColor(Color.GRAY);
for (int i = 0; i < 4; i++) {
g.fillOval(100 + i * 30, 210, 20, 20); // 功能键
}
}
这样可以实现模块化设计,提高代码复用率。
7.2.2 绘图性能调优技巧
- 避免重复计算 :将坐标、颜色等常量提取为
static final常量。 - 减少重绘次数 :使用双缓冲技术(如前一章所述),将图形绘制到
Image对象后再绘制到屏幕,减少闪烁。 - 使用
paintComponent()(Swing)代替paint():在更现代的GUI框架中使用paintComponent()方法进行绘制,提高绘图效率和响应性。
7.3 完整示例:绘制诺基亚经典机型界面
7.3.1 搭建主框架与Applet容器
以下是一个完整的Applet示例,展示如何将上述方法整合:
import java.applet.Applet;
import java.awt.*;
public class NokiaPhoneApplet extends Applet {
public void paint(Graphics g) {
drawPhoneFrame(g);
drawScreen(g);
drawFunctionKeys(g);
drawNavigationKeys(g);
}
private void drawPhoneFrame(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(50, 50, 200, 300);
g.setColor(Color.LIGHT_GRAY);
g.fillRect(70, 70, 160, 260);
}
private void drawScreen(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(90, 90, 120, 100);
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.BOLD, 12));
g.drawString("NOKIA", 120, 120);
g.drawString("Menu", 120, 140);
}
private void drawFunctionKeys(Graphics g) {
g.setColor(Color.GRAY);
for (int i = 0; i < 4; i++) {
g.fillOval(100 + i * 30, 210, 20, 20);
}
}
private void drawNavigationKeys(Graphics g) {
g.setColor(Color.DARK_GRAY);
g.drawLine(130, 250, 130, 270); // 上
g.drawLine(130, 270, 110, 290); // 左
g.drawLine(130, 270, 150, 290); // 右
g.drawLine(130, 270, 130, 290); // 下
}
}
7.3.2 调用各绘图方法组合绘制
在 paint() 方法中依次调用各个模块方法,即可绘制出完整的诺基亚手机界面。该示例中使用了多种绘图方法,包括矩形、圆形、直线和字符串的绘制。
7.4 扩展思路:添加交互功能
7.4.1 按键点击事件模拟
可以为图形按键添加交互效果。例如,使用 MouseListener 监听鼠标点击事件,模拟按键按下:
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class NokiaPhoneApplet extends Applet {
private boolean keyPressed = false;
public NokiaPhoneApplet() {
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if (x >= 100 && x <= 120 && y >= 210 && y <= 230) {
keyPressed = true;
repaint();
}
}
public void mouseReleased(MouseEvent e) {
keyPressed = false;
repaint();
}
});
}
public void paint(Graphics g) {
drawPhoneFrame(g);
drawScreen(g);
drawFunctionKeys(g, g.getColor());
drawNavigationKeys(g);
}
private void drawFunctionKeys(Graphics g, Color defaultColor) {
g.setColor(keyPressed ? Color.RED : Color.GRAY);
for (int i = 0; i < 4; i++) {
g.fillOval(100 + i * 30, 210, 20, 20);
}
g.setColor(defaultColor);
}
}
7.4.2 动态切换界面内容
可以使用 Timer 类定时切换屏幕内容,例如实现一个简单的菜单滚动动画:
import java.util.Timer;
import java.util.TimerTask;
public class NokiaPhoneApplet extends Applet {
private int menuIndex = 0;
private String[] menuItems = {"Home", "Contacts", "Messages", "Settings"};
public NokiaPhoneApplet() {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
menuIndex = (menuIndex + 1) % menuItems.length;
repaint();
}
}, 0, 2000); // 每2秒切换一次
}
private void drawScreen(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(90, 90, 120, 100);
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.BOLD, 12));
g.drawString("NOKIA", 120, 120);
g.drawString(menuItems[menuIndex], 120, 140);
}
}
该示例通过定时器实现菜单项的轮换,为图形界面添加动态效果。
简介:本实例“Java简单画图实例_画一个诺基亚手机”是为Java初学者设计的一个图形绘制练习,旨在帮助掌握Java GUI基础绘图技巧。通过使用Java的Applet类和Graphics绘图对象,开发者可以重写paint()方法实现自定义图形绘制,包括矩形、线条、圆形等元素,从而构建出一个诺基亚手机的图形界面。虽然Applet已逐渐被现代Web技术取代,但其绘图机制对理解Java图形编程仍具有重要学习价值。项目适合用于Java图形界面入门教学,并为后续Swing或JavaFX开发打下基础。
更多推荐



所有评论(0)