Swing中如何实现快捷键绑定和修改

news2024/11/16 1:50:16

在许许多多市面上常见的桌面软件中, 可以使用快捷键操作, 比如像微信一样,使用Alt+A 可以打开截图窗口,如果不习惯于Alt+A按键时,还可以打开设置去修改。

如果在swing中也想实现一个快捷键绑定和修改的操作,那么应该如何操作?

请添加图片描述

一、实现思路

1.1创建事件Action

创建一个Action 对象,实现actionPerformed方法即可。
// 该事件触发时, 会弹出bindKeyMapButton按钮上绑定的按键值。

   
  Action actionListener = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MessageUtil.ok(bindKeyMapButton, "Key:" + SwingCoreUtil.keyStroke2Str((KeyStroke) bindKeyMapButton.getClientProperty("activeKeyStroke")));
            }
        };

1.2.为按钮绑定默认快捷键

创建一个KeyStroke按键,绑定键盘Ctrl+Q ,那么键盘按下Ctrl+Q时,会触发事件Action

        KeyStroke defaultKeyStroke = KeyStroke.getKeyStroke("ctrl Q");
        bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(defaultKeyStroke, "active");
        bindKeyMapButton.getActionMap().put("active", actionListener);
        bindKeyMapButton.putClientProperty("activeKeyStroke", defaultKeyStroke);

1.3. 创建Jtextfield来监听键盘事件

