如何在 JavaFX 的 TextArea 实现回车发送信息而不换行,但组合键 Ctrl + Enter 换行
如何在 JavaFX 的 TextArea 实现回车发送信息而不换行,但组合键 Ctrl + Enter 换行
JavaFX 的恼人之处在于很多基本的操作都要自己亲力亲为。在默认情况下,在 TextArea 输入回车会导致换行,但在很多场景中,我们希望它在用户输入回车不换行而改为触发信息的发送,换行则由组合键 Ctrl + Enter
来触发。在 JavaFX,这项功能没有简单直接的方法,并不是在所有的 UI 语言中都是如此,但 JavaFX 没有提供直接的 API。在不断踩坑之后,笔者终于在 JavaFX 中实现了这一功能。
实现的算法大致如下:
使用 TextArea 的处理器 onKeyPressed 来监听 TextArea 的键盘输入事件。
如果监听到用户输入了回车,作如下判断:
如果用户输入的不是组合键
Ctrl + Enter
,去掉刚刚输入的换行符,然后将文本发送。此时可以选择清空文本框的内容还是保持文本框的内容不变。然后本算法结束。如果用户输入的是组合键
Ctrl + Enter
,在光标处插入换行符,然后将光标移至到换行符之后。然后本算法结束。
如果用户没有输入回车,什么也不做,本算法结束。
主要的注意事项如下:
文本框光标的范围是 [0, length]。因为光标指向文字左右及之间的空隙,而空隙的数量比文字多 1。
处理器 onKeyPressed 的回调方法是在用户按下按键(还没释放之前)就马上触发。
TextArea 不认为输入的组合键为单独依次这些键的效果之和。这意味着,在按下但不松开 Ctrl 键之后,输入 Enter 时,输入的文本不会包含换行符,因为 TextArea 不认为此时输入的是 Enter。但如果依次按下(不松开)
a
、Enter
,则输入的文本为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
原文地址: https://blog.csdn.net/wangpaiblog/article/details/121506912
本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
相关文章