当前位置: 首页 > article >正文

12.2Swing中JButton简单分析

 JButton 的继承结构

public class JButton extends AbstractButton implements Accessible
  • AbstractButton 是所有 Swing 按钮类(如 JToggleButtonJRadioButtonJCheckBox)的基类。
  • 它封装了按钮的核心逻辑:图标、文本、边框、动作事件等。
java.awt.Component↳ java.awt.Container↳ javax.swing.JComponent↳ javax.swing.AbstractButton

1. java.awt.Component

这是 AWT 中最基本的 GUI 类,代表一个可视化的组件(如按钮、文本框等)。

主要功能:
  • 提供绘制能力(paint(Graphics g))
  • 支持布局管理、事件处理(键盘、鼠标等)
  • 控制可见性、大小、位置等属性
常用方法:
  • paint()repaint()
  • setVisible(boolean)
  • setEnabled(boolean)
  • setSize(Dimension)
  • addMouseListener()addKeyListener() 等

2. java.awt.Container

继承自 Component,表示可以包含其他组件的容器。

主要功能:
  • 可以添加子组件(add(Component)
  • 支持布局管理器(LayoutManager)
  • 提供了组件的层次结构支持
常用方法:
  • add(Component comp)
  • remove(Component comp)
  • setLayout(LayoutManager mgr)
  • getComponents()

3. javax.swing.JComponent

Swing 所有可视化组件的基类,是 Container 的子类。

核心特性:
  • 轻量级组件:不依赖本地系统资源,由 Java 自己绘制
  • 双缓冲机制:减少重绘闪烁(默认开启)
  • 可插拔外观与感觉(PLAF):通过 updateUI() 方法切换 L&F
  • 支持边框、工具提示、ActionMap/InputMap 等高级功能
  • 事件监听体系:继承并扩展 AWT 的事件模型
常用方法:
  • setBorder(Border border):设置边框
  • setToolTipText(String text):设置提示文字
  • revalidate()repaint():用于布局更新
  • updateUI():用于切换外观
  • getGraphics():获取图形上下文(不建议直接使用)

AbstractButton 自身的功能扩展

作为 JComponent 的子类,AbstractButton 在其基础上封装了所有按钮共有的行为和状态:

1. 按钮的基本属性

属性说明
text显示的文字
icon显示的图标
rolloverIcon鼠标悬停时显示的图标
pressedIcon按钮按下时显示的图标
disabledIcon不可用时显示的图标
selectedIcon选中状态下显示的图标(适用于 ToggleButton)

2. 按钮的状态

状态说明
armed是否处于“准备触发”状态(鼠标按下)
pressed是否被按下
rollover鼠标是否悬停
selected是否被选中(适用于 JToggleButton)
enabled是否启用

这些状态由 ButtonModel 接口实现类(如 DefaultButtonModel)来维护。


3. 模型对象:ButtonModel

每个 AbstractButton 都关联一个 ButtonModel 实例,它负责管理按钮的状态变化。

public interface ButtonModel {boolean isArmed();boolean isSelected();boolean isEnabled();boolean isPressed();boolean isRollover();void setSelected(boolean b);void setEnabled(boolean enabled);void addActionListener(ActionListener l);...
}

AbstractButton 使用这个模型来判断当前应该显示什么状态下的图像或样式。


4. 行为与交互

功能描述
action绑定一个 Action 对象,执行动作逻辑
actionCommand触发 ActionEvent 时发送的命令字符串
addActionListener()添加动作监听器,响应点击事件
doClick()模拟按钮点击行为
setMnemonic()设置快捷键(Alt + 字符)
setDisplayedMnemonicIndex()设置快捷键下划线位置

绘图方式:不是直接绘制,而是由 UI Delegate 负责绘制

关键概念:ComponentUI 和 ButtonUI

Swing 使用了一种叫做 UI Delegate 的设计模式,将组件的绘制和交互逻辑委托给特定的 UI 类(这些类通常以 XXXUI 结尾)。

对于 JButton

  • 它的 UI 委托类是 ButtonUI(抽象类)
  • 具体实现根据当前 L&F(LookAndFeel)决定使用哪个子类:
    • MetalButtonUI(默认 Metal 风格)
    • WindowsButtonUI(Windows 外观)
    • GTKButtonUI(Linux/GTK 环境下)
    • AquaButtonUI(macOS)

这些 UI 类负责绘制按钮的外观,包括背景、边框、文字颜色、阴影效果等。

示例代码片段(简化版):

// 在 JButton 初始化时会调用 updateUI() 方法
public void updateUI() {setUI((ButtonUI) UIManager.getUI(this));
}

这个方法从 UIManager 获取当前 LookAndFeel 下的 ButtonUI 实现,并设置到按钮上。


绘图流程详解

1. 绘图入口:paint() 方法

所有 Swing 组件的绘制都通过 paint(Graphics g) 方法完成,该方法定义在 AWT 的 Component 类中。

JButton 自己并不重写 paint(),而是继承自 JComponent,最终调用的是其 UI delegate 的绘制方法:

protected void paintComponent(Graphics g) {if (ui != null) {ui.paint(g, this);}
}

所以,实际的绘制工作是在 ButtonUIpaint() 方法中完成的。


2. ButtonUI 的 paint() 方法

比如,在 MetalButtonUI 中,paint() 方法内部会处理:

  • 按钮是否被按下或选中
  • 是否有焦点
  • 边框绘制
  • 渐变背景
  • 文字对齐与渲染

示例伪代码:

public void paint(Graphics g, JComponent c) {AbstractButton button = (AbstractButton) c;ButtonModel model = button.getModel();// 绘制背景if (model.isPressed() && model.isArmed()) {drawPressedButton(g);} else {drawNormalButton(g);}// 绘制文本String text = button.getText();if (text != null && !text.isEmpty()) {paintText(g, button, button.getFont(), text);}// 绘制图标(如果有)Icon icon = button.getIcon();if (icon != null) {icon.paintIcon(c, g, x, y);}
}

跨平台统一的关键:LookAndFeel(L&F)

Swing 提供了 可插拔的外观和感觉机制(PLAF),使得 JButton 可以在不同平台上显示为不同的风格,同时保持一致的行为逻辑。

 支持的 L&F 包括:

L&F 名称描述
Metal默认的 Swing 外观,跨平台统一风格
Windows在 Windows 上模仿本地风格
Nimbus更现代美观的 Swing 风格
GTKLinux 上模仿 GTK 主题风格
AquamacOS 上模仿原生 Mac 风格

 设置 LookAndFeel 示例:

try {UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
} catch (Exception e) {e.printStackTrace();
}

总结:JButton 如何实现自己的绘制 + 跨平台统一?

特性实现方式
是否自己绘制?❌ 不直接绘制,而是通过 ButtonUI 子类绘制
绘制入口?paintComponent(Graphics g) → ButtonUI.paint()
绘制内容?文本、图标、边框、状态变化(如按下、聚焦)
跨平台关键?通过 PLAF(LookAndFeel)机制选择不同的 ButtonUI 实现
优点外观统一、支持换肤、易于扩展
缺点性能略低于本地控件(但差异不大)

示例:在同一个窗口上绘制两个“按钮” 

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;public class CustomButtonDemo extends JFrame {private Rectangle button1 = new Rectangle(50, 50, 120, 40);private Rectangle button2 = new Rectangle(50, 120, 120, 40);public CustomButtonDemo() {setTitle("Java 2D 自定义控件示例");setSize(300, 250);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);add(new DrawingPanel());setLocationRelativeTo(null); // 居中显示setVisible(true);}class DrawingPanel extends JPanel {private boolean isButton1Pressed = false;private boolean isButton2Pressed = false;public DrawingPanel() {// 添加鼠标监听器来检测点击addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {if (button1.contains(e.getPoint())) {isButton1Pressed = true;repaint();} else if (button2.contains(e.getPoint())) {isButton2Pressed = true;repaint();}}@Overridepublic void mouseReleased(MouseEvent e) {if (isButton1Pressed && button1.contains(e.getPoint())) {System.out.println("你点击了按钮 1");}if (isButton2Pressed && button2.contains(e.getPoint())) {System.out.println("你点击了按钮 2");}isButton1Pressed = false;isButton2Pressed = false;repaint();}});}@Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);Graphics2D g2d = (Graphics2D) g;// 设置抗锯齿g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);// 绘制按钮 1if (isButton1Pressed) {g2d.setColor(Color.LIGHT_GRAY);} else {g2d.setColor(Color.GRAY);}g2d.fill(button1);g2d.setColor(Color.BLACK);g2d.draw(button1);drawCenteredString(g2d, "按钮 1", button1);// 绘制按钮 2if (isButton2Pressed) {g2d.setColor(Color.LIGHT_GRAY);} else {g2d.setColor(Color.GRAY);}g2d.fill(button2);g2d.setColor(Color.BLACK);g2d.draw(button2);drawCenteredString(g2d, "按钮 2", button2);}// 在矩形中间画字符串private void drawCenteredString(Graphics2D g, String text, Rectangle rect) {FontMetrics fm = g.getFontMetrics();int x = rect.x + (rect.width - fm.stringWidth(text)) / 2;int y = rect.y + ((rect.height - fm.getHeight()) / 2) + fm.getAscent();g.drawString(text, x, y);}}public static void main(String[] args) {SwingUtilities.invokeLater(CustomButtonDemo::new);}
}

相关文章:

12.2Swing中JButton简单分析

JButton 的继承结构 public class JButton extends AbstractButton implements Accessible AbstractButton 是所有 Swing 按钮类(如 JToggleButton, JRadioButton, JCheckBox)的基类。它封装了按钮的核心逻辑:图标、文本、边框、动作事件等…...

内存管理--《Hello C++ Wrold!》(8)--(C/C++)--深入剖析new和delete的使用和底层实现

文章目录 前言C/C内存分布new和deletenew和delete的底层定位new表达式 内存泄漏作业部分 前言 在C/C编程中,内存管理是理解程序运行机制的核心基础,也是开发高效、稳定程序的关键。无论是局部变量的存储、动态内存的分配,还是对象生命周期的…...

JavaScript性能优化实战指南(详尽分解版)

JavaScript性能优化实战指南 一、加载优化 减少HTTP请求 // 合并CSS/JS文件 // 使用雪碧图CSS Sprites .icon {background-image: url(sprites.png);background-position: -20px 0; }代码分割与懒加载 // 动态导入模块 button.addEventListener(click, async () > {cons…...

从 AMQP 到 RabbitMQ:核心组件设计与工作原理(一)

一、引言 ** 在当今分布式系统盛行的时代,消息队列作为一种关键的中间件技术,承担着系统间异步通信、解耦和削峰填谷的重要职责。AMQP(Advanced Message Queuing Protocol)作为一种高级消息队列协议,为消息队列的实现…...

Java进阶---JVM

JVM概述 JVM作用: 负责将字节码翻译为机器码,管理运行时内存 JVM整体组成部分: 类加载系统(ClasLoader):负责将硬盘上的字节码文件加载到内存中 运行时数据区(RuntimeData Area):负责存储运行时各种数据 执行引擎(Ex…...

鸿蒙OSUniApp离线优先数据同步实战:打造无缝衔接的鸿蒙应用体验#三方框架 #Uniapp

UniApp离线优先数据同步实战:打造无缝衔接的鸿蒙应用体验 最近在开发一个面向鸿蒙生态的UniApp应用时,遇到了一个有趣的挑战:如何在网络不稳定的情况下保证数据的实时性和可用性。经过一番探索和实践,我们最终实现了一套行之有效…...

地震资料裂缝定量识别——学习计划

学习计划 地震资料裂缝定量识别——理解常规采集地震裂缝识别方法纵波各向异性方法蚁群算法相干体及倾角检测方法叠后地震融合属性方法裂缝边缘检测方法 非常规采集地震裂缝识别方法P-S 转换波方法垂直地震剖面方法 学习计划 地震资料裂缝定量识别——理解 地震资料裂缝识别&a…...

C++ 检查一条线是否与圆接触或相交(Check if a line touches or intersects a circle)

给定一个圆的圆心坐标、半径 > 1 的圆心坐标以及一条直线的方程。任务是检查给定的直线是否与圆相交。有三种可能性: 1、线与圆相交。 2、线与圆相切。 3、线在圆外。 注意:直线的一般方程是 a*x b*y c 0,因此输入中只给出常数 a、b、…...

23. Merge k Sorted Lists

目录 题目描述 方法一、k-1次两两合并 方法二、分治法合并 方法三、使用优先队列 题目描述 23. Merge k Sorted Lists 方法一、k-1次两两合并 选第一个链表作为结果链表,每次将后面未合并的链表合并到结果链表中,经过k-1次合并,即可得到…...

每日算法刷题计划Day20 6.2:leetcode二分答案3道题,用时1h20min

9.3048.标记所有下标的最早秒数(中等) 3048. 标记所有下标的最早秒数 I - 力扣(LeetCode) 思想 1.给你两个下标从 1 开始的整数数组 nums 和 changeIndices ,数组的长度分别为 n 和 m 。 一开始,nums 中所有下标都是未标记的&a…...

Spring Security安全实践指南

安全性的核心价值 用户视角的数据敏感性认知 从终端用户角度出发,每个应用程序都涉及不同级别的数据敏感度。以电子邮件服务与网上银行为例:前者内容泄露可能仅造成隐私困扰,而后者账户若被操控将直接导致财产损失。这种差异体现了安全防护需要分级实施的基本原则: // 伪…...

Unity + HybirdCLR热更新 入门篇

官方文档 HybridCLR | HybridCLRhttps://hybridclr.doc.code-philosophy.com/docs/intro 什么是HybirdCLR? HybridCLR(原名 huatuo)是一个专为 Unity 项目设计的C#热更新解决方案,它通过扩展 IL2CPP 运行时,使其支持动态加载和…...

QuickBASIC QB64 支持 64 位系统和跨平台Linux/MAC OS

QuickBASIC 的现代继任者 QB64 已发展成为一个功能强大的开源项目,支持 64 位系统和跨平台开发。以下是详细介绍: 项目首页 - QB64pe:The QB64 Phoenix Edition Repository - GitCode https://gitcode.com/gh_mirrors/qb/QB64pe 1. QB64 概述 官网&am…...

ElasticSearch迁移至openGauss

Elasticsearch 作为一种高效的全文搜索引擎,广泛应用于实时搜索、日志分析等场景。而 openGauss,作为一款企业级关系型数据库,强调事务处理与数据一致性。那么,当这两者的应用场景和技术架构发生交集时,如何实现它们之…...

【C语言极简自学笔记】项目开发——扫雷游戏

一、项目概述 1.项目背景 扫雷是一款经典的益智游戏,由于它简单而富有挑战性的玩法深受人们喜爱。在 C 语言学习过程中,开发扫雷游戏是一个非常合适的实践项目,它能够综合运用 C 语言的多种基础知识,如数组、函数、循环、条件判…...

Global Security Markets 第5章知识点总结

一、章节核心内容概述 《Global Securities Markets》第五章聚焦全球主要证券交易所、关联存管机构及跨境交易实务,重点解析“乘客市场(Passenger Markets)”概念与合规风险,同时涵盖交易费用、监管规则等实操要点。考虑到市场的…...

电子电路:4017计数器工作原理解析

4017是CMOS十进制计数器/分频器,它属于CD4000系列,工作电压范围比较宽,可能3V到15V。我记得它有10个译码输出端,每个输出端依次在高电平和低电平之间循环,可能用于时序控制或者LED显示什么的。 4017内部应该由计数器和译码器两部分组成。计数器部分可能是一个约翰逊计数器…...

Vim 中设置插入模式下输入中文

在 Vim 中设置插入模式下输入中文需要配置输入法切换和 Vim 的相关设置。以下是详细步骤: 1. 确保系统已安装中文输入法 在 Linux 系统中,常用的中文输入法有: IBus(推荐):支持拼音、五笔等Fcitx&#xf…...

GitHub 趋势日报 (2025年05月31日)

📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 1153 prompt-eng-interactive-tutorial 509 BillionMail 435 ai-agents-for-begin…...

Maven概述,搭建,使用

一.Maven概述 Maven是Apache软件基金会的一个开源项目,是一个有优秀的项目构建(创建)工具,它用来帮助开发者管理项目中的jar,以及jar之间的依赖关系,完成项目的编译,测试,打包和发布等工作. 我在当前学习阶段遇到过的jar文件: MySQL官方提供的JDBC驱动文件,通常命名为mysql-…...

基于大模型的数据库MCP Server设计与实现

基于大模型的数据库MCP Server设计与实现 引言 随着大语言模型(LLM, Large Language Model)能力的不断提升,AI Agent(智能体)正在从简单的对话问答,向更复杂的自动化任务执行和业务流程管理演进。在企业和开发者的实际需求中,数据库操作是最常见、最核心的场景之一。如…...

【前端】macOS 的 Gatekeeper 安全机制阻止你加载 bcrypt_lib.node 文件 如何解决

这个弹窗是 macOS 的 Gatekeeper 安全机制阻止你加载 bcrypt_lib.node 文件,因为它不是 Apple 签名的文件。 你想 “忽视” 它,其实是让系统允许这个 .node 原生模块运行,解决方式如下: sudo xattr -d com.apple.quarantine nod…...

Unity 环境搭建

Unity是一款游戏引擎,可用于开发各种类型的游戏和交互式应用程序。它由Unity Technologies开发,并在多个平台上运行,包括Windows、macOS、Linux、iOS、Android和WebGL。Unity也支持虚拟现实(VR)和增强现实(AR)技术,允许用户构建逼…...

【入门】【练9.3】 加四密码

| 时间限制:C/C 1000MS,其他语言 2000MS 内存限制:C/C 64MB,其他语言 128MB 难度:中等 分数:100 OI排行榜得分:12(0.1*分数2*难度) 出题人:root | 描述 要将 China…...

使用 SASS 与 CSS Grid 实现鼠标悬停动态布局变换效果

最终效果概述 页面为 3x3 的彩色格子网格;当鼠标悬停任意格子,所在的行和列被放大;使用纯 CSS 实现,无需 JavaScript;利用 SASS 的模块能力大幅减少冗余代码。 HTML 结构 我们使用非常基础的结构,9 个 .i…...

Node.js 全栈开发方向常见面试题

Node.js 全栈开发”方向的面试题**,这类岗位通常包括: 后端:Node.js(Express/Nest)、数据库、REST API、安全、部署等 前端:React/Vue(部分可能含 Next.js)、API 调用、状态管理等 …...

Spring如何实现组件扫描与@Component注解原理

Spring如何实现组件扫描与Component注解原理 注解配置与包扫描的实现机制一、概述:什么是注解配置与包扫描?二、处理流程概览三、注解定义ComponentScope 四、核心代码结构1. ClassPathScanningCandidateComponentProvider2. ClassPathBeanDefinitionSca…...

历年四川大学计算机保研上机真题

2025四川大学计算机保研上机真题 2024四川大学计算机保研上机真题 2023四川大学计算机保研上机真题 在线测评链接:https://pgcode.cn/school 分数求和 题目描述 有一分数序列: 2 / 1 2/1 2/1, 3 / 2 3/2 3/2, 5 / 3 5/3 5/3, 8 / 5 8/5 8/5, 13 /…...

gcc符号表生成机制

符号表生成机制 我们以C语言的编译链接过程为例,详细讲解符号表(Symbol Table)的流程,涵盖编译和链接两个阶段。理解符号表是理解链接器如何解决符号引用(如函数、变量)的关键。 符号表分为两种&#xff…...

达梦数据库 Windows 系统安装教程

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开…...