I want to display a TextField only when user has entered a value in Input field
Here is my code:
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class PlayingAround {
JFrame frame;
JTextField display;
JTextField input;
public static void main(String[] args) {
PlayingAround obj = new PlayingAround();
obj.create();
}
private void create() {
frame = new JFrame();
display = new JTextField();
input = new JTextField();
display.setEditable(false);
display.setVisible(false);
input.addKeyListener(new Listener());
frame.add(BorderLayout.NORTH, display);
frame.add(BorderLayout.SOUTH, input);
frame.setSize(300, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class Listener implements KeyListener {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
display.setVisible(true);
display.setText(input.getText());
}
}
}
But my problem is that the Display JTextField doesn't becomes visible until there are some events like Resizing the Window, Minimizing and maximizing the Window.
I tried calling frame.repaint() in the keyReleased Method but even it has not helped.
You should call revalidate() and repaint() on the container that holds the JTextField after placing the text field component in the container. The revalidate() call sends a request to the container's layout managers to re-layout its components. The repaint() then requests that the JVM request of the paint manager to redraw the newly laid out container and its child components. The repaint() is not always needed but is usually a good idea.
Also, don't use a KeyListener for this, but rather a DocumentListener on the first text component's Document. This way, if the user empties the first text component, you can make the second text component disappear if desired. Also, text can be entered without key presses, and you want to allow for that.
Related
I'm trying to find a way how to update a panel content after changing a state variable.
Concretely in the example below, there is simple JPanel inside JFrame with two buttons. When the app starts, its state variable ("window") equals "home" so home button should be invisible. After clicking on the page button the state variable change and so both buttons visibility should change after frame repainting. (i.e. the page button should disappear and the home button should appear).
In this case, it is possible to solve it without the state variable just using setVisibility() method for buttons. But in my app, I would like to have more JComponetns connected to the state variable. Is there a way how to do it?
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class JPanelUpdateTest {
private JFrame frame;
private String window = "home";
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JPanelUpdateTest window = new JPanelUpdateTest();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public JPanelUpdateTest() {
initialize();
}
private void initialize() {
frame = new JFrame("JPanelUpdateTest");
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
JButton btnHome = new JButton("home");
btnHome.setVisible(window == "home" ? false : true);
btnHome.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
window = "page";
panel.revalidate();
frame.repaint();
}
});
panel.add(btnHome);
JButton btnPage = new JButton("page");
btnPage.setVisible(window == "page" ? false : true);
btnPage.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
window = "home";
panel.revalidate();
frame.repaint();
}
});
panel.add(btnPage);
}
}
The problem is that initialize is only being called once, at object creation, and it should only be called once, and because of this the setVisible(...) code is not being called from the ActionListeners. Instead you need to put the mechanisms for changing the views within the ActionListeners themselves, not just changing state, not unless you are using a "bound property" and PropertyChangeListeners.
Myself, I'd recommend using a CardLayout to assist you in your swapping, and rather than directly changing Strings, call a public method of your class -- planning for when and if the ActionListener (controller) code is ever removed from the view class.
Also, regarding:
btnPage.setVisible(window == "page" ? false : true);
don't compare Strings using == or !=. Use the equals(...) or the equalsIgnoreCase(...) method instead. Understand that == checks if the two object references are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here.
Also, if all you want to do is change the text and behavior that a JButton is doing, then you can change this easily by using setText(...) to change only the text, and for a deeper change, call setAction(Action action) to change text and state.
I'm trying to update a JFrame, and all of the components in the frame, including JPanel, JLabel components, etc. I tried using revalidate(), but that didn't seem to be working. I have a JLabel in the frame displaying an int, and I have the int iterating by 1 when I click a JButton. I can see that the value of the int changes, but the actual text on the label doesn't change. I know I can use JLabel.setText(), but is there a method to use for all components, that would update the displayed text/image upon pressing a button?
Here is my code below:
package repainttest;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Window extends JFrame {
int test = 1;
JLabel label;
public Window() {
setLayout(new FlowLayout());
label = new JLabel(Integer.toString(test));
add(label);
JButton button = new JButton("Add");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
test +=1;
System.out.println(test);
refresh();
}
});
add(button);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void refresh() {
SwingUtilities.updateComponentTreeUI(this);
revalidate();
repaint();
}
}
If you do not set the new text (test) on the JLabel, it is never going to know that the value of test is changing. So, please insert following statement:
label.setText(String.valueOf(test));
in public void actionPerformed(ActionEvent e) after following statement:
test +=1;
and see the results.
When I was creating a bunch of JTextFields I saw that first one is selected. I want to deselect it, because I have focus listener and it's running automatically. Any clues?
SSCCE:
JTextField tf = new JTextField("hello");
tf.setForeground(Color.decode("0x8C8C8C")); // for nice comment inside the text field
textFieldKwotaWplacona.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e)
{
if(tf.getForeground() != Color.BLACK)
{
tf.setText("");
tf.setForeground(Color.BLACK);
}
} #Override
public void focusLost(FocusEvent arg0) {}});
//for deleting "nice comment" after click
tf.setBounds(//some bounds);
add(tf);
Repeat that process for another text field
EDIT2 :
actual code (I believe its sscce :P)
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
public class Main extends JFrame implements ActionListener
{
JTextField textFieldKwotaWplacona, textFieldOprocentowanie, textFieldDlugoscLokaty, textFieldKwotaOtrzymana;
Main()
{ setSize(500,300);
setLayout(null);
setTitle("Program do liczenia procentu składanego");
setDefaultCloseOperation(EXIT_ON_CLOSE);
textFieldKwotaWplacona = new JTextField("Ilość pieniędzy wpłaconych");
textFieldKwotaWplacona.setForeground(Color.decode("0x8C8C8C"));
textFieldKwotaWplacona.addActionListener(this);
textFieldKwotaWplacona.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e)
{
if(textFieldKwotaWplacona.getForeground() != Color.BLACK)
{
textFieldKwotaWplacona.setText("");
textFieldKwotaWplacona.setForeground(Color.BLACK);
}
} #Override
public void focusLost(FocusEvent arg0) {}});
textFieldKwotaWplacona.setBounds(10, 10, 100, 20);
add(textFieldKwotaWplacona);
textFieldOprocentowanie = new JTextField("Oprocentowanie");
textFieldOprocentowanie.setForeground(Color.decode("0x8C8C8C"));
textFieldOprocentowanie.addActionListener(this);
textFieldOprocentowanie.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e)
{
if(textFieldOprocentowanie.getForeground() != Color.BLACK)
{
textFieldOprocentowanie.setText("");
textFieldOprocentowanie.setForeground(Color.BLACK);
}
}
#Override
public void focusLost(FocusEvent arg0) {}});
textFieldOprocentowanie.setBounds(10, 40, 100, 20);
add(textFieldOprocentowanie);
}
#Override
public void actionPerformed(ActionEvent arg0)
{
// TODO Auto-generated method stub
}
public static void main(String[] args)
{
Main a=new Main();
a.setVisible(true);
}
}
I want to set focus to window or sth else, in order to prevent text from disappearing.
As discussed in the comments, I added a radio button to take the focus instead:
public class Main extends JFrame {
JTextField textFieldKwotaWplacona, textFieldOprocentowanie;
Main() {
setTitle("Program do liczenia procentu składanego");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
textFieldKwotaWplacona = new JTextField("Ilość pieniędzy wpłaconych");
textFieldKwotaWplacona.setForeground(Color.decode("0x8C8C8C"));
textFieldKwotaWplacona.addFocusListener(new FieldFocusListener(textFieldKwotaWplacona));
add(textFieldKwotaWplacona);
textFieldOprocentowanie = new JTextField("Oprocentowanie");
textFieldOprocentowanie.setForeground(Color.decode("0x8C8C8C"));
textFieldOprocentowanie.addFocusListener(new FieldFocusListener(textFieldOprocentowanie));
add(textFieldOprocentowanie);
JRadioButton btn = new JRadioButton("text");
add(btn);
pack();
btn.requestFocusInWindow();
}
private class FieldFocusListener extends FocusAdapter {
private JTextField field;
FieldFocusListener(JTextField field) {
this.field = field;
}
#Override
public void focusGained(FocusEvent e) {
if (field.getForeground() != Color.BLACK) {
field.setText("");
field.setForeground(Color.BLACK);
}
}
}
public static void main(String[] args) {
Main a = new Main();
a.setVisible(true);
}
}
Explanation
From the tutorial:
If you want to ensure that a particular component gains the focus the first time a window is activated, you can call the requestFocusInWindow method on the component after the component has been realized, but before the frame is displayed.
That means btn.requestFocusInWindow() must appear after pack() and before a.setVisible(true).
The reason you need another component to take the focus is that when a window is focused, a component inside it must gain the focus.
Notes:
If you want a better text field hint, see #camickr's answer.
Don't use null layout. Pick one that serves your GUI design (I picked FlowLayout just because it's fast to use, though probably not what you need).
Instead of setting the size of the frame, pack() after all components had been added.
Instead of creating the same focus listener for every text field, just create it as a class and reuse it. I show one way with passing the component to a constructor, but you can get rid of that and use e.getComponent() to get the text field instance.
In your constructor, you could use the method requestFocusInWindow().
This is what was working for me here-
After creating the JFrame, call frame.requestFocusinWindow();. This will make sure your text field is not focused.
Then, when you focus on the text field, the event is being fired.
tf.setForeground(Color.decode("0x8C8C8C")); // for nice comment inside the text field
Maybe you are trying to set a prompt for the text field that disappears when the text field gains focus?
If so, check out Text Field Prompt for a solution.
If not, then post a proper SSCCE, because I still can't guess what you are trying to do.
I have a simple GUI with two components: a JTextField, and a custom component (MyComponent).
Initially the text field has focus, and clicking on the custom component causes that to have focus.
Currently I am manually setting the focus using requestFocusInWindow, but the focusLost event happens after the mousePressed event has finished. Is there any way to get the focusLost event to happen before the mousePressed event finishes?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Example {
public static void main(String[] args) {
JFrame frame = new JFrame("Example");
frame.setLayout(new FlowLayout());
JTextField textField = new JTextField(10);
textField.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent event) {
System.out.println("focusLost");
}
});
frame.add(textField);
frame.add(new MyComponent());
frame.pack();
frame.setVisible(true);
}
private static class MyComponent extends JComponent {
public MyComponent() {
setFocusable(true);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent event) {
requestFocusInWindow();
System.out.println("mousePressed");
}
});
}
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
}
It also needs to be validated (and stored)
You can use an InputVerifier on the JTextField. It will validate the text in the text field before focus is transferred. If the data is not valid focus will stay on the text field.
Edit:
Can I remove this behaviour and instead revert the text field to the previous value if the data is not valid when losing focus?
Instead of using a JTextField use a JFormattedTextField it will revert the data to the previous value. You don't need to use an InputVerifier. Read the section from the Swing tutorial on How to Use Formatted Text Fields for more information and examples.
I would like my top level JFrame as well as the JDesktopPane to listen on key events at all times, no matter what component is currently focused/visible.
Currently, when this program is launched, the JFrame is receving the key events OK. However if a JinternalFrame is clicked, then events no longer reach the JFrame. Even if I do click outside the JInternalFrame, (which is supposed to be the JDesktop component), the events no longer reach the JFrame, like they did in the beginning. Why? Thx.
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class focus extends JFrame implements KeyListener {
private focus() {
JDesktopPane desktop = new JDesktopPane();
setContentPane(desktop);
addPane(this, "one");
addPane(this, "two");
addPane(this, "three");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(400, 450);
addKeyListener(this);
}
public static void main(String[] args) {
focus t = new focus();
}
private void addPane(JFrame frame, String name) {
JTextArea textArea = new JTextArea();
textArea.setName(name);
textArea.setEditable(false);
addWindow(frame, textArea, name);
}
private JInternalFrame addWindow(JFrame frame, JComponent component,
String name) {
JScrollPane scrollablePane = new JScrollPane(component);
JInternalFrame iframe = new JInternalFrame(name + " ", true, true,
true, true);
iframe.setSize(300, 300);
iframe.setLocation((int) (100 * Math.random()),
(int) (100 * Math.random()));
iframe.setVisible(true);
iframe.getContentPane().add(scrollablePane);
frame.getContentPane().add(iframe);
return iframe;
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
System.err.println(e.getKeyChar());
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Events are dispatched to the component that has focus.
Why would you want the frame to receive events when focus is on the internal frame? What is your actual requirement?
If you want to invoke an Action, then the easiest way is to use menu bars with menu items and then you can assign accelerators to each menu item to the Action can be invoked no matter with component has focus.
Since you would like your JFrame to be able to listen to all the key events no matter what element the focus is on, add that as the key listener to all the elements on which there can be focus. Like so,
textArea.setEditable(false);
**textArea.addKeyListener(this);**
and
iframe.getContentPane().add(scrollablePane);
**iframe.addKeyListener(this);**
frame.getContentPane().add(iframe);
You might be interested in the java.awt.KeyboardFocusManager class. I believe it is the class that, among other things, dispatches KeyEvents to the focused component. It seems that the methods addKeyEventDispatcher and addKeyEventPostProcessor could be used to get KeyEvents before and after being sent to the focused component, respectively.
I have not ever used this class before, so I do not know if it does what I expect it to do.