I'm developing a chat application and when I press an enter button when focusing the JTextArea I want it to stop adding the unnecessary new line of text, so for example i will be able to determine when user has pressed the enter button and not typed anything inside the JTextArea. I am using a KeyListener for the means of detecting when an user has released the enter key and then sending the message. I firstly tried replacing the new line of text with an empty string message.replaceAll("[\n]", "") and also trimming the message, however it didn't work. Is there anything i'm doing wrong with my approach or would there be any other solution i could adapt?
Don't use a JTextArea for this, but instead use a JTextField.
You can then easily listen for the enter press by giving the JTextField an ActionListener.
Most Swing chat applications I've seen use two text components for this: a JTextArea to display incoming text and your sent text, and a JTextField to allow user input of the text to send.
Usually one is right on top of the other using a BorderLayout.
If you absolutely must use a JTextArea, then you will probably want to use Key Binding to capture the enter key and deal with it. Check out the How to use Key Bindings Tutorial.
For example:
Example Key Bindings Solution:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class CaptureTextAreaEnter extends JPanel {
private static final int COLS = 30;
private static final int VIEW_ROWS = 12;
private static final int ENTER_ROWS = 4;
private JTextArea chatViewArea = new JTextArea(VIEW_ROWS, COLS);
private JTextArea chatEnterArea = new JTextArea(ENTER_ROWS, COLS);
public CaptureTextAreaEnter() {
setLayout(new BorderLayout());
add(new JScrollPane(chatViewArea), BorderLayout.CENTER);
add(new JScrollPane(chatEnterArea), BorderLayout.SOUTH);
chatViewArea.setFocusable(false);
chatViewArea.setWrapStyleWord(true);
chatEnterArea.setWrapStyleWord(true);
chatViewArea.setLineWrap(true);
chatEnterArea.setLineWrap(true);
// start our set up of key bindings
// to get the correct InputMap
int condition = WHEN_FOCUSED;
// get our maps for binding from the chatEnterArea JTextArea
InputMap inputMap = chatEnterArea.getInputMap(condition);
ActionMap actionMap = chatEnterArea.getActionMap();
// the key stroke we want to capture
KeyStroke enterStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
// tell input map that we are handling the enter key
inputMap.put(enterStroke, enterStroke.toString());
// tell action map just how we want to handle the enter key
actionMap.put(enterStroke.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
String text = chatEnterArea.getText();
chatEnterArea.setText("");
chatViewArea.append(text + "\n");
// *** you will want to send text to your
// *** PrintStream to the chat server here
}
});
}
private static void createAndShowGui() {
CaptureTextAreaEnter mainPanel = new CaptureTextAreaEnter();
JFrame frame = new JFrame("CaptureTextAreaEnter");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
This is the solution that functioning perfectly for my system.
AddTxtA.getDocument().putProperty("filterNewlines", Boolean.TRUE);
**When user press on the "Enter" button in the JTextArea, a blank space will be input instead of new line. Below shown the sample output for two different situations.
1) Without AddTxtA.getDocument().putProperty("filterNewlines", Boolean.TRUE);.
OUTPUT: "My name
is Adam."
2) With AddTxtA.getDocument().putProperty("filterNewlines", Boolean.TRUE);.
OUTPUT: "My name is Adam."
To replace the standard behaviour of "enter" key you should use the Input/Action maps of your text area
See the method registerKeyboardAction(ActionListener anAction,String aCommand,KeyStroke aKeyStroke,int aCondition). As action listener you should take the Action from your "send" button, command is a string your choise, key-stroke is KeyStroke.getKeyStroke(KeyEvent.VK_ENTER) and condition is JComponent.WHEN_FOCUSED.
Related
I have a GUI window with several components. Some of them are buttons to which I added keyboard shortcuts. For example, a certain button can be triggered by pressing "a" anywhere in the window. One of the components in a JTextArea. Now, when the focus is in the textarea, and the user types, e.g., "aha" into the JTextArea, the button is triggered twice (in addition to the text "aha" being added to the text area). How can I turn this off? I want the JTextArea to consume the "a" event, so that it does not also trigger the button.
What I want: if an "a" is typed anywhere in the window except in the JTextArea, I want my button to be triggered. But I don't want the button to be triggered while the JTextArea is in focus and the user is typing into the JTextArea.
What I already tried: I tried adding a KeyListener to the JTextArea, which intercepts and consumes any key that is typed. But it had the opposite effect: the button is still triggered, but the letter is not added to the JTextArea.
Here is a minimal example:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
// Create a simple GUI window
public class Gui
{
private static void createWindow()
{
// Create a frame.
JFrame frame = new JFrame("Simple");
// Add a
// Add a text area.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextArea textarea = new JTextArea();
textarea.setPreferredSize(new Dimension(300, 100));
frame.getContentPane().add(textarea, BorderLayout.CENTER);
// Add a button.
JButton button = new JButton();
button.setText("Button");
button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.get\
KeyStroke(KeyEvent.VK_A, 0), "key");
Action action = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
System.out.println("Action!");
button.doClick();
}
};
button.getActionMap().put("key", action);
frame.getContentPane().add(button, BorderLayout.LINE_END);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[])
{
System.out.println("Hello, World");
createWindow();
}
}
The JTextArea listens for keyTyped events. You are adding a binding for keyPressed.
If you instead create the binding for keyTyped event then the text area will handle the event:
//button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "key");
button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("typed a"), "key");
So I am currently having a problem with my Java Swing timer but first lemme describe what I am trying to do.
So I have a Swing GUI that updates a map with JSON data every 'X' number of seconds. The user can input the number of seconds into a text field and then click a button to start updating the map. The map will then update by querying the JSON based on the input.
So I am using a Swing timer to repeat a certain action event based on the input of the the user. Seen below:
clickOkButton.addActionListener(e1 -> {
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String url = "";
url = new_text.getText();
layer[0] = (RenderableLayer) geo.createLayerFromSource(url);
appFrame.getWwd().getModel().getLayers().set(20, layer[0]);
ltree.getModel().refresh(appFrame.getWwd().getModel().getLayers());
}
};
int time = Integer.parseInt(queryTime.getText());
Timer timer = new Timer(time * 1000, actionListener);
timer.setRepeats(true);
//timer.setDelay(1);
timer.start();
d.setVisible(false);
//System.out.println(text);
});
When the program is launched whatever time the user enters first works great. But then if they change the time the timer doesn't change.
int time = Integer.parseInt(queryTime.getText());
Timer timer = new Timer(time * 1000, actionListener);
It has something to do with these lines but I just can't figure it out. I'm pulling the numerical value from the text field and setting it as the delay in the timer. But it only works the first time the program is launched and not when it is changed.
Any help would be much appreciated.
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SwingTimerDemo extends JPanel {
final static int height = 500;
final static int width = 500;
final static String title = "default title";
JFrame frame = new JFrame(title);
JTextField field = new JTextField(10);
JTextArea area = new JTextArea(50,20);
public static void main(String[] args) {
SwingUtilities.invokeLater(
() -> new SwingTimerDemo().start());
}
public SwingTimerDemo() {
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
// add this panel to the frame
frame.add(this);
// add the JTextArea and JTextField to the panel
add(area);
add(field);
setPreferredSize(
new Dimension(500, 500));
frame.pack();
// center the frame on the screen
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void start() {
// append the string to the JTextArea
Timer t = new Timer(0, (ae)->area.append("Processing...\n"));
// set the inter-event delay to 2 seconds
t.setDelay(2000);
// start the timer
t.start();
field.addActionListener(ae->{
String text = field.getText();
field.setText(""); // "erase" the text
// convert to a number
int delay = Integer.parseInt(text);
// reset the timer delay
t.setDelay(delay*300);
});
}
}
Assuming you are familiar with Frames and Panels I will skip to the JTextField and JTextArea.
the field is where the user types in the delay. It is notified using an actionListener. That input is then retrieved, parsed as an int and sets the timer delay.
the area is simply a place where the timer writes the output.
Note that instead of an event when the user types in information, a button could be used instead. The user types in the information and then clicks the button. In that case, there would be no need for JTextField listener. Instead the listener would be for the button to check the text field.
This is a very rudimentary example to demonstrate the interaction between two actionListeners. If the user types in anything but a number an exception will be thrown. You may want to check out the Java Tutorials where they talk about event handling and other things that you would find interesting.
I don't want the user to select the content on JTextArea. I use setEditable(false) but it's not working. How to disable this feature of JTextArea component. Could you give me advise. Thanks.
If you would like to just disable text selection on any swing control such as JtextArea you can use the coding below:
JtextArea.setHighlighter(null);
This one line of coding will help disable the text selection and can be placed in the constructor or within a initialized method upon Frame execution.
Hope this helps
You can set the "mark" equal to the "dot" of the caret. When these values are equal there is no text selection:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class NoTextSelectionCaret extends DefaultCaret
{
public NoTextSelectionCaret(JTextComponent textComponent)
{
setBlinkRate( textComponent.getCaret().getBlinkRate() );
textComponent.setHighlighter( null );
}
#Override
public int getMark()
{
return getDot();
}
private static void createAndShowUI()
{
JTextField textField1 = new JTextField("No Text Selection Allowed");
textField1.setCaret( new NoTextSelectionCaret( textField1 ) );
textField1.setEditable(false);
JTextField textField2 = new JTextField("Text Selection Allowed");
JFrame frame = new JFrame("No Text Selection Caret");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textField1, BorderLayout.NORTH);
frame.add(textField2, BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Late to the party, but here are my findings. I had tried using setEnabled(false) on a JTextPane displaying static (not user-modifiable) content such as line numbers (for another text component). This one alone prevents the component from getting focus and text selection on it:
JTextArea textArea = new JTextArea("Static text");
textArea.setEnabled(false);
My problem with setEnabled(false) is that it forces a single disabledTextColor for all of the text (I've traced it down to javax.swing.text.GlyphView.paint()), while I want to style individual lines/chunks. I've finally tried setFocusable(false) that appears to satisfy both needs:
Not user focusable and no user selection on it;
Custom text color could be applied to individual parts of the content, or it just doesn't change the text color to the disabled one.
The complete solution needs additional setEditable(false) to prevent the mouse cursor from changing but that's it – two properties:
JTextArea textArea = new JTextArea("Static text");
textArea.setEditable(false);
textArea.setFocusable(false);
Is there any way to get the source of an event? I know event.getSource() however, is there any way to convert it into a string?
For example, if the source is a button, button1, is there anyway to assign the value button1 to a string variable? (I'm dealing with a lot of buttons and so, I can't write if statements)
For the sake of clarity:
The getSource() method returns the object from which the Event initially occurred. You could use this to get some sort of property from the element, like the text inside a label or the name of a button.
These are Strings, but if you chose to go this route, I would make sure you pick something that is uniform across all components that will be calling that ActionListerner.
This is where getActionCommand() might come in handy. You can set unique 'identifiers' when components are created, and the access them later.
JButton button = new JButton("Button");
button.setActionCommand("1");
JButton button = new JButton("Button");
button.setActionCommand("2");
Then you can compare these later using any method you like, or you could do something fancy, like this (because you said you didn't want to use if-else statements):
String command = e.getActionCommand();
int i = Integer.parseInt(command);
switch (i) {
case 1: // do something
break;
}
According to the Java docs:
Returns the command string associated with this action. This string allows a "modal" component to specify one of several commands, depending on its state. For example, a single button might toggle between "show details" and "hide details". The source object and the event would be the same in each case, but the command string would identify the intended action.
Keep in mind that I think this is best approach only if you are using one ActionListerner for lots of components. As another answer pointed out, you could just make unique ActionListeners per each button.
Hope this helps you!
You can pass anything you want to an action listener through the constructor, as DaaaahWhoosh stated in his comment.
package com.ggl.fse;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ButtonActionListener implements ActionListener {
private String buttonText;
public ButtonActionListener(String buttonText) {
this.buttonText = buttonText;
}
#Override
public void actionPerformed(ActionEvent event) {
// TODO Auto-generated method stub
}
}
The buttonText String will be available in the actionPerformed method.
You can use a specific ActionListener for each JButton. Try this code:
private static String text;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(200, 200, 200, 200);
frame.setLayout(new BorderLayout());
JButton button1 = new JButton("Button 1");
button1.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
text = button1.getText();
JOptionPane.showMessageDialog(null, "Text is: " + text);
}
});
JButton button2 = new JButton("Button 2");
button2.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
text = button2.getText();
JOptionPane.showMessageDialog(null, "Text is: " + text);
}
});
frame.add(button1, BorderLayout.NORTH);
frame.add(button2, BorderLayout.SOUTH);
frame.setVisible(true);
}
I've got an array that creates buttons from A-Z, but I want to use it in a
Method where it returns the button pressed.
this is my original code for the buttons:
String b[]={"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
for(i = 0; i < buttons.length; i++)
{
buttons[i] = new JButton(b[i]);
buttons[i].setSize(80, 80);
buttons[i].setActionCommand(b[i]);
buttons[i].addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String choice = e.getActionCommand();
JOptionPane.showMessageDialog(null, "You have clicked: "+choice);
}
});
panel.add(buttons[i]);
}
I wasn't sure exactly what you question was, so I have a few answers:
If you want to pull the button creation into a method - see the getButton method in the example
If you want to access the actual button when it's clicked, you can do that by using the ActionEvent.getSource() method (not shown) or by marking the button as final during declaration (shown in example). From there you can do anything you want with the button.
If you question is "How can I create a method which takes in a array of letters and returns to me the last clicked button", you should modify you question to explicitly say that. I didn't answer that here because unless you have a very special situation, it's probably not a good approach to the problem you're working on. You could explain why you need to do that, and we can suggest a better alternative.
Example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TempProject extends Box{
/** Label to update with currently pressed keys */
JLabel output = new JLabel();
public TempProject(){
super(BoxLayout.Y_AXIS);
for(char i = 'A'; i <= 'Z'; i++){
String buttonText = new Character(i).toString();
JButton button = getButton(buttonText);
add(button);
}
}
public JButton getButton(final String text){
final JButton button = new JButton(text);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "You have clicked: "+text);
//If you want to do something with the button:
button.setText("Clicked"); // (can access button because it's marked as final)
}
});
return button;
}
public static void main(String args[])
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setContentPane(new TempProject());
frame.pack();
frame.setVisible(true);
new TempProject();
}
});
}
}
ActionListener can return (every Listeners in Swing) Object that representing JButton
from this JButton you can to determine, getActionCommand() or getText()
I'm not sure what exactly you want, but what about storing the keys in a queue (e.g. a Deque<String>) and any method that needs to poll the buttons that have been pressed queries that queue. This way you would also get the order of button presses.
Alternatively, you could register other action listeners on each button (or a central one that dispatches the events) that receive the events in the moment they are fired. I'd probably prefer this approach, but it depends on your exact requirements.
try change in Action listener to this
JOptionPane.showMessageDialog(null, "You have clicked: "+((JButton)e.getSource()).getText());
1. First when you will be creating the button, please set the text on them from A to Z.
2. Now when your GUI is all ready, and you click the button, extract the text on the button, and then display the message that you have clicked this button.
Eg:
I am showing you, how you gonna extract the name of the button pressed, i am using the getText() method
butt.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(null, "You have clicked: "+butt.getText());
}
});