While going through Java swing I faced this problem. I have a JTextField which has predefined and not editable text. the user should be able to append other text to it but without editing the predefined text. Is there any method to obtain this solution or any other?
-
Why not put a label with the uneditable text before the text field as seen in [this answer](http://stackoverflow.com/a/16271173/418556)? – Andrew Thompson Apr 30 '13 at 08:03
-
tell me if only want to use JTextfield? – Tech Nerd Apr 30 '13 at 08:07
-
yes, I only have to use JTextField – sanjeeda Apr 30 '13 at 08:11
-
You could take a look at the BuddySupprt API in the latest SwingX release – MadProgrammer Apr 30 '13 at 10:23
-
Is the predefined text part of the string? That is when you invoke `textField.getText()` should you get just the text entered by the user, or should you get the predefined text as well? – camickr Apr 30 '13 at 15:58
5 Answers
You can simply use a 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();
}
});
}
}

- 47,259
- 4
- 83
- 117
-
2It's amazing that, when it comes to Java, using the word `simply` is actually fine, even though the code is maybe 100 lines long... – Radu Murzea Apr 30 '13 at 08:16
-
Actually, I'm editing an already existing code. So, inserting a code this long might be a little risky. Is there any simple solution like using a method or something else? – sanjeeda Apr 30 '13 at 08:23
-
2@sanjeeda You don't have to take the whole code, only the part of the `DocumentFilter`. The rest of the code is only there to show a fully-working example in order to avoid comments such as "I tried but it did not work". – Guillaume Polet Apr 30 '13 at 08:27
I have a JTextField which has predefined and not editable text. the user should be able to append other text to it but without editing the predefined text. Is there any method to obtain this solution or any other?
use
originally made by @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);
}
}
I'd suggest to use OverlayLayout (JLabel over JTextField), by changing Insets (input area) in JTextField for JLabels area, otherwise any formatting in JTextField make code and suggestion in this thread quite useless and with strange output to the Swing GUI
e.g.
JTextField.setHorizontalAlignment(JTextField.RIGHT);
EDIT
put JLabel & JTextField to JPanel, quite simple and without side effects
change built in FlowLayout for JPanel
required to call revalidate() and repaint() in the case that text in JLabel is changed
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();
}
});
}
}

- 109,525
- 20
- 134
- 319
-
1I cannot use JComboBox because its strictly needed that I have to use a JTextField with some text "$" in it and I want the user to enter the numbers without editing/removing the dollar symbol. – sanjeeda Apr 30 '13 at 08:06
Take a look at DocumentFilter
. It should allow you to define a "protected" area of text.

- 343,457
- 22
- 230
- 366
Though I believe DocumentFilter is the logical and versatile solution, a short solution here. It simply makes an JTextField with an inner left margin in which the fixed text is written.
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());
}
}

- 107,315
- 7
- 83
- 138
-
Calling `setMargin(new Insets(3, 3 + w, 3, 3));` within `paintComponent` is really dangerous. You should call that outside the painting mechanism. – Guillaume Polet Apr 30 '13 at 09:47
Alternative solution:
1. Put the $ symbole in the JTextField
2. Remove the dollar symbole when the JTextField get the focus
3. Let the user modify the full text
4. When he's done typing (see here) add the $ symbole back
But it would be way easier to add a label next to the JTextField

- 1
- 1

- 1,503
- 2
- 12
- 26