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

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);}
}

http://www.xdnf.cn/news/10567.html

相关文章:

  • 05-power BI高级筛选器filter与Values人工造表
  • 【烧脑算法】不定长滑动窗口:从动态调整到精准匹配以灵活特性实现高效破题
  • 第2篇:数据库连接池原理与自定义连接池开发实践
  • 01 Ubuntu20.04下编译QEMU8.2.4,交叉编译32位ARM程序,运行ARM程序的方法
  • 基于GPT-SoVITS-v4-TTS的音频文本推理,流式生成
  • 第12次13: 修改登录密码
  • 《 C++ 点滴漫谈: 四十 》文本的艺术:C++ 正则表达式的高效应用之道
  • Linux学习笔记:shell脚本篇(1)
  • 【基于阿里云搭建数据仓库(离线)】IDEA导出Jar包(包括第三方依赖)
  • Perl One-liner 数据处理——基础语法篇【匠心】
  • Go 语言 + Word 文档模板:WordZero 引擎如何让企业文档处理效率提升 300%?
  • 使命召唤16:现代战争 MOD整合包 豪华中文 免安 离线运行版
  • 做好 4个基本动作,拦住性能优化改坏原功能的bug
  • Hadoop学习笔记
  • 开源的JT1078转GB28181服务器
  • 一次借助ChatGPT抵御恶意攻击的经历,为个人服务器添加自动防御系统Fail2ban
  • Vue 项目创建教程 (开发前的准备工作保姆级辅助文档)
  • 系统调用与程序接口的关系
  • 业务到解决方案构想
  • JVM——从JIT到AOT:JVM编译器的云原生演进之路
  • Modern C++(二)预处理器及表达式
  • 6个月Python学习计划 Day 12 - 字符串处理 文件路径操作
  • 企业级应用狂潮:从Spotify到LinkedIn的Llama实战手册
  • MySQL:视图+用户管理+访问+连接池原理
  • 任务26:绘制1-12月各省份平均气温和预测可视化图形(折线
  • Python数学可视化——显函数、隐函数及复杂曲线的交互式绘图技术
  • Linux(10)——第二个小程序(自制shell)
  • 7.4-Creating data loaders for an instruction dataset
  • debian12.9或ubuntu,vagrant离线安装插件vagrant-libvirt,20250601
  • 第二章支线四 ·响应圣坛:媒体查询与移动适配