当键盘被按下时,记录符合要求的按键值,并显示文本到输入框内。

   keymapText.addKeyListener(new KeyAdapter() {

            private KeyStroke keyStroke;

            @Override
            public void keyPressed(KeyEvent e) {
                keyStroke = null;
                // 无ctrl/shift/alt 组合键
                if (e.getModifiersEx() == 0 || e.getKeyCode() == KeyEvent.VK_UNDEFINED) {
                    keyStroke = null;
                } else {
                    keyStroke = KeyStroke.getKeyStrokeForEvent(e);
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {

                if (keyStroke == null) {
                    return;
                }
                int keyCode = keyStroke.getKeyCode();
                // 禁止某些键
                if (keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT
                        || keyCode == KeyEvent.VK_ALT || keyCode == KeyEvent.VK_ALT_GRAPH
                        || keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_PRINTSCREEN
                ) {
                    return;
                }
                String keyStrokeText = SwingCoreUtil.keyStroke2Str(keyStroke);
                keymapText.setText(keyStrokeText);
                keymapText.putClientProperty("bindKeyStroke", keyStroke);

            }
        });

1.4. 监听Jtextfield文本变化更新button的按键绑定

对button移除按键操作,并绑定Jtextfield中设置的新按键值。

        keymapText.getDocument().addDocumentListener(new ChangeDocumentListener() {
            @Override
            public void update(DocumentEvent e) {
                bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove((KeyStroke)                   bindKeyMapButton.getClientProperty("activeKeyStroke"));
                KeyStroke keyStroke = (KeyStroke) keymapText.getClientProperty("bindKeyStroke");
                if (keyStroke != null) {
                    bindKeyMapButton.putClientProperty("activeKeyStroke", keyStroke);
                    bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, "active");
                }
            }
        });

1.5. 完整示例代码

cn.note.swing.core 引用自swing-helper

import cn.note.swing.core.listener.ChangeDocumentListener;
import cn.note.swing.core.util.ButtonFactory;
import cn.note.swing.core.util.FrameUtil;
import cn.note.swing.core.util.MessageUtil;
import cn.note.swing.core.util.SwingCoreUtil;
import cn.note.swing.core.view.AbstractMigView;
import cn.note.swing.core.view.theme.ThemeFlatLaf;
import net.miginfocom.swing.MigLayout;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

/**
 * 测试keymap
 *
 * @author jee
 * @version 1.0
 */
public class KeymapV1Test extends AbstractMigView {
    private JTextField keymapText;
    private JButton bindKeyMapButton;

    @Override
    protected MigLayout defineMigLayout() {
        return new MigLayout("center,nogrid","grow","grow");
    }

    @Override
    protected void init() {
        keymapText = new JTextField();
        keymapText.setEditable(false);
        bindKeyMapButton = ButtonFactory.primaryButton("");
    }

    @Override
    public void bindEvents() {

        // 绑定按钮按键
        Action actionListener = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MessageUtil.ok(bindKeyMapButton, "Key:" + SwingCoreUtil.keyStroke2Str((KeyStroke) bindKeyMapButton.getClientProperty("activeKeyStroke")));
            }
        };
        KeyStroke defaultKeyStroke = KeyStroke.getKeyStroke("ctrl Q");
        bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(defaultKeyStroke, "active");
        bindKeyMapButton.getActionMap().put("active", actionListener);
        bindKeyMapButton.putClientProperty("activeKeyStroke", defaultKeyStroke);
        bindKeyMapButton.setText("Init BindKey:" + SwingCoreUtil.keyStroke2Str(defaultKeyStroke));

        // 监听按键更改
        keymapText.addKeyListener(new KeyAdapter() {

            private KeyStroke keyStroke;

            @Override
            public void keyPressed(KeyEvent e) {
                keyStroke = null;
                // 无ctrl/shift/alt 组合键
                if (e.getModifiersEx() == 0 || e.getKeyCode() == KeyEvent.VK_UNDEFINED) {
                    keyStroke = null;
                } else {
                    keyStroke = KeyStroke.getKeyStrokeForEvent(e);
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {

                if (keyStroke == null) {
                    return;
                }
                int keyCode = keyStroke.getKeyCode();
                // 禁止某些键
                if (keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT
                        || keyCode == KeyEvent.VK_ALT || keyCode == KeyEvent.VK_ALT_GRAPH
                        || keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_PRINTSCREEN
                ) {
                    return;
                }
                String keyStrokeText = SwingCoreUtil.keyStroke2Str(keyStroke);
                keymapText.setText(keyStrokeText);
                keymapText.putClientProperty("bindKeyStroke", keyStroke);

            }
        });

        keymapText.getDocument().addDocumentListener(new ChangeDocumentListener() {
            @Override
            public void update(DocumentEvent e) {
                bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove((KeyStroke) bindKeyMapButton.getClientProperty("activeKeyStroke"));
                KeyStroke keyStroke = (KeyStroke) keymapText.getClientProperty("bindKeyStroke");
                if (keyStroke != null) {
                    bindKeyMapButton.putClientProperty("activeKeyStroke", keyStroke);
                    bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, "active");
                }
            }
        });
    }

    @Override
    protected void render() {
        view.add(new JLabel("keymap V1:"));
        view.add(keymapText, "w 200!");
        view.add(bindKeyMapButton, "wrap");
    }


    public static void main(String[] args) {
        ThemeFlatLaf.install();
        FrameUtil.launchTest(KeymapV1Test.class);
    }

二、拓展

但是,就像上述操作一样,如果页面上只有一个按键绑定还好,如果有多个按键呢 ? 每一次更改都需要去实现KeyListener 和DocumentListener , 所以为什么大家比较认可java的核心概念:抽象、封装、继承、多态 呢?
在这里插入图片描述

  • 继承
    普通输入框既然不能满足了, 那么扩展一个可以支持按键的输入框,但是还想具备原来输入框的一些功能,
    那么按键输入框只需要继承普通输入框即可。
  • 封装与抽象
    在第4步中输入框文本变化时,需要与绑定的按钮进行交互,耦合太大了,那么如何降低耦合呢? 可以让按键输入框释放一个事件操作,该事件操作与按钮建立绑定关系,那么这一步就是封装与抽象了。
    如何更优雅的封装与抽象,取决于代码的汇总量和阅读优秀代码的眼界。
    为什么经常想重构代码,觉得代码比较粗糙不堪呢?可能跟你的代码量上去了,阅读和抒写了更优秀的代码,所以大牛看小白的代码就是垃圾,小白看大牛的代码就是神作。
    所以,如果你是过了这境界的大牛,还望忽略前进中的小牛。
  • 多态
    当确定了要创建一个按键输入框时,可能随着时间的推移或者功能上的扩展,造成它不够健壮了,但是它还占用了你喜欢的名字, 那么多态就是一个很好的解决方案了。

    比如: 在1.0的是否创建了一个KeymapTextField,2.0的时候创建一个增强版本ExtKeymapTextField
    KeymapTextField keymapTextField=new KeymapTextField(); // V1.0
    keymapTextField=new ExtKeymapTextField(); //V2.0

2.1 封装键盘输入框

JEKeymapTextField 内置封装了不可编辑 、禁止选择、监听键盘按键回显 功能,对外暴露了setKeyStrokeChangedListener 让外界可以感知实时变化的KeyStroke

import cn.note.swing.core.listener.ChangeDocumentListener;
import cn.note.swing.core.util.SwingCoreUtil;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.function.Consumer;

/**
 * 快捷键绑定文本框
 *
 * @author jee
 * @version 1.0
 */
public class JEKeymapTextField extends JTextField implements KeyListener {

    /* 绑定按键*/
    private KeyStroke keyStroke;

    /* 快捷键改变监听*/
    private Consumer<KeyStroke> keyStrokeChangedListener;

    public JEKeymapTextField() {
        super.setEditable(false);
        // 绑定按键
        super.addKeyListener(this);
        bindEvents();
    }

    @Override
    public void moveCaretPosition(int pos) {
        //nothing 禁止光标移动,即不可选择
    }

    private void bindEvents() {
        // 文本框内容改变时触发
        this.getDocument().addDocumentListener(new ChangeDocumentListener() {
            @Override
            public void update(DocumentEvent e) {
                if (keyStrokeChangedListener != null) {
                    keyStrokeChangedListener.accept(keyStroke);
                }
            }
        });
    }

    @Override
    public void keyTyped(KeyEvent e) {
        //nothing
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // 无ctrl/shift/alt 组合键时不绑定
        if (e.getModifiersEx() == 0 || e.getKeyCode() == KeyEvent.VK_UNDEFINED) {
            keyStroke = null;
        } else {
            keyStroke = KeyStroke.getKeyStrokeForEvent(e);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        if (keyStroke == null) {
            return;
        }
        int keyCode = keyStroke.getKeyCode();
        // 禁止某些键
        if (keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT
                || keyCode == KeyEvent.VK_ALT || keyCode == KeyEvent.VK_ALT_GRAPH
                || keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_PRINTSCREEN
        ) {
            return;
        }
        String keyStrokeText = SwingCoreUtil.keyStroke2Str(keyStroke);
        super.setText(keyStrokeText);
    }

    public void setKeyStrokeChangedListener(Consumer<KeyStroke> keyStrokeChangedListener) {
        this.keyStrokeChangedListener = keyStrokeChangedListener;
    }
}

2.1 封装后测试示例

  • 代码简洁
  • 耦合性更低
  • 逻辑更清楚
    如果对JEKeymapTextField 进行拓展增强 (非破坏性对外方法参数变更时)则不会影响KeymapV2Test代码修改。
import cn.note.swing.core.util.ButtonFactory;
import cn.note.swing.core.util.FrameUtil;
import cn.note.swing.core.util.MessageUtil;
import cn.note.swing.core.util.SwingCoreUtil;
import cn.note.swing.core.view.AbstractMigView;
import cn.note.swing.core.view.theme.ThemeFlatLaf;
import net.miginfocom.swing.MigLayout;

import javax.swing.*;
import java.awt.event.ActionEvent;

/**
 * 测试keymap
 *
 * @author jee
 * @version 2.0
 */
public class KeymapV2Test extends AbstractMigView {
    private JEKeymapTextField keymapText;
    private JButton bindKeyMapButton;

    @Override
    protected MigLayout defineMigLayout() {
        return new MigLayout("center,nogrid","grow","grow");
    }

    @Override
    protected void init() {
        keymapText = new JEKeymapTextField();
        bindKeyMapButton = ButtonFactory.primaryButton("");
    }

    @Override
    public void bindEvents() {

        // 绑定事件按钮
        Action actionListener = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MessageUtil.ok(bindKeyMapButton, "Key:" + SwingCoreUtil.keyStroke2Str((KeyStroke) bindKeyMapButton.getClientProperty("activeKeyStroke")));
            }
        };
        KeyStroke defaultKeyStroke = KeyStroke.getKeyStroke("ctrl pressed Q");
        bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(defaultKeyStroke, "active");
        bindKeyMapButton.getActionMap().put("active", actionListener);
        bindKeyMapButton.putClientProperty("activeKeyStroke", defaultKeyStroke);
        bindKeyMapButton.setText("Init BindKey:" + SwingCoreUtil.keyStroke2Str(defaultKeyStroke));

        // 监听按键更改
        keymapText.setKeyStrokeChangedListener(keyStroke -> {
            bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove((KeyStroke) bindKeyMapButton.getClientProperty("activeKeyStroke"));
            bindKeyMapButton.putClientProperty("activeKeyStroke", keyStroke);
            bindKeyMapButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, "active");
        });
    }

    @Override
    protected void render() {
        view.add(new JLabel("keymap V2:"));
        view.add(keymapText, "w 200!");
        view.add(bindKeyMapButton, "wrap");
    }

    public static void main(String[] args) {
        ThemeFlatLaf.install();
        FrameUtil.launchTest(KeymapV2Test.class);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2067506.html

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

相关文章

《计算机操作系统》(第4版)第8章 磁盘存储器的管理 复习笔记

第8章 磁盘存储器的管理 一 、外存的组织方式 1. 连续组织方式(连续分配方式) (1)概述 ①定义 连续组织方式要求为每一个文件分配一组相邻接的盘块。磁盘空间的联系组织方式如图8-1所示。 ②记录方法 在目录项的“文件物理地址”字段中记录该文件第一个记录所在的盘块号和文件…

【Docker深入浅出】Docker引擎架构介绍

文章目录 一. docker引擎介绍1. Docker daemon&#xff1a;实现Docker API&#xff0c;通过API管理容器2. containerd&#xff1a;负责容器的生命周期3. runc&#xff1a;用于创建和启动容器 二. 启动容器的过程1. 启动过程2. docker daemon的维护不会影响到运行中的容器3. shi…

依靠 VPN 生存——探索 VPN 后利用技术

执行摘要 在这篇博文中,Akamai 研究人员强调了被忽视的 VPN 后利用威胁;也就是说,我们讨论了威胁行为者在入侵 VPN 服务器后可以用来进一步升级入侵的技术。 我们的发现包括影响 Ivanti Connect Secure 和 FortiGate VPN 的几个漏洞。 除了漏洞之外,我们还详细介绍了一组…

C语言试题(含答案解析)

单选 1.下面C程序的运行结果为&#xff08;&#xff09; int main(void) {printf("%d", B < A);return 0; }A.编译错误 B.1 C.0 D.运行错误 A’的ascii码值为65&#xff0c;‘B’的ascii码值为66&#xff0c;‘B’<‘A’是不成立的&#xff0c;返回0&#xf…

spring学习(1)

目录 一、是什么 二、IOC思想 2.1 IOC创建对象的方式 三、Spring的配置 3.1 别名 3.2 Bean的配置 3.3 import 四、DI依赖注入 4.1 构造器注入 4.2 Set方式注入【重点】 4.3 拓展方式注入 五、Bean的自动装配 5.1 byName自动装配 5.2 byType自动装配 5.3 注解实现…

Vue小知识大杂烩

一、Vue组件的三大部分&#xff1a;template、Script、Style template --> 组件的模板结构 写html的地方 注意&#xff1a;<template> 是 vue 提供的容器标签&#xff0c;只起到包裹性质的作用&#xff0c;它不会被渲染为真正的 DOM 元素。 script -> 组件的…

超声波清洗机什么牌子值得入手? 清洁力好的超声波清洗机推荐

对于眼镜佩戴人士而言&#xff0c;超声波清洗机无疑是清洁神器&#xff01;它凭借高频振动技术&#xff0c;能深入眼镜的每一细微处及手洗难以触及的缝隙&#xff0c;有效清除顽固污渍&#xff0c;不仅大幅提高清洁效率&#xff0c;而且清洁质量远胜传统方法。随着超声波清洗机…

Linux下快速搭建七日杀官方私人服务器教程

今天给大家分享一下七日杀的个人开服教程&#xff0c;本教程基于Linux系统开发&#xff0c;推荐有一定基础的小伙伴尝试&#xff01;如果你没有Linux的基础但实在想开的小伙伴可以根据以下教程一步步进行操作&#xff0c;后续这边也会上架对应视频操作 架设前准备&#xff1a; …

Redis篇三:在Ubuntu下安装Redis

文章目录 1. 安装Redis2. 更改Redis的IP3. 使用redis自带的客户端来连接服务器4. Redis的客户端介绍 1. 安装Redis sudo apt install redis2. 更改Redis的IP 刚安装的Redis的ip是一个本地环回的ip&#xff0c;也就是只能由当前主机上的客户端进行访问&#xff0c;跨主机就访问…

IO进程线程 0823作业

作业 创建子父进程&#xff0c;子进程将1.txt内容拷贝到2.txt中&#xff0c;父进程将3.txt内容拷贝到4.txt中。 #include <myhead.h> int main(int argc, const char *argv[]) {pid_t ID;ID fork();if(ID > 0){int fd1;fd1 open("./3.txt",O_RDONLY);if(…

js 键盘监听 组合键

今天分享如何快速实现js快捷键监听 所需环境&#xff1a; 浏览器js 实现目标 mac/win兼容&#xff0c;一套代码&#xff0c;多个平台支持快捷键监听/单按键监听事件是否冒泡可设置使用方式简单快速挂载与卸载4行代码实现组合键监听 代码原理 把键盘监听事件挂载在documen…

c#-DataGridView控件实现分页

有时候我们需要进行分页显示&#xff0c;第一方面是在大数据量下可以降低卡顿&#xff0c;另一方面也是方便查找。 首先划重点&#xff0c;如果卡顿&#xff0c;不要用单元格填充的方式去刷新&#xff0c;用绑定数据源的方式比较高效&#xff01; 下面重点讲如何使用数据源绑定…

正式收官!阿里云携手优酷,用AI重塑影视IP创新边界

影视行业的新一轮创作风潮&#xff0c;将由AI掀起。 GPT和Sora等先进AI模型的出现&#xff0c;带动影视行业进入一场前所未有的创意变革。当前&#xff0c;在角色创作、脚本生成、营销策略等方面&#xff0c;AI已经展现了强大的潜力。而作为影视创作的“灵魂”&#xff0c;影视…

重新审视 ChatGPT 和 Elasticsearch:RAG 真正将应用程序紧密结合在一起

作者&#xff1a;来自 Elastic Jeff Vestal 关注博客 ChatGPT 和 Elasticsearch&#xff1a;OpenAI 遇到私人数据。 在此博客中&#xff0c;你将了解如何&#xff1a; 创建 Elasticsearch Serverless 项目创建推理端点以使用 ELSER 生成嵌入使用语义文本字段进行自动分块并调…

SpringBoot如何进行全局异常处理?

1.为什么需要全局异常处理&#xff1f; 在日常开发中&#xff0c;为了不抛出异常堆栈信息给前端页面&#xff0c;每次编写Controller层代码都要尽可能的catch住所有service层、dao层等异常&#xff0c;代码耦合性较高&#xff0c;且不美观&#xff0c;不利于后期维护。 应用场…

基于java的少儿编程网上报名系统+vue

TOC ssm006基于java的少儿编程网上报名系统vue 研究背景 近年来&#xff0c;随着网络技术的不断发展&#xff0c;越来越多人在网站查询各种信息&#xff0c;少儿编程网上报名系统对用户和管理员都有很大帮助&#xff0c;少儿编程网上报名系统通过和数据库管理系软件协作来实…

基于STM32开发的智能安防报警系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化传感器数据采集与处理报警控制与通知Wi-Fi通信与远程监控应用场景 家庭安防管理商铺和办公室的智能安防常见问题及解决方案 常见问题解决方案结论 1. 引言 随着智能家居和物联网技…

拟合的置信区间

目标图: 图片来源:Fig. 4e from Arwani, Ruth Theresia, et al. "Stretchable ionic–electronic bilayer hydrogel electronics enable in situ detection of solid-state epidermal biomarkers." Nature Materials (2024): 1-8. 1. 数据输入 假设原始数据如下:…

书生大模型实战营第三期基础岛第二课——8G 显存玩转书生大模型 Demo

8G 显存玩转书生大模型 Demo 基础任务进阶作业一&#xff1a;进阶作业二&#xff1a; 基础任务 使用 Cli Demo 完成 InternLM2-Chat-1.8B 模型的部署&#xff0c;并生成 300 字小故事&#xff0c;记录复现过程并截图。 创建conda环境 # 创建环境 conda create -n demo pytho…

协作新选择:即时白板在线白板软件分享

在团队合作中&#xff0c;产品经理扮演着至关重要的角色&#xff0c;他们不仅是产品与用户之间的纽带&#xff0c;更是产品性能和用户需求的桥梁。他们需要深入参与产品的研发过程&#xff0c;并与研发团队保持紧密的沟通。因此&#xff0c;产品经理需要一款高效的协作工具来提…