12.2Swing中JButton简单分析

news2025/6/5 5:33:03

 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() {
                @Override
                public void mousePressed(MouseEvent e) {
                    if (button1.contains(e.getPoint())) {
                        isButton1Pressed = true;
                        repaint();
                    } else if (button2.contains(e.getPoint())) {
                        isButton2Pressed = true;
                        repaint();
                    }
                }

                @Override
                public 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();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;

            // 设置抗锯齿
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                 RenderingHints.VALUE_ANTIALIAS_ON);

            // 绘制按钮 1
            if (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);

            // 绘制按钮 2
            if (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.coloradmin.cn/o/2396952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

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

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

Java进阶---JVM

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

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次合并,即可得到…

Unity + HybirdCLR热更新 入门篇

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

ElasticSearch迁移至openGauss

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

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

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

Maven概述,搭建,使用

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

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…

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

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

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

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

【Java EE初阶】计算机是如何⼯作的

计算机是如何⼯作的 计算机发展史冯诺依曼体系(Von Neumann Architecture)CPU指令(Instruction)CPU 是如何执行指令的(重点) 操作系统(Operating System)进程(process) 进程 PCB 中的…

RAG理论基础总结

目录 概念 流程 文档收集和切割 读取文档 转换文档 写入文档 向量转换和存储 搜索请求构建 向量存储工作原理 向量数据库 文档过滤和检索 检索前 检索 检索后 查询增强和关联 QuestionAnswerAdvisor查询增强 高级RAG架构 自纠错 RAG(C-RAG&#xf…

列表推导式(Python)

[表达式 for 变量 in 列表] 注意:in后面不仅可以放列表,还可以放range ()可迭代对象 [表达式 for 变量 in 列表 if 条件]

一天搞懂深度学习--李宏毅教程笔记

目录 1. Introduction of Deep Learning1.1. Neural Network - A Set of Function1.2. Learning Target - Define the goodness of a function1.3. Learn! - Pick the best functionLocal minimaBackpropagation 2. Tips for Training Deep Neural Network3. Variant of Neural…

python打卡训练营打卡记录day43

复习日 作业: kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化 进阶:并拆分成多个文件 数据集来源:Flowers Recognition 选择该数据集原因: 中等规模:4242张图片 - 训练快速但足够展示效…

【QT控件】QWidget 常用核心属性介绍 -- 万字详解

目录 一、控件概述 二、QWidget 核心属性 2.1 核心属性概览 2.2 enabled ​编辑 2.3 geometry 2.4 windowTitle 2.5 windowIcon 使用qrc文件管理资源 2.6 windowOpacity 2.7 cursor 2.8 font ​编辑 2.9 toolTip 2.10 focusPolicy 2.11 styleSheet QT专栏&…

uniapp-商城-77-shop(8.2-商品列表,地址信息添加,级联选择器picker)

地址信息,在我们支付订单上有这样一个接口,就是物流方式,一个自提,我们就显示商家地址。一个是外送,就是用户自己填写的地址。 这里先说说用户的地址添加。需要使用到的一些方式方法,主要有关于地址选择器,就是uni-data-picker级联选择。 该文介绍了电商应用中地址信息处…