JTextPane含双线字符时嵌入JTextArea焦点异常,无法持续输入求助
解决JTextArea嵌入JTextPane时焦点跳转问题(仅含特殊字符时触发)
这个问题我之前处理带特殊数学字符的富文本组件时也碰到过,挺棘手的。根源在于Cambria Math字体里的高码位双线字符(比如你用的\uD835\uDD38)会触发JTextPane的视图重绘逻辑,意外夺走嵌入JTextArea的焦点——普通字符的布局逻辑不会触发这个bug,所以才会出现“只有特殊字符时才出问题”的现象。
解决方案1:给JTextArea添加焦点监听器,强制保持焦点
最简单的补救办法是监听JTextArea的焦点丢失事件,一旦失去焦点就立即重新请求:
import javax.swing.*; import javax.swing.event.FocusAdapter; import javax.swing.event.FocusEvent; import java.awt.*; public class TestFocusFix { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame(); JTextPane jTextPane = new JTextPane(); jTextPane.setFont(new Font("Cambria Math", 0, 12)); JTextArea area = new JTextArea(); area.setBorder(BorderFactory.createLineBorder(Color.black)); // 添加焦点监听器,防止焦点跳转到外层JTextPane area.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { // 用invokeLater延迟执行,避免和JTextPane的布局事件冲突 SwingUtilities.invokeLater(() -> area.requestFocusInWindow()); } }); try { jTextPane.getDocument().insertString(jTextPane.getDocument().getLength(), "a", null); jTextPane.getDocument().insertString(jTextPane.getDocument().getLength(), "\uD835\uDD38", null); jTextPane.getDocument().insertString(jTextPane.getDocument().getLength(), "b", null); jTextPane.insertComponent(area); } catch (Exception ex) { ex.printStackTrace(); } frame.add(jTextPane); frame.setSize(100, 100); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } }
解决方案2:重写JTextPane的ComponentView,修正焦点遍历逻辑
如果焦点监听器的“强制抢回”方式不够优雅,你可以通过自定义EditorKit来修改嵌入组件的视图逻辑,从根源上确保JTextArea能正确持有焦点:
import javax.swing.*; import javax.swing.text.*; import java.awt.*; public class TestFocusFix { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame(); // 自定义EditorKit,重写ComponentView的焦点判断逻辑 JTextPane jTextPane = new JTextPane(); jTextPane.setEditorKit(new StyledEditorKit() { @Override public ViewFactory getViewFactory() { return elem -> { View view = super.getViewFactory().create(elem); if (view instanceof ComponentView) { return new ComponentView(elem) { @Override public boolean isFocusTraversable() { Component comp = getComponent(); // 让嵌入组件自己决定是否可获取焦点,而非交给外层JTextPane return comp != null && comp.isFocusTraversable(); } }; } return view; }; } }); jTextPane.setFont(new Font("Cambria Math", 0, 12)); JTextArea area = new JTextArea(); area.setBorder(BorderFactory.createLineBorder(Color.black)); try { jTextPane.getDocument().insertString(jTextPane.getDocument().getLength(), "a", null); jTextPane.getDocument().insertString(jTextPane.getDocument().getLength(), "\uD835\uDD38", null); jTextPane.getDocument().insertString(jTextPane.getDocument().getLength(), "b", null); jTextPane.insertComponent(area); } catch (Exception ex) { ex.printStackTrace(); } frame.add(jTextPane); frame.setSize(100, 100); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } }
原理说明
JTextPane在渲染高码位特殊字符时,会触发视图的重新布局(比如字符基线调整、宽度计算),默认的ComponentView会错误地将焦点交还给JTextPane本身。方案1是通过“补救”的方式抢回焦点,方案2则是从根源上修正视图的焦点遍历规则,让嵌入组件能正常持有焦点。
内容的提问来源于stack exchange,提问作者Sanya
相关产品推荐
相关产品推荐

