【发布时间】:2021-07-24 16:32:59
【问题描述】:
好的,我遇到了一个非常具体的问题。我有一个带有 JTextField 和 JTextPane 的面板(实际上是 2 个 JTextPane,但我只有一个处于活动状态),并且在用户完成文本编辑后需要一个事件来更新调用者表单中的相关变量。
JTextPane 设置有 StyledDocument,因此,我无法在每次按键时都更新其变量,因此使用 DocumentListener 是不可行的。
使用 FocusListener 也不是一个解决方案,因为变量(对)是使用 JList 组件选择的,并且它在 FocusLost 更改之前更改了引用。简而言之,其他组件正在干扰变量实例。
这就是我解决问题的方法。
首先我创建了一个InputVerifier:
InputVerifier myInputVerifier = new InputVerifier() {
@Override
public boolean verify(JComponent input) {
if ((input instanceof JTextField source) && source == tfKeywords) {
PropertyChangeEvent evt = new PropertyChangeEvent(source, "Keywords", source.getText(), null);
for (PropertyChangeListener l: pnlEditorFull.this.getPropertyChangeListeners())
l.propertyChange(evt);
} else if (input instanceof JTextPane source && source == ActualEditor) {
PropertyChangeEvent evt = new PropertyChangeEvent(source, "Description", source.getText(), null);
for (PropertyChangeListener l: pnlEditorFull.this.getPropertyChangeListeners())
l.propertyChange(evt);
}
return true;
}
};
当询问验证者时,它会触发一个事件 propertyChange,作为 JTextField 的“关键字”或 JTextArea 的“描述”。
另一方面,我有一个PropertyListener
pnlEditor.addPropertyChangeListener((PropertyChangeEvent evt) - > {
if ("Keywords".equals(evt.getPropertyName()))
myKeywords = EditorPane.getKeywords(); // actually a little more complicated
else if ("Description".equals(evt.getPropertyName()))
myDescription = EditorPane.getText(); // actually a little more complicated
});
为了检索文本,我在面板上使用了一个名为 getText() 的函数:
public String getText() {
StringWriter sw = new StringWriter();
try {
ActualEditor.write(sw);
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
return sw.toString();
}
需要使用ActualEditor.write(sw);,因为我需要检索文本和格式。
成功了。它在 FocusLost 之前和 JList 更改其选定项之前触发。当然,我可以在选择更改时使用 JList ListSelectionListener 来设置更改(我最初是这样做的),但这会留下一个不确定窗口,如果其他组件干扰,我很可能会丢失所有更改或更改错误的变量。它需要在更改完成时调用(而不是在更改发生时),这是我唯一能想到的解决方案。
这里是它应该如何工作的简化示例(它还包括 JList):
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import javax.swing.DefaultListModel;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JTextArea;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
*
* @author luca.scarcia
*/
public class frmMain extends javax.swing.JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
static final Logger LOGGER = System.getLogger(frmMain.class.getName());
private JTextArea anEditor;
private JList<String> aList;
private DefaultListModel<String> Model;
public frmMain() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new java.awt.GridLayout());
aList = new JList<>();
this.add(aList);
Model = new DefaultListModel<>();
Model.addElement("Element 1");
Model.addElement("Element 2");
Model.addElement("Element 3");
Model.addElement("Element 4");
aList.setModel(Model);
anEditor = new JTextArea();
this.add(anEditor);
anEditor.setInputVerifier(new InputVerifier() {
@Override
public boolean verify(JComponent input) {
if(input == anEditor) {
PropertyChangeEvent evt = new PropertyChangeEvent(anEditor, "Description", anEditor.getText(), null);
for(PropertyChangeListener l:anEditor.getPropertyChangeListeners()) {
l.propertyChange(evt);
}
}
return true;
}
});
anEditor.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(aList.getSelectedIndex()>=0)
if("Description".equals(evt.getPropertyName()) && evt.getSource()==anEditor) {
StringWriter sw = new StringWriter();
try {
anEditor.write(sw);
} catch (IOException ex) {
LOGGER.log(Level.ERROR, ex);
}
Model.setElementAt(sw.toString(), aList.getSelectedIndex());
aList.validate();
}
}
});
aList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if(e.getSource() == aList) {
StringReader sr = new StringReader((String)aList.getSelectedValue());
try {
anEditor.read(sr, null);
} catch (IOException ex) {
LOGGER.log(Level.ERROR, ex);
}
}
}
});
pack();
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
LOGGER.log(Level.ERROR, ex);
}
/* Create and display the form */
java.awt.EventQueue.invokeLater(() -> {
new frmMain().setVisible(true);
});
}
}
问题:有人有更好的解决方案吗?
【问题讨论】:
-
要求很简单:我需要在一个带有 JTextPane 的面板中放置一个文本。我需要在用户完成编辑时而不是在编辑时检索文本。而且 FocusLost 事件太慢了,因为当它触发时,另一个组件改变了我正在处理的领域,从而产生了严重的数据完整性问题。因此,我需要一种更可靠的方法来获取该 JTextPane 的内容,而不会因为不断调用 JTextPane.write(out) 而滞后接口(因为我正在使用 EditorKits);稍后我将添加一个可重现的最小示例。
标签: java swing jtextfield jtextarea textchanged