当前位置: 首页 > 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);}
}

http://www.lryc.cn/news/2396952.html

相关文章:

  • 内存管理--《Hello C++ Wrold!》(8)--(C/C++)--深入剖析new和delete的使用和底层实现
  • JavaScript性能优化实战指南(详尽分解版)
  • 从 AMQP 到 RabbitMQ:核心组件设计与工作原理(一)
  • Java进阶---JVM
  • 鸿蒙OSUniApp离线优先数据同步实战:打造无缝衔接的鸿蒙应用体验#三方框架 #Uniapp
  • 地震资料裂缝定量识别——学习计划
  • C++ 检查一条线是否与圆接触或相交(Check if a line touches or intersects a circle)
  • 23. Merge k Sorted Lists
  • 每日算法刷题计划Day20 6.2:leetcode二分答案3道题,用时1h20min
  • Spring Security安全实践指南
  • Unity + HybirdCLR热更新 入门篇
  • QuickBASIC QB64 支持 64 位系统和跨平台Linux/MAC OS
  • ElasticSearch迁移至openGauss
  • 【C语言极简自学笔记】项目开发——扫雷游戏
  • Global Security Markets 第5章知识点总结
  • 电子电路:4017计数器工作原理解析
  • Vim 中设置插入模式下输入中文
  • GitHub 趋势日报 (2025年05月31日)
  • Maven概述,搭建,使用
  • 基于大模型的数据库MCP Server设计与实现
  • 【前端】macOS 的 Gatekeeper 安全机制阻止你加载 bcrypt_lib.node 文件 如何解决
  • Unity 环境搭建
  • 【入门】【练9.3】 加四密码
  • 使用 SASS 与 CSS Grid 实现鼠标悬停动态布局变换效果
  • Node.js 全栈开发方向常见面试题
  • Spring如何实现组件扫描与@Component注解原理
  • 历年四川大学计算机保研上机真题
  • gcc符号表生成机制
  • 达梦数据库 Windows 系统安装教程
  • unix/linux source 命令,其基本概念、定义、性质、定理