如何在 JavaFX 的 TextArea 实现回车发送信息而不换行,但组合键 Ctrl + Enter 换行

本文介绍了如何在JavaFX的TextArea组件中定制输入行为,实现在普通回车时不换行,仅在按Ctrl+Enter时触发换行。通过监听键盘事件,作者详细分享了实现算法和关键代码片段,适合JavaFX初学者和高级开发者参考。
摘要由CSDN通过智能技术生成

如何在 JavaFX 的 TextArea 实现回车发送信息而不换行,但组合键 Ctrl + Enter 换行

  JavaFX 的恼人之处在于很多基本的操作都要自己亲力亲为。在默认情况下,在 TextArea 输入回车会导致换行,但在很多场景中,我们希望它在用户输入回车不换行而改为触发信息的发送,换行则由组合键 Ctrl + Enter 来触发。在 JavaFX,这项功能没有简单直接的方法,并不是在所有的 UI 语言中都是如此,但 JavaFX 没有提供直接的 API。在不断踩坑之后,笔者终于在 JavaFX 中实现了这一功能。


实现的算法大致如下:

  1. 使用 TextArea 的处理器 onKeyPressed 来监听 TextArea 的键盘输入事件。

  2. 如果监听到用户输入了回车,作如下判断:

    1. 如果用户输入的不是组合键 Ctrl + Enter,去掉刚刚输入的换行符,然后将文本发送。此时可以选择清空文本框的内容还是保持文本框的内容不变。然后本算法结束。

    2. 如果用户输入的是组合键 Ctrl + Enter,在光标处插入换行符,然后将光标移至到换行符之后。然后本算法结束。

  3. 如果用户没有输入回车,什么也不做,本算法结束。


主要的注意事项如下:

  • 文本框光标的范围是 [0, length]。因为光标指向文字左右及之间的空隙,而空隙的数量比文字多 1。

  • 处理器 onKeyPressed 的回调方法是在用户按下按键(还没释放之前)就马上触发。

  • TextArea 不认为输入的组合键为单独依次这些键的效果之和。这意味着,在按下但不松开 Ctrl 键之后,输入 Enter 时,输入的文本不会包含换行符,因为 TextArea 不认为此时输入的是 Enter。但如果依次按下(不松开) aEnter,则输入的文本为 a\n

  • 在 onKeyPressed 的回调方法中,当用户输入的是普通的按键时,光标的位置为输入该字符之前的位置(刚刚输入的字符尚未在文本框中生效)。如果用户输入的是特殊的按键(如 Ctrl、Alt 、Enter 等),光标的位置为此键生效之后光标的位置。这意味着,如果输入的是 Enter,则当 onKeyPressed 的回调方法触发时,文本框中不仅包含换行符,而光标在该换行符之后。

  • 在拼接光标两侧的文本时,Enter 与组合键 Ctrl + Enter 的光标位置与文本内容均有差异。对于 Enter,需要清除换行符,而输入的换行符位于光标的左边。对于组合键 Ctrl + Enter,不仅要插入换行符,还要将光标的位置右移。

  • Windows 会将回车解释成 \n\r,但 TextArea 清除文本中所有的 \r。换句话说,当在 Windows 输入回车时,实际上输入的是 \n\r。但当向 TextArea 输入 \n\r 时,TextArea 会移除所有的 \r。从 TextArea 得到的字符串中不会包含任何 \r

  • 方法 keyEvent.isControlDown() 并不是用来判断触发 onKeyPressed 的按键(刚刚按下的按键)是不是键 Ctrl ,而是用来判断,在调用方法 keyEvent.isControlDown() 时,已经按下且未松开的按键含不含键 Ctrl。因此,如果使用该方法,就无需自行记录用户按下与释放的每一个键了。


核心代码如下:(FXML 与 FXML 的控制器的代码)

TextSend.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>

<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
      fx:controller="org.wangpai.demo.textsend.TextSendController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>
    <TextArea prefHeight="200" prefWidth="200" fx:id="textArea" onKeyPressed="#onKeyPressedTextArea"/>
    <Button onAction="#onActionButton" text="发送(S)"/>
</VBox>

TextSendController.java

package org.wangpai.demo.textsend;

import javafx.fxml.FXML;
import javafx.event.ActionEvent;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

public class TextSendController {
    @FXML
    private TextArea textArea;

    @FXML
    public void onKeyPressedTextArea(KeyEvent keyEvent) {
        // 如果按下了回车键
        if (keyEvent.getCode() == KeyCode.ENTER) {
            // 获得此时的光标位置。此位置为刚刚输入的换行符之后
            var caretPosition = this.textArea.getCaretPosition();

            // 如果已经按下的按键中包含 Control 键
            if (!keyEvent.isControlDown()) { // 如果输入的不是组合键 `Ctrl+Enter`,去掉换行符,然后将文本发送
                // 获得输入文本,此文本包含刚刚输入的换行符
                var text = this.textArea.getText();
                // 获得换行符两边的文本
                var front = text.substring(0, caretPosition - 1);
                var end = text.substring(caretPosition);
                this.textArea.setText(front + end);
                this.onActionButton(null); // 模拟发送

                /*----- 如果希望发送后保留输入框文本,需要只使用下面这行代码,然后去掉清除文本框的代码 -------*/
                // this.textArea.positionCaret(caretPosition - 1);
            } else {
                // 获得输入文本,此文本不包含刚刚输入的换行符
                var text = this.textArea.getText();
                // 获得光标两边的文本
                var front = text.substring(0, caretPosition);
                var end = text.substring(caretPosition);
                // 在光标处插入换行符
                this.textArea.setText(front + System.lineSeparator() + end);
                // 将光标移至换行符
                this.textArea.positionCaret(caretPosition + 1);
            }
        }
    }

    /**
     * 模拟的发送方法
     */
    @FXML
    public void onActionButton(ActionEvent event) {
        System.out.println("正在发送信息...");
        System.out.println(this.textArea.getText());

        this.textArea.requestFocus();
        /*----- 如果希望发送后清除输入框文本,使用下面这行代码 -------*/
        this.textArea.clear();
    }
}

示例程序运行截图如下:

笔者的运行环境:

  • JDK 17.0.1

  • JavaFX 17.0.1

  • IntelliJ IDEA 2021.2.2 (Ultimate Edition)


在这里插入图片描述


在这里插入图片描述


完整的代码:https://github.com/wangpaiblog/20211124-textsend


  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值