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

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

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

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

请添加图片描述

一、实现思路

1.1创建事件Action

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

   Action actionListener = new AbstractAction() {@Overridepublic 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;@Overridepublic 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);}}@Overridepublic 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() {@Overridepublic 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;@Overrideprotected MigLayout defineMigLayout() {return new MigLayout("center,nogrid","grow","grow");}@Overrideprotected void init() {keymapText = new JTextField();keymapText.setEditable(false);bindKeyMapButton = ButtonFactory.primaryButton("");}@Overridepublic void bindEvents() {// 绑定按钮按键Action actionListener = new AbstractAction() {@Overridepublic 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;@Overridepublic 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);}}@Overridepublic 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() {@Overridepublic 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");}}});}@Overrideprotected 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();}@Overridepublic void moveCaretPosition(int pos) {//nothing 禁止光标移动,即不可选择}private void bindEvents() {// 文本框内容改变时触发this.getDocument().addDocumentListener(new ChangeDocumentListener() {@Overridepublic void update(DocumentEvent e) {if (keyStrokeChangedListener != null) {keyStrokeChangedListener.accept(keyStroke);}}});}@Overridepublic void keyTyped(KeyEvent e) {//nothing}@Overridepublic void keyPressed(KeyEvent e) {// 无ctrl/shift/alt 组合键时不绑定if (e.getModifiersEx() == 0 || e.getKeyCode() == KeyEvent.VK_UNDEFINED) {keyStroke = null;} else {keyStroke = KeyStroke.getKeyStrokeForEvent(e);}}@Overridepublic 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;@Overrideprotected MigLayout defineMigLayout() {return new MigLayout("center,nogrid","grow","grow");}@Overrideprotected void init() {keymapText = new JEKeymapTextField();bindKeyMapButton = ButtonFactory.primaryButton("");}@Overridepublic void bindEvents() {// 绑定事件按钮Action actionListener = new AbstractAction() {@Overridepublic 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");});}@Overrideprotected 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.mrgr.cn/news/5979.html

相关文章:

  • clickhouse自定义函数的困惑
  • 2024.8.23(docker)
  • Java二十三种设计模式-命令模式(18/23)
  • 将前端上传的文件同步到sftp服务器
  • 如何让ChatGPT说话更像人类
  • Linux中nano编辑器详解
  • ChaCha20:高效且安全的流密码算法
  • Excel 中找出每列第一个和最后一个非空格对应的行--Excel难题#87
  • 基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(三)---创建自定义激光雷达Componet组件
  • 同业、金融市场、投资银行业务系统应用架构设计
  • MySQL表的内外连接
  • 为IntelliJ IDEA安装插件
  • Milvus实践(4) ---- attu2.4x及以下版本可视化工具搭建(不stop milvus服务)
  • java 中的设计模式
  • 官方强烈建议更新,关键漏洞影响GitHub Enterprise Server 所有版本
  • 阿里云服务器的基本使用
  • 基于GPT-SoVITS的API实现批量克隆声音
  • C学习(数据结构)-->实现链式结构二叉树
  • springboot 上传文件失败:The temporary upload location
  • 【JavaEE初阶】三次握手与四次挥手