JavaFX中如何在TextArea搜索格式化文本及修复按钮逻辑问题
问题分析与解决方案
核心问题点
- 对话框按钮事件绑定时机错误:原代码在
showAndWait()后绑定事件,而showAndWait()会阻塞到对话框关闭,此时按钮已销毁,事件永远不会触发。 - Optional
误用 :showAndWait()返回的Optional是用户点击的ButtonType,不是输入的文本,输入内容需从对话框的编辑器组件获取。 - 文本匹配逻辑错误:匹配源用了用户输入的文本而非
TextArea内容,且未处理正则特殊字符,也没有记录当前匹配位置,无法连续遍历结果。 - 样式设置方式错误:原生
TextArea是纯文本组件,无法直接插入带样式的Text节点,需通过选区高亮或使用富文本组件实现。
修改后的完整代码
首先添加必要的成员变量:
import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.MatchResult; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; import javafx.scene.control.TextInputDialog; // 类成员变量,用于记录匹配状态 private int currentMatchIndex = -1; private List<MatchResult> matches = new ArrayList<>(); private TextInputDialog findDialog;
修改onLocateText()方法:
@FXML protected void onLocateText(){ findDialog = new TextInputDialog(); findDialog.setTitle("Find"); findDialog.setHeaderText("Enter the search phrase"); // 添加按钮类型 ButtonType nextBtnType = new ButtonType("Next", ButtonBar.ButtonData.OK_DONE); ButtonType prevBtnType = new ButtonType("Previous", ButtonBar.ButtonData.CANCEL_CLOSE); ButtonType closeBtnType = new ButtonType("Close", ButtonBar.ButtonData.CLOSE); findDialog.getDialogPane().getButtonTypes().setAll(nextBtnType, prevBtnType, closeBtnType); // 获取按钮并提前绑定事件 Button nextBtn = (Button) findDialog.getDialogPane().lookupButton(nextBtnType); Button prevBtn = (Button) findDialog.getDialogPane().lookupButton(prevBtnType); nextBtn.setOnAction(e -> handleNextClick()); prevBtn.setOnAction(e -> handlePreviousClick()); // 对话框关闭时重置状态 findDialog.setOnHidden(e -> { currentMatchIndex = -1; matches.clear(); findDialog = null; }); // 用show()而非showAndWait(),保持对话框打开以便多次操作 findDialog.show(); }
实现handleNextClick()方法:
private void handleNextClick() { if (findDialog == null) return; String searchText = findDialog.getEditor().getText().trim(); if (searchText.isEmpty()) { textArea.selectRange(0, 0); currentMatchIndex = -1; matches.clear(); return; } // 搜索词变更时重新获取所有匹配 if (currentMatchIndex == -1 || !matches.isEmpty() && !matches.get(0).group().equals(searchText)) { Pattern pattern = Pattern.compile(Pattern.quote(searchText)); // 避免正则特殊字符干扰 Matcher matcher = pattern.matcher(textArea.getText()); matches.clear(); while (matcher.find()) { matches.add(matcher.toMatchResult()); } currentMatchIndex = -1; } // 遍历下一个匹配项 if (!matches.isEmpty()) { currentMatchIndex = (currentMatchIndex + 1) % matches.size(); MatchResult match = matches.get(currentMatchIndex); // 选中匹配文本并滚动到视图 textArea.selectRange(match.start(), match.end()); textArea.requestFocus(); textArea.scrollTo(textArea.getParagraphIndex(match.start())); } }
实现handlePreviousClick()方法:
private void handlePreviousClick() { if (findDialog == null) return; String searchText = findDialog.getEditor().getText().trim(); if (searchText.isEmpty()) { textArea.selectRange(0, 0); currentMatchIndex = -1; matches.clear(); return; } if (currentMatchIndex == -1 || !matches.isEmpty() && !matches.get(0).group().equals(searchText)) { Pattern pattern = Pattern.compile(Pattern.quote(searchText)); Matcher matcher = pattern.matcher(textArea.getText()); matches.clear(); while (matcher.find()) { matches.add(matcher.toMatchResult()); } currentMatchIndex = matches.size(); } // 遍历上一个匹配项 if (!matches.isEmpty()) { currentMatchIndex = (currentMatchIndex - 1 + matches.size()) % matches.size(); MatchResult match = matches.get(currentMatchIndex); textArea.selectRange(match.start(), match.end()); textArea.requestFocus(); textArea.scrollTo(textArea.getParagraphIndex(match.start())); } }
额外说明(永久高亮需求)
如果需要对匹配文本设置永久样式,原生TextArea无法实现,需使用第三方富文本组件(如RichTextFX)。示例替换逻辑如下:
// 假设使用RichTextArea代替TextArea import org.fxmisc.richtext.RichTextArea; private RichTextArea richTextArea; // 在handleNextClick中替换为: richTextArea.setStyle(match.start(), match.end(), "-fx-fill: red; -fx-font-weight: bold;");
内容的提问来源于stack exchange,提问作者user16612111
相关产品推荐
相关产品推荐

