I want to create a virtual numeric keyboard, so that when I press U I get a 4, I produces a 5, O produces a 6, and so on:
789 789
uio becomes 456
jkl 123
m 0
But I need the rest of the keyboard to continue working as usual. I have tried this and some other solutions, but they are not useful to me, because on my JTextField I get 4U5I6O (or U4I5O6 depending on which solution I implement).
I need to get rid of the letter, and produce only the number.
Does anybody know a proper solution?
Thanks.
If you are typing directly to JTextField, then I recommend to use DocumentFilter.
As an example of DocumentFilter, see:
Only allowing numbers and a symbol (-) to be typed into a JTextField
This is an eample of #Eng.Fouad's suggestion (please, all credit to him).
Basically, this will replace all the text entered into the text field with a random character instead.
I wouldn't be difficult to update the code to deliver mapped changes (for example a = 1) or even an encryption process.
public class TestPhasicDocumentFilter {
public static void main(String[] args) {
new TestPhasicDocumentFilter();
}
public TestPhasicDocumentFilter() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PhasicPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PhasicPane extends JPanel {
public PhasicPane() {
setLayout(new GridBagLayout());
JTextField field = new JTextField(12);
add(field);
((AbstractDocument)field.getDocument()).setDocumentFilter(new PhasicDocumentFilter());
}
public class PhasicDocumentFilter extends DocumentFilter {
protected String phasic(String text) {
StringBuilder sb = new StringBuilder(text);
for (int index = 0; index < sb.length(); index++) {
sb.setCharAt(index, (char)(33 + (int)Math.round(Math.random() * 93)));
}
return sb.toString();
}
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
super.insertString(fb, offset, phasic(text), attr);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
super.replace(fb, offset, length, phasic(text), attrs);
}
}
}
}
Related
I have a requirement where I am entering input in JTextarea at run time and input should be masked. I am able to achieve this through below code snippet
if(encryptKeystroke == true) {
jTextArea.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getExtendedKeyCode() == KeyEvent.VK_BACK_SPACE) {
if(text.length() > 0)
text = text.substring(0, text.length() - 1);
}
else {
text += String.valueOf(e.getKeyChar());
}
jTextArea.setText(text.replaceAll(".", "*"));
}
public void keyReleased(KeyEvent e) {
jTextArea.setText(text.replaceAll(".", "*"));
}
});
}
Issue is when I am running this, entered character is visible for a small moment and then getting masked(like it happened in Android).
I am using JTextarea because unable to achieve the scrollable & wrapstyle in Ttextfield.
Any suggestion how this can be achieved ?
Don't use a KeyListener for something like this. What if the user:
pastes text into the text area. The code won't handle multiple characters
moves the caret to the beginning of the text area. The code assumes text is always added at the end.
uses the Delete key
highlights a block of text and then enters a character
A KeyListener can't handle all these special situations. Swing has better and newer API's to use.
Instead you can use a DocumentFilter. The DocumentFilter allows you to filter the text BEFORE it is added to the Document of the JTextArea.
Basic example:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class AsteriskFilter extends DocumentFilter
{
private StringBuilder realText = new StringBuilder();
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attributes)
throws BadLocationException
{
replace(fb, offset, 0, text, attributes);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attributes)
throws BadLocationException
{
// Update the StringBuilder to contain the real text
Document doc = fb.getDocument();
realText.replace(offset, offset + length, text);
// Update the Document with asterisks
text = text.replaceAll(".", "*");
super.replace(fb, offset, length, text, attributes);
}
#Override
public void remove(DocumentFilter.FilterBypass fb, int offset, int length)
throws BadLocationException
{
realText.delete(offset, offset + length);
super.remove(fb, offset, length);
}
public String getRealText()
{
return realText.toString();
}
private static void createAndShowGUI()
{
JTextArea textArea = new JTextArea(3, 20);
AbstractDocument doc = (AbstractDocument) textArea.getDocument();
AsteriskFilter filter = new AsteriskFilter();
doc.setDocumentFilter( filter );
JButton button = new JButton("Display Text");
button.addActionListener(e -> JOptionPane.showMessageDialog(textArea, filter.getRealText()));
JFrame frame = new JFrame("Asterisk Filter");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textArea, BorderLayout.CENTER);
frame.add(button, BorderLayout.PAGE_END);
frame.setSize(220, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
I'm using java swing and trying to enter Numbers only into a JTextField.
when typing a char I want to show an Invalid message, and prevent from typing the char into the JTextField.
idText.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
try
{
int i = Integer.parseInt(idText.getText()+e.getKeyChar());
validText.setText("");
}
catch(NumberFormatException e1) {
e.consume();
validText.setText("Numbers Only!");
}
}
});
for some reason, e.consume() doesnt work as I expected, and i can type chars.
Generally, adding a custom KeyListener to prevent characters in a JTextField is not recommended. It would be better for users (and easier for you as programmer) to use a component that it has been created for only-numbers input.
JSpinner is one of them.
JFormattedTextField is another one.
If you insist of doing it your self though, it would be better to achieve it with a DocumentFilter and not with a KeyListener. Here is another example as well.
In addition to these examples, here is my solution:
public class DocumentFilterExample extends JFrame {
public DocumentFilterExample() {
super("example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
JTextField field = new JTextField(15);
AbstractDocument document = (AbstractDocument) field.getDocument();
document.setDocumentFilter(new OnlyDigitsDocumentFilter());
add(field);
setLocationByPlatform(true);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new DocumentFilterExample().setVisible(true));
}
private static class OnlyDigitsDocumentFilter extends DocumentFilter {
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
text = keepOnlyDigits(text);
super.replace(fb, offset, length, text, attrs);
}
#Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
string = keepOnlyDigits(string);
super.insertString(fb, offset, string, attr);
}
private String keepOnlyDigits(String text) {
StringBuilder sb = new StringBuilder();
text.chars().filter(Character::isDigit).forEach(sb::append);
return sb.toString();
}
}
}
I wanted to make a JTextArea where the user can't erase the previous line. Just like the Command Prompt in Windows and the terminal in Linux, you can't edit previous lines.
This is what I've come up with, but it doesn't seem to work, and I can only come up with one reason for this, but it appears to be more than just one reason.
if(commandArea.getCaretPosition() < commandArea.getText().lastIndexOf("\n")){
commandArea.setCaretPosition(commandArea.getText().lastIndexOf("\n"));
}
This block of code lives inside this method:
private void commandAreaKeyPressed(java.awt.event.KeyEvent evt)
You can use DocumentFilter for JTextArea's Document. Runnable working example that allows editing last line only in JTextArea:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.DocumentFilter.FilterBypass;
public class MainClass {
public static void main(String[] args) {
JFrame frame = new JFrame("text area test");
JPanel panelContent = new JPanel(new BorderLayout());
frame.setContentPane(panelContent);
UIManager.getDefaults().put("TextArea.font", UIManager.getFont("TextField.font")); //let text area respect DPI
panelContent.add(createSpecialTextArea(), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null); //center screen
frame.setVisible(true);
}
private static JTextArea createSpecialTextArea() {
final JTextArea textArea = new JTextArea("first line\nsecond line\nthird line");
((AbstractDocument)textArea.getDocument()).setDocumentFilter(new DocumentFilter() {
private boolean allowChange(int offset) {
try {
int offsetLastLine = textArea.getLineCount() == 0 ? 0 : textArea.getLineStartOffset(textArea.getLineCount() - 1);
return offset >= offsetLastLine;
} catch (BadLocationException ex) {
throw new RuntimeException(ex); //should never happen anyway
}
}
#Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
if (allowChange(offset)) {
super.remove(fb, offset, length);
}
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
if (allowChange(offset)) {
super.replace(fb, offset, length, text, attrs);
}
}
#Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
if (allowChange(offset)) {
super.insertString(fb, offset, string, attr);
}
}
});
return textArea;
}
}
How does it work? JTextArea is a text control, actual data has Document. Document allows listening for changes (DocumentListener), and some documents allow to set DocumentFilter to forbid changes. Both PlainDocument and DefaultStyledDocument extend from AbstractDocument, which allows setting a filter.
Be sure to read java doc:
JTextArea
AbstractDocument
DocumentFilter
I recommend also tutorials:
How to use text area
How to Write a Document Listener
Implementing a Document Filter
Well, this might sound as if it is a duplicate question but it is not. I have asked this question in connection with this question here. I have rewritten my DocumentFilter to use regular expressions. In validating a person's name I want only the following characters [a-zA-Z],',\S and ..
I have written my regular expression with the hope that it will solve this. It is working the way I want it but the fact that it is not allowing numbers when I have not yet set it to do so is puzzling me.
Question: Why is regex not allowing numbers?
This is the regular expression [\\_\\(\\)#!\"#%&*+,-:;<>=?\\[\\]\\^\\~\\{\\}\\|] and what it should not allow to be entered is commented in the code below:
My DocumentFilter is as follows:
public class NameValidator extends DocumentFilter{
#Override
public void insertString(FilterBypass fb, int off
, String str, AttributeSet attr)
throws BadLocationException
{
// remove 0-9 !"#$%&()*+,-/:;<=>?#[\]^_`{|}~
fb.insertString(off, str.replaceAll("^[\\_\\(\\)#!\"#%&*+,-:;<>=?\\[\\]\\^\\~\\{\\}\\|]", ""), attr);
}
#Override
public void replace(FilterBypass fb, int off
, int len, String str, AttributeSet attr)
throws BadLocationException
{
// remove 0-9 !"#$%&()*+,-/:;<=>?#[\]^_`{|}~
fb.replace(off, len, str.replaceAll("^[\\_\\(\\)#!\"#%&*+,-:;<>=?\\[\\]\\^\\~\\{\\}\\|]", ""), attr);
}
}
Here is my test class:
public class NameTest {
private JFrame frame;
public NameTest() throws ParseException {
frame = new JFrame();
initGui();
}
private void initGui() throws ParseException {
frame.setSize(100, 100);
frame.setVisible(true);
frame.setLayout(new GridLayout(2, 1, 5, 5));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextField name = new JTextField(10);
((AbstractDocument)name.getDocument()).setDocumentFilter(new NameValidator());
frame.add(name);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
NameTest nt = new NameTest();
} catch (ParseException e) {
e.printStackTrace();
}
}
});
}
}
The reason is this part of your regex:
,-:
Which matches any character in the range of , (ASCII 44) to : (ASCII 58), which includes all the numbers (ASCII 48-57 inclusive).
If you escape the - it should work fine and not match numbers:
[\\_\\(\\)#!\"#%&*+,\\-:;<>=?\\[\\]\\^\\~\\{\\}\\|]
I use a DocumentListener to handle any change in a JTextPane document. while the user types i want to delete the contents of JTextPane and insert a customized text instead. it is not possible to change the document in the DocumentListener,instead a solution is said here:
java.lang.IllegalStateException while using Document Listener in TextArea, Java
,but i don't understand that, at least i don't know what to do in my case?
DocumentListener is really only good for notification of changes and should never be used to modify a text field/document.
Instead, use a DocumentFilter
Check here for examples
FYI
The root course of your problem is that the DocumentListener is notified WHILE the document is been updated. Attempts to modify the document (apart from risking a infinite loop) put the document into a invalid state, hence the exception
Updated with an example
This is VERY basic example...It doesn't handle insert or remove, but my testing had remove working without doing anything anyway...
public class TestHighlight {
public static void main(String[] args) {
new TestHighlight();
}
public TestHighlight() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTextPane textPane = new JTextPane(new DefaultStyledDocument());
((AbstractDocument) textPane.getDocument()).setDocumentFilter(new HighlightDocumentFilter(textPane));
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(textPane));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class HighlightDocumentFilter extends DocumentFilter {
private DefaultHighlightPainter highlightPainter = new DefaultHighlightPainter(Color.YELLOW);
private JTextPane textPane;
private SimpleAttributeSet background;
public HighlightDocumentFilter(JTextPane textPane) {
this.textPane = textPane;
background = new SimpleAttributeSet();
StyleConstants.setBackground(background, Color.RED);
}
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
System.out.println("insert");
super.insertString(fb, offset, text, attr);
}
#Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
System.out.println("remove");
super.remove(fb, offset, length);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
String match = "test";
super.replace(fb, offset, length, text, attrs);
int startIndex = offset - match.length();
if (startIndex >= 0) {
String last = fb.getDocument().getText(startIndex, match.length()).trim();
System.out.println(last);
if (last.equalsIgnoreCase(match)) {
textPane.getHighlighter().addHighlight(startIndex, startIndex + match.length(), highlightPainter);
}
}
}
}
}
while the user types i want to delete the contents of JTextPane and
insert a customized text instead.
this isn't job for DocumentListener, basically this Listener is designed to firing events out from JTextComponents to the another JComponent, to Swing GUI, implemented methods in used Java
have look at DocumentFilter, this provide desired methods to change, modify or update own Document (model for JTextComponents) on runtime
Wrap the code you call in SwingUtilities.invokeLater()