Capture JFrame resize only once, when mouse is released - java

I am trying to capture the moment a user finished resizing a JFrame through the mouse drag of a frame corner. I have so far found the below options, but they both print BLAH again and again until I am done stretching the window size. I only want it to print BLAH once I released the mouse after the continuous dragging of the JFrame corner resizing it. Any thoughts?
frame.addComponentListener(new ComponentAdapter()
{
public void componentResized(ComponentEvent evt) {
Component c = (Component)evt.getSource();
System.out.println("BLAH");
}
});
AND
frame.addComponentListener(new ComponentListener() {
#Override
public void componentShown(ComponentEvent e) {
// TODO Auto-generated method stub
}
#Override
public void componentResized(ComponentEvent e) {
// TODO Auto-generated method stub
System.out.println("BLAH");
}
#Override
public void componentMoved(ComponentEvent e) {
// TODO Auto-generated method stub
}
#Override
public void componentHidden(ComponentEvent e) {
// TODO Auto-generated method stub
}
}
);

The core problem is, you're unlikely to be able to detect when a mouse event occurs on the frame decorations, as it tends to be handled at a much lower level.
One trick I've used in the past, is to use a short lived, single run, Swing Timer, which is restarted each time componentResized is called. This means that the Timer will only trigger AFTER a "short delay" after componentResized stops been called, for example
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Timer resizeTimer;
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel(".......");
add(label);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
if (resizeTimer == null) {
resizeTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(getSize().width + "x" + getSize().getHeight());
resizeTimer.stop();
resizeTimer = null;
}
});
resizeTimer.setRepeats(false);
}
resizeTimer.restart();
}
});
}
}
}

Related

The loading message is not updated when the process starts

I want to display a "loading message" when a process is started and I want to change the message when the process is finished. I tried to update the text from a JLabel before and after the thread with the process is started but the problem is that on the frame appears only the last update.
Here is my code:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MyClass extends JFrame {
private JLabel loading;
private JButton jButton;
private JPanel jPanel;
public static void main(String[] args) {
new MyClass();
}
MyClass() {
jPanel = new JPanel();
setLayout(new GridLayout(0, 1));
loading = new JLabel("");
loading.setVisible(true);
jButton = new JButton("Click me!");
addActionToJButon();
setSize(300, 300);
jPanel.add(jButton);
jPanel.add(loading);
add(jPanel);
setVisible(true);
}
private void addActionToJButon() {
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
loading.setText("Loading....");
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i <= 1000000; i++) {
System.out.println(i);
}
}
}).start();
loading.setText("Done!");
}
});
}
}
I was expecting to appear the label "Loading..." once what the process is started and the message "Done" when the process is finished but I can't find out why on the frame appears the label with the message "Done!".
Thanks to JB Nizet advices I used SwingWorker and the code is working now.
Here is the correct code:
package view;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
public class MyClass extends JFrame {
private JLabel loading;
private JButton jButton;
private JPanel jPanel;
public static void main(String[] args) {
new MyClass();
}
MyClass() {
jPanel = new JPanel();
setLayout(new GridLayout(0, 1));
loading = new JLabel("");
loading.setVisible(true);
jButton = new JButton("Click me!");
addActionToJButon();
setSize(300, 300);
jPanel.add(jButton);
jPanel.add(loading);
add(jPanel);
setVisible(true);
}
private void addActionToJButon() {
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
loading.setText("Loading....");
swingWorker();
}
});
}
private void swingWorker() {
SwingWorker worker = new SwingWorker<String, String>() {
#Override
protected String doInBackground() throws Exception {
// TODO Auto-generated method stub
for (int i = 0; i <= 1000000; i++) {
System.out.println(i);
}
return "Done";
}
protected void done() {
try {
String finished = get();
loading.setText(finished);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
worker.execute();
}
}
Because you are doing your "loading" within a thread, and you are setting your loading text outside the thread, you immediately set loading to "Done!" when you begin loading. What you want to do is set loading within your run() function like this:
private void addActionToJButon() {
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
#Override
public void run() {
loading.setText("Loading....");
// TODO Auto-generated method stub
for (int i = 0; i <= 1000000; i++) {
System.out.println(i);
}
loading.setText("Done!");
}
}).start();
}
});
}

Stopping Thread on Mouse Hover Event in Swing

I have a desktop application in which I am showing one Frame as notification but I want to stop that thread when mouse hover to the notification frame. Then how could I can do that?
Here current code:
final Notification not = new Notification();
Notification.notification_name.setText(msg[1]);
final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
s.schedule(new Runnable() {
public void run() {
not.setVisible(false); //should be invoked on the EDT
not.dispose();
}
}, 6, TimeUnit.SECONDS);
It's showing and Exit in 6 sec.
The code that I am trying.
final Notification not = new Notification();
not.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
try {
super.mouseEntered(e);
System.out.println("Mouse Entered");
//s.wait();
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception x) {
x.printStackTrace();
}
}
#Override
public void mouseExited(MouseEvent e) {
try {
super.mouseExited(e);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
s.schedule(new Runnable() {
public void run() {
not.setVisible(false); //should be invoked on the EDT
not.dispose();
}
}, 6, TimeUnit.SECONDS);
In this code Notification frame is showing till 6 sec but if user hover that frame then it should be Show till user mouse exit from that Frame.
Whenever you deal with anything that might affect the UI in some way, you need to be careful and ensure that everything is done within the context of the EDT.
javax.swing.Timer allows you to setup a callback at some time in the future that when triggered, will be called within the context of the EDT
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class TimeExample {
public static void main(String[] args) {
new TimeExample();
}
public TimeExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(new TestPane());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer timer;
public TestPane() {
setBorder(new LineBorder(Color.BLACK));
timer = new Timer(6000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setBackground(Color.RED);
}
});
timer.setRepeats(false);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (timer.isRunning()) {
timer.stop();
setBackground(Color.BLUE);
}
}
#Override
public void mouseExited(MouseEvent e) {
if (!timer.isRunning()) {
timer.restart();
setBackground(UIManager.getColor("Panel.background"));
}
}
/**
* Testing purposes only!!
*
* #param e
*/
#Override
public void mouseClicked(MouseEvent e) {
setBackground(UIManager.getColor("Panel.background"));
timer.restart();
}
};
addMouseListener(ma);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
}
}
Have a look at How to Use Swing Timers for more details

How to know the release listener is triggered after click operation or drag operation?

I asked a fuzzy question hours ago and hope this description can make it clear.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class TestMouseEvent {
public void createUI(){
JFrame frame = new JFrame("Test Mouse Event");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
MainPanel mainPanel = new MainPanel();
mainPanel.setPreferredSize(new Dimension(800, 600));
mainPanel.addMouseListener(new ImageMouseListener());
mainPanel.addMouseMotionListener(new ImageMouseAdapter());
frame.add(mainPanel,BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
TestMouseEvent testMouseEvent = new TestMouseEvent();
testMouseEvent.createUI();
}
#SuppressWarnings("serial")
class MainPanel extends JPanel{
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setFont(new Font("Arial", Font.PLAIN, 20));
g.drawString("I'm a panel and I'm being listened now", 200, 300);
}
}
class ImageMouseListener implements MouseListener{
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("clicked");
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("pressed");
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("released");
JOptionPane.showMessageDialog(null, "I only want to be showed when \"drag\" event over but not for click event!");
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
class ImageMouseAdapter extends MouseAdapter{
public void mouseDragged(MouseEvent e){
if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
System.out.println("dragged");
}
}
}
}
I only want to show the JOptionPane right after the drag operation but not the click operation.So how can I know the difference?
Inside your ImageMouseAdapter set a flag (let's name if dragInProgressFlag) to true. On release check the flag. If it's true show the JOptionPane and reset it back to false.
First, unify your MouseListener and MouseMotionListener into a single class, you can do this simply by using MouseAdapter...
Second, add a instance variable to act as a flag to indicate if a drag operation is taking place...
class ImageMouseListener extends MouseAdapter {
private boolean isDragging = false;
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("clicked");
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
if (isDragging) {
System.out.println("released");
JOptionPane.showMessageDialog(null, "I only want to be showed when \"drag\" event over but not for click event!");
}
isDragging = false
}
public void mouseDragged(MouseEvent e) {
if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
isDragging = true;
System.out.println("dragged");
}
}
}
Create a single instance of the ImageMouseListener and add it as the mouseListener and mouseMotionListener for your panel...
ImageMouseListener handler = new ImageMouseListener();
mainPanel.addMouseListener(handler);
mainPanel.addMouseMotionListener(handler);
You could consider using the inbuilt Drag'n'Drop support within in Java, depending on what you want to achieve, for example, have a look at Java - How to drag and drop JPanel with its components

I am trying to add a JButton array that I added to a panel onto my JFrame, yet nothing is showing up

Here is my code:
For some reason nothing will appear on my screen, yet I don't know why, I believe I am initializing it correctly and adding it. Help?
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class main implements MouseListener{
final int WIDTH = 800, HEIGHT = 500, BOARD_WIDTH = 10, BOARD_HEIGHT = 10;
private JButton [][]buttons = new JButton[BOARD_WIDTH][BOARD_HEIGHT];
public static void main(String[] args) {
// TODO Auto-generated method stub
new main();
}
public main()
{
Start();
}
private void Start()
{
JFrame mainFrame = new JFrame("MineSweeper");
mainFrame.setVisible(true);
mainFrame.setSize(WIDTH,HEIGHT);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setLocationRelativeTo(null);
mainFrame.setResizable(false);
mainFrame.setLayout(new BorderLayout());
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(BOARD_WIDTH, BOARD_HEIGHT));
for(int x = 0; x < BOARD_WIDTH; x++)
for(int y = 0; y < BOARD_HEIGHT; y++)
{
buttons[x][y] = new JButton("01");
buttons[x][y].addMouseListener(this);
p1.add(buttons[x][y]);
}
mainFrame.add(p1, BorderLayout.CENTER);
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
Thanks for any help!
Also sorry for any confusion it is that my buttons wont appear on the screen not that the frame will no appear.
Call mainFrame.setVisible(true); last
private void Start()
{
JFrame mainFrame = new JFrame("MineSweeper");
// Move this...
//mainFrame.setVisible(true);
//...
mainFrame.add(p1, BorderLayout.CENTER);
// To here
mainFrame.setVisible(true);
}
You should also launch you application within the context of the EDT. Take a look at Initial Threads for more details
You should also avoid using a MouseListener on buttons, they have a ActionListener API which includes notification when the use clicks the button or "active" key (usually Enter or Space)

Wait cursor and disable java application

I want to have the user press a button to kick off a background thread.
While the thread is processing, I want two things to happen:
1) A WAIT_CURSOR should be displayed.
2) The application should not respond to mouse events.
As per the setCursor documentation "This cursor image is displayed when the contains method for this component returns true for the current cursor location, and this Component is visible, displayable, and enabled. ".
I want my application to be disabled while this background thread is processing.
Any ideas how to get the functionality I want?
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class WaitCursor extends JFrame
{
private static final long serialVersionUID = 1L;
public WaitCursor()
{
setResizable(false);
setName(getClass().getSimpleName());
setTitle("My Frame");
setSize(300, 300);
getContentPane().add(new MyButtonPanel());
}
private class MyButtonPanel extends JPanel
{
private static final long serialVersionUID = 1L;
public MyButtonPanel()
{
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new BtnStartActionListener());
add(btnStart);
}
private class BtnStartActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
// Change to WAIT_CURSOR
Component root = SwingUtilities.getRoot((JButton) e.getSource());
JOptionPane.showMessageDialog(root, "Wait 10 seconds");
root.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// TODO: Disabling the root component prevents the WAIT_CURSOR from being displayed
root.setEnabled(false);
new Thread(new TimeKiller(root)).start();
}
}
}
private class TimeKiller implements Runnable
{
Component _root;
public TimeKiller(Component root)
{
_root = root;
}
public void run()
{
try
{
Thread.sleep(10 * 1000);
}
catch (InterruptedException e)
{
// Ignore it
}
// Change back to DEFAULT CURSOR
JOptionPane.showMessageDialog(_root, "Done waiting");
_root.setCursor(Cursor.getDefaultCursor());
_root.setEnabled(true);
}
}
private static void createAndShowGUI()
{
// Create and set up the window.
WaitCursor frame = new WaitCursor();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
createAndShowGUI();
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
});
}
}
One way to disable it is to use the glass pane to block mouse input.
For example:
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import javax.swing.*;
#SuppressWarnings("serial")
public class WaitCursor2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private JComponent glassPane;
private JButton runBackgroundProcBtn;
private JTextArea textarea = new JTextArea(15, 30);
public WaitCursor2(JComponent glassPane) {
this.glassPane = glassPane;
glassPane.setFocusable(true);
glassPane.addMouseListener(new MouseAdapter() {
}); // so it will trap mouse events.
add(new JTextField(10));
add(runBackgroundProcBtn = new JButton(new AbstractAction(
"Run Background Process") {
#Override
public void actionPerformed(ActionEvent arg0) {
runBackgroundProcessAction();
}
}));
add(new JScrollPane(textarea));
}
private void runBackgroundProcessAction() {
disableSystem(true);
glassPane.setVisible(true);
new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
long sleepTime = 5000;
Thread.sleep(sleepTime);
return null;
}
#Override
protected void done() {
disableSystem(false);
}
}.execute();
}
public void disableSystem(boolean disable) {
glassPane.setVisible(disable);
runBackgroundProcBtn.setEnabled(!disable);
if (disable) {
System.out.println("started");
glassPane.requestFocusInWindow(); // so can't add text to text components
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
} else {
System.out.println("done");
glassPane.setCursor(Cursor.getDefaultCursor());
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("WaitCursor2");
WaitCursor2 mainPanel = new WaitCursor2((JComponent) frame.getGlassPane());
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();
}
});
}
}
The glass pane will trap mouse events if it set visible and given a MouseListener. It will lose t his ability if it is set invisible. Likewise it will pull the caret from text components if you make it focusable and give it focus.
added a field current_active and at method actionPerformed, do a simple check. Albeit it is not perfect but for simple app, i think this do the trick. A crude way of solving your two requirement. :-) Hope it works for you too.
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class WaitCursor extends JFrame
{
private static boolean current_active = false;
public WaitCursor()
{
setResizable(false);
setName(getClass().getSimpleName());
setTitle("My Frame");
setSize(300, 300);
getContentPane().add(new MyButtonPanel());
}
private class MyButtonPanel extends JPanel
{
public MyButtonPanel()
{
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new BtnStartActionListener());
add(btnStart);
}
private class BtnStartActionListener implements ActionListener
{
// change to wait_cursor
public void actionPerformed(ActionEvent e)
{
if (!current_active)
{
Component root = SwingUtilities.getRoot((JButton) e.getSource());
JOptionPane.showMessageDialog(root, "Wait 10 seconds");
root.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// TODO: Disabling the root component prevents the WAIT_CURSOR from being displayed
//root.setEnabled(false);
current_active = true;
new Thread(new TimeKiller(root)).start();
}
}
}
}
private class TimeKiller implements Runnable
{
Component m_root;
public TimeKiller(Component p_root)
{
m_root = p_root;
}
#Override
public void run()
{
try
{
Thread.sleep(10 * 1000);
}
catch (InterruptedException e)
{
//Ignore it
}
// Change back to DEFAULT CURSOR
JOptionPane.showMessageDialog(m_root, "Done waiting");
m_root.setCursor(Cursor.getDefaultCursor());
current_active = false;
}
}
// create and setup the window.
public static void createAndShowGUI()
{
WaitCursor frame = new WaitCursor();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
createAndShowGUI();
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
});
}
}

Categories