I create a Popup using the PopupFactory.getPopup method. According to the documentation, I am required to call the hide() method on the popup when it is no longer needed.
In my application, the popup is the child of a JLabel which may be removed from the current frame in a number of different situations. (Either the JLabel itself or one of its parent containers is removed.) Rather that calling hide() in every single place (and making the Popup object available in all these places) I would prefer to be able to detect the removal of the JLabel or one of its parent containers.
How can I detect the removal? I naively assumed that the removal of a component meant the removal/hiding of its children, but as the code below shows, the popup survives the removal of the JLabel.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Xyzzy extends JFrame {
static Xyzzy frame;
static JPanel panel;
static JLabel text1;
static JLabel text2;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame = new Xyzzy();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
frame.add(panel);
text1 = new JLabel("text1");
text2 = new JLabel("text2");
panel.add(text1);
frame.add(new JButton(new AbstractAction("Add popup") {
public void actionPerformed(ActionEvent e) {
PopupFactory factory = PopupFactory.getSharedInstance();
Popup popup = factory.getPopup(text1, new JLabel("POPUP"),frame.getX()+300,frame.getY()+300);
popup.show();
}
}));
frame.add(new JButton(new AbstractAction("New label") {
public void actionPerformed(ActionEvent e) {
panel.remove(text1);
panel.add(text2);
panel.revalidate();
}
}));
frame.setSize(600, 600);
frame.setVisible(true);
}
});
}
}
This code creates a JFrame displaying the text "text1" and two buttons. If you press the button labeled "Add popup", a Popup with the text "POPUP" appears in the window. This Popup is a child of text1. Press the "New label" button and "text1" is removed from the display, but the Popup survives.
I need to be able to detect when text1 or the containing panel is removed so that I can hide the popup. I want to avoid adding code where the actual remove() method is called.
You can use HierarchyListener:
public void actionPerformed(ActionEvent e) {
PopupFactory factory = PopupFactory.getSharedInstance();
final Popup popup = factory.getPopup(text1, new JLabel("POPUP"),frame.getX()+300,frame.getY()+300);
text1.addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent e) {
if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED
&& (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
popup.hide();
}
}
});
popup.show();
}
Related
Having an issue with some bits of my code.
label1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
label1.setText(-Here i want the button to open up a new "update window, and it will update the label to the text i'll provide in a seperate window);
}
});
Is there any way to do it without without an additional form? Just wanted to add that i have several labels, and i'm not sure on how to start with it.
One approach is to return a result from your update dialog which you can then use to update the text in label1. Here's an example which updates the label text based on the result return from a JOptionPane
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setMinimumSize(new Dimension(200, 85));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
JLabel label = new JLabel("Original Text");
frame.add(label);
JButton button = new JButton("Click Me");
frame.add(button);
// to demonstrate, a JOptionPane will be used, but this could be replaced with a custom dialog or other control
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int result = JOptionPane.showConfirmDialog(frame, "Should I update the label?", "Test", JOptionPane.OK_CANCEL_OPTION );
// if the user selected 'Ok' then updated the label text
if(result == JOptionPane.OK_OPTION) {
label.setText("Updated text");
}
}
});
frame.setVisible(true);
}
});
}
}
Another approach would be to use an Observer and Observable which would listen for updates and change the label text accordingly. For more on the Observer, take a look at this question: When should we use Observer and Observable
I'm changing my JFrame's content pane and simply want to focus a JTextField in the new panel. So I'm doing this:
JPanel pNew = new JPanel();
frame.setContentPane(pNew);
frame.revalidate();
frame.repaint();
public JPanel() {
...
tf.requestFocusInWindow();
}
When I use setVisible(false) and setVisible(true) instead of revalidating and repainting my frame, I get my wished effect, but that's not the way I want to do it.
What else happens in setVisible() but revalidating and repainting?
A CardLayout is typically used to swap panels.
However, even the default implementation of CardLayout does not set focus on the panel when it is swapped. However you can check out Card Layout Focus which will allow you to request focus on the panel when it is switched.
The requestFocusInWindow() method only works on a component that is displayed in a visible frame. So you can't invoke the method in the constructor of the class.
You could use the RequestFocsListener found in Dialog Focus. It will wait until the panel is added to a visible GUI before generating the event.
I got it to work simply by putting the requestFocusInWindow() call in the button's action listener. As camickr mentioned the call needs to be made after the constructor. Here's an example program showing how I got it to work. Hope it helps!
public class PanelRevalidate {
public JFrame frame;
public MyPanel panel1, panel2;
public PanelRevalidate() {
frame = new JFrame();
panel1 = new MyPanel(1);
panel2 = new MyPanel(2);
frame.setContentPane(panel1);
panel1.getSwap().addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
frame.setContentPane(panel2);
frame.revalidate();
panel2.getTextField().requestFocusInWindow();
}
});
panel2.getSwap().addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
frame.setContentPane(panel1);
frame.revalidate();
panel1.getTextField().requestFocusInWindow();
}
});
frame.setVisible(true);
frame.setResizable(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new PanelRevalidate();
}
});
}
}
And the MyPanel class:
public class MyPanel extends JPanel {
public JTextField tf;
public JButton swap;
public JLabel panel_label;
public MyPanel(int n) {
tf = new JTextField(25);
swap = new JButton("Swap");
panel_label = new JLabel("panel " + n);
add(tf);
add(swap);
add(panel_label);
}
public JButton getSwap() {
return swap;
}
public JTextField getTextField() {
return tf;
}
}
I have a JFrame in which I remove and add components and each time I do this, I resize the frame accordingly. I added a Component Listener but of course it gets triggered both by the user resizing the window and also by my setSize() methods.
Is there any way to see from the ComponentEvent whether the user resized the window or I did through setSize()?
The posible solutions I found are:
1. Use a flag - boolean resizing - which I set to true before setSize() and to false after that.
2. Add a mouseDragged listener and compare sizes before and after the drag.
The second one is definitely not a good choice. The first one would work but I would like to know if I can find in a simple way whether the user is the one who resized the window or not.
I resize the frame accordingly
Whats wrong with using pack()?
I remove and add components and each time I do this,
Then this is where you should set your Boolean value:
programResize == true:
panel.add(...);
frame.setSize(...); // this should cause the ComponentListener to fire
// the ComponentListener would then use programResize == false;
Or a better option option could be:
component.removeComponentListener(...);
panel.add(...);
frame.setSize(...);
component.addComponentListener(...);
I like this approach better because all the logic based on the manual update is self contained in one place and there is no need to define a Boolean variable.
Is there any way to see from the ComponentEvent whether the user
resized the window or I did through setSize()?
yes it is, use boolean flag reseted by Swing Timer
in the case that user resized window then ComponentListener firing a new event per every pixel, bunch of events
by determine the contianer set(Xxx)Size is this event fired only once time
example about events from ComponentListner
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
public class ComponentEventDemo extends JPanel
implements ComponentListener, HierarchyListener,
ItemListener {
private JFrame frame;
private static final long serialVersionUID = 1L;
private JTextArea display;
private JLabel label;
private JButton button = new JButton("Change Size");
private String newline = "\n";
public ComponentEventDemo() {
display = new JTextArea();
display.setEditable(false);
JScrollPane scrollPane = new JScrollPane(display);
scrollPane.setPreferredSize(new Dimension(350, 200));
label = new JLabel("This is a label", JLabel.CENTER);
label.addComponentListener(this);
JCheckBox checkbox = new JCheckBox("Label visible", true);
checkbox.addItemListener(this);
checkbox.addComponentListener(this);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Dimension dim = frame.getPreferredSize();
if (!dim.equals(new Dimension(800, 600))) {
frame.setPreferredSize(new Dimension(800, 600));
frame.pack();
} else {
frame.setPreferredSize(new Dimension(400, 300));
frame.pack();
}
}
});
JPanel panel = new JPanel(new GridLayout(1, 3));
panel.add(label);
panel.add(checkbox);
panel.add(button);
panel.addComponentListener(this);
frame = new JFrame("ComponentEventDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane, BorderLayout.CENTER);
frame.add(panel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
label.setVisible(true);
label.revalidate();
label.repaint();
} else {
label.setVisible(false);
}
}
protected void displayMessage(String message) {
//If the text area is not yet realized, and
//we tell it to draw text, it could cause
//a text/AWT tree deadlock. Our solution is
//to ensure that the text area is realized
//before attempting to draw text.
// if (display.isShowing()) {
display.append(message + newline);
display.setCaretPosition(display.getDocument().getLength());
//}
}
#Override
public void componentHidden(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Hidden");
}
#Override
public void componentMoved(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Moved");
}
#Override
public void componentResized(ComponentEvent e) {
displayMessage(e.getComponent().getClass().getName() + " --- Resized ");
}
#Override
public void componentShown(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Shown");
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ComponentEventDemo componentEventDemo = new ComponentEventDemo();
}
});
}
#Override
public void hierarchyChanged(HierarchyEvent e) {
displayMessage(e.getComponent().getClass().getName() + " --- Hierarchy changed");
}
}
I have created a frame in Java which has some textfields and buttons in it. Assuming that user wants more textfields (for example to add more data), I want to put a button and when a user clicks the button, then a new textfield should appear. then user can fill data in it and again by clicking that button another textfield should appear.
How can I do this ? What code I need to write for the button to show more and more text fields by clicking button?
Thank you !
It would be wise that instead of adding components to your JFrame directly, you add them to a JPanel. Though related to your problem, have a look at this small example, hopefully might be able to give you some hint, else ask me what is out of bounds.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JFrameExample
{
private JFrame frame;
private JButton button;
private JTextField tfield;
private String nameTField;
private int count;
public JFrameExample()
{
nameTField = "tField";
count = 0;
}
private void displayGUI()
{
frame = new JFrame("JFrame Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0, 1, 2, 2));
button = new JButton("Add JTextField");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent ae)
{
tfield = new JTextField();
tfield.setName(nameTField + count);
count++;
frame.add(tfield);
frame.revalidate(); // For JDK 1.7 or above.
//frame.getContentPane().revalidate(); // For JDK 1.6 or below.
frame.repaint();
}
});
frame.add(button);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new JFrameExample().displayGUI();
}
});
}
}
Supposing that you have a main container called panel and a button variable button which is already added to panel, you can do:
// handle the button action event
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// create the new text field
JTextField newTextField = new JTextField();
// add it to the container
panel.add(newTextField);
panel.validate();
panel.repaint();
}
});
When adding the new text field, you may need to mention some layout related characteristics, depending on the layout manager you are using (for instance if you use GridBagLayout, you will need to specify the constraints).
Could you please help me on this one? I have a JDialog with some textfields, checkboxes and buttons. I want that when the frame is not focused anymore, to disappear. So I added a focus listener to the JDialog and when the focus is lost, I call dialog.setVisible(false);. The problem is that if I click on the checkbox,textfield or button, the frame loses it's focus and disappears. How could I keep it focused until the user clicks outside it's area?
EDIT : The "frame" I am referring to is a JDialog. I don't use a Frame nor a JFrame. All the components are placed on the JDialog. I want it to hide when not focused, but keep it focused until the user clicks outside it's area.
Seems like you had added the wrong Listener, what you should be adding is addWindowFocusListener(...), see this small sample program, is this what you want to happen :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DialogFocus
{
private JFrame frame;
private MyDialog myDialog;
public DialogFocus()
{
}
private void createAndDisplayGUI()
{
frame = new JFrame("JFRAME");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
myDialog = new MyDialog(frame, "My Dialog", false);
JButton showButton = new JButton("SHOW DIALOG");
showButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
if (!(myDialog.isShowing()))
myDialog.setVisible(true);
}
});
frame.add(showButton, BorderLayout.PAGE_END);
frame.setSize(300, 300);
frame.setVisible(true);
}
public static void main(String\u005B\u005D args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new DialogFocus().createAndDisplayGUI();
}
});
}
}
class MyDialog extends JDialog
{
private WindowFocusListener windowFocusListener;
public MyDialog(JFrame frame, String title, boolean isModal)
{
setTitle(title);
setModal(isModal);
JPanel contentPane = new JPanel();
JTextField tfield = new JTextField(10);
JComboBox cbox = new JComboBox();
cbox.addItem("One");
cbox.addItem("Two");
cbox.addItem("Three");
contentPane.add(tfield);
contentPane.add(cbox);
windowFocusListener = new WindowFocusListener()
{
public void windowGainedFocus(WindowEvent we)
{
}
public void windowLostFocus(WindowEvent we)
{
setVisible(false);
}
};
addWindowFocusListener(windowFocusListener);
add(contentPane);
pack();
}
}
Make the dialog modal, then the user cannot click on the frame.
Check the FocusEvent
it has public Component getOppositeComponent(). If the opposite component is child component of the JDialog don't hide the dialog.