在通过 Java swing 时,我遇到了这个问题。我有一个 JTextField,它具有预定义且不可编辑的文本。用户应该能够向其附加其他文本,但无需编辑预定义的文本。有什么方法可以获得这个解决方案或其他任何方法吗?
5 回答
您可以简单地使用DocumentFilter
:
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
public class TestDocumentFilter {
private static final String TEXT_NOT_TO_TOUCH = "You can't touch this!";
private void initUI() {
JFrame frame = new JFrame(TestDocumentFilter.class.getSimpleName());
frame.setLayout(new FlowLayout());
final JTextField textfield = new JTextField(50);
textfield.setText(TEXT_NOT_TO_TOUCH);
((AbstractDocument) textfield.getDocument()).setDocumentFilter(new DocumentFilter() {
@Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
if (offset < TEXT_NOT_TO_TOUCH.length()) {
return;
}
super.insertString(fb, offset, string, attr);
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
if (offset < TEXT_NOT_TO_TOUCH.length()) {
length = Math.max(0, length - TEXT_NOT_TO_TOUCH.length());
offset = TEXT_NOT_TO_TOUCH.length();
}
super.replace(fb, offset, length, text, attrs);
}
@Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
if (offset < TEXT_NOT_TO_TOUCH.length()) {
length = Math.max(0, length + offset - TEXT_NOT_TO_TOUCH.length());
offset = TEXT_NOT_TO_TOUCH.length();
}
if (length > 0) {
super.remove(fb, offset, length);
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textfield);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestDocumentFilter().initUI();
}
});
}
}
看看DocumentFilter
。它应该允许您定义文本的“受保护”区域。
我有一个 JTextField,它具有预定义且不可编辑的文本。用户应该能够向其附加其他文本,但无需编辑预定义的文本。有什么方法可以获得这个解决方案或其他任何方法吗?
利用
最初由@camickr 制作
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class NavigationFilterPrefixWithBackspace extends NavigationFilter {
private int prefixLength;
private Action deletePrevious;
public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component) {
this.prefixLength = prefixLength;
deletePrevious = component.getActionMap().get("delete-previous");
component.getActionMap().put("delete-previous", new BackspaceAction());
component.setCaretPosition(prefixLength);
}
@Override
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
fb.setDot(Math.max(dot, prefixLength), bias);
}
@Override
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
fb.moveDot(Math.max(dot, prefixLength), bias);
}
class BackspaceAction extends AbstractAction {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
JTextComponent component = (JTextComponent) e.getSource();
if (component.getCaretPosition() > prefixLength) {
deletePrevious.actionPerformed(null);
}
}
}
public static void main(String args[]) throws Exception {
JTextField textField = new JTextField(" $ ", 20);
textField.setNavigationFilter(new NavigationFilterPrefixWithBackspace(textField.getDocument().getLength(), textField));
JFrame frame = new JFrame("Navigation Filter Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(textField);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
我建议使用 OverlayLayout(JLabel over JTextField),通过将 JTextField 中的 Insets(输入区域)更改为 JLabels 区域,否则 JTextField 中的任何格式都会使该线程中的代码和建议变得非常无用,并且对 Swing GUI 有奇怪的输出
例如
JTextField.setHorizontalAlignment(JTextField.RIGHT);
编辑
把 JLabel & JTextField 放到 JPanel 上,很简单,没有副作用
更改为 JPanel 内置的 FlowLayout
如果 JLabel 中的文本发生更改,则需要调用 revalidate() 和 repaint()
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class NavigationFilterBias {
private JFrame frame = new JFrame("Navigation Filter Example");
private JPanel panel = new JPanel();
private JLabel label = new JLabel(" $ ");
private JTextField textField = new JTextField();
public NavigationFilterBias() {
panel.setBorder(textField.getBorder());
panel.setBackground(textField.getBackground());
panel.setLayout(new BorderLayout());
panel.add(label,BorderLayout.WEST);
textField.setBorder(null);
panel.add(textField,BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String args[]) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
NavigationFilterBias exam = new NavigationFilterBias();
}
});
}
}
虽然我相信 DocumentFilter 是合乎逻辑且通用的解决方案,但这里是一个简短的解决方案。它只是创建了一个带有内部左边距的 JTextField,其中写入了固定的文本。
public class PrefixTextField extends JTextField {
private String prefix;
private JLabel label;
public PrefixTextField(String prefix) {
this.prefix = prefix;
label = new JLabel(prefix + '\u00a0');
}
@Override
protected void paintComponent(Graphics g) {
int w = SwingUtilities.computeStringWidth(
getFontMetrics(getFont()), prefix);
setMargin(new Insets(3, 3 + w, 3, 3));
super.paintComponent(g);
SwingUtilities.paintComponent(g, label, this.getParent(),
2 + 3, 0, getWidth(), getHeight());
}
}
替代解决方案:
1. 将 $ 符号放入 JTextField
2. 当 JTextField 获得焦点时删除美元符号
3. 让用户修改全文
4. 当他完成输入后(参见此处)添加 $ 符号
但是在 JTextField 旁边添加标签会更容易