paintComponent not being called at the right time - java

I'm trying to write an app that goes something like this:
- Display a dialog
- When user clicks OK, close dialog, go to main app
Here are the relevant code snippets:
public class Owari extends JPanel implements ActionListener, MouseListener, Runnable {
// FIELDS
JFrame frame;
JTextField IP;
String IPAddress;
static final int SERVER_MODE = 0;
static final int CLIENT_MODE = 1;
int mode;
OwariBoard board;
public static void main( String[] args ) {
SwingUtilities.invokeLater( new Owari() );
}
Owari() {
setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
board = new OwariBoard();
}
void main() {
this.addMouseListener( this );
frame.dispose();
frame = new JFrame( "Owari" );
frame.setContentPane( this );
frame.pack();
frame.setVisible(true);
if ( mode == SERVER_MODE ) {
server();
}
if ( mode == CLIENT_MODE ) {
client();
}
}
public void run() {
frame = new JFrame( "Owari" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel init = new JPanel( new GridBagLayout() );
frame.setContentPane( init );
add some components to the init panel including a button with
this as its actionListener and OK as its command.
frame.pack();
frame.setVisible( true );
}
public void actionPerformed( ActionEvent e ) {
if ( e.getActionCommand().equals( "Client" ) ) {
mode = CLIENT_MODE;
IP.setVisible( true );
}
else if ( e.getActionCommand().equals( "Server" ) ) {
mode = SERVER_MODE;
IP.setVisible( false );
}
else {
IPAddress = IP.getText();
main();
}
}
public void paintComponent( Graphics g ) {
super.paintComponent( g );
System.out.println( "painting" );
do some paintin
}
void server() {
frame.setTitle( "Owari Server" );
try {
server = new ServerSocket( 666 );
socket = server.accept();
initIO();
} catch ( IOException e ) {}
yourTurn = true;
System.out.println( "Got to end of server()" ); // At this point, the window
DOES get painted
What happens is the following:
The initial dialog displays:
I click the OK button.
The main window gets resized to the preferred size of the main app but it doesn't get painted, it's just transparent (shown here with this page as the background, heh):
http://imgur.com/6Ssij.jpg
I can tell the paintComponent method hasn't been called because "painting" isn't printed to the console.
However, "got to this point in the program" DOES get printed, so the program isn't hanging, it's just not calling paintComponent.
Then when I launch a client and connect, the app finally gets painted, and "painting" and "got a client" get printed to the console.
Also later on in the app, calls to repaint() are delayed (ie paintComponent is actually called later in the program than when the call to repaint() is made).
I also tried replacing the initial dialog using sthing along the lines of
public void main
frame.getRootPane.removeAll()
frame.setContentPane(this)
frame.getRootPane().revalidate()
frame.pack()
Exact same result.
tl;dr paintcomponent isn't being called when i want it to, what do?
Bumping for some more info: the call to repaint() is done before the call to sever.accept() So why does it not repaint() before hanging at the server.accept() call?

openasocketandwaitforaclient
Your code is executing in the Event Dispatch Thread so the blocking socket is preventing the GUI from repainting itself.
YOu need to use a separate Thread for the socket. Read the section from the Swing tutorial on Concurrency for an explanation and a solution.

your code seems to work so, maybe you should try to invoke the repaint() methode of you frame after resizing this frame.
Anhuin

Related

Difficulty in using addWindowListener

I am trying to implement a listener to detect the closing of a window. My main class is extending other classes so I am unable to extend JFrame which is not allowing me to use addWindowListener.
I have tried the code below but it is not working.
public class PowerPanel extends AnotherPanel implements ActionListener, PropertyChangeListener {
// logic …
this.addWindowListener(new WindowAdapter() {
public void windowClosing (WindowEvent e ) {
// do things
}
});
}
The error I am getting is indicating that addWindowListener is "undefined for the type PowerPanel."
You can't add window listener to your panel - you need to add it into the java.awt.Window instance which in your case is a JFrame.
If it is hard for you to pass your JFrame instance into the panel - you can access it in a different way:
public class SamplePanel extends JPanel
{
/**
* Simple panel example that has no idea about it's parent window.
*/
public SamplePanel ()
{
super ();
// Your custom window listener
final WindowAdapter windowAdapter = new WindowAdapter ()
{
#Override
public void windowClosing ( final WindowEvent e )
{
System.out.println ( "Window is closing" );
}
};
// Ancestor availability listener
// It will make sure that whenever component appears on some visible window
// and is displayable container - we will add our window listener to it
addAncestorListener ( new AncestorAdapter ()
{
/**
* Saved window to remove listener from later on.
*/
private Window window = null;
#Override
public void ancestorAdded ( final AncestorEvent event )
{
// Component appeared on some window, we can add our listener now
addListener ();
}
#Override
public void ancestorMoved ( final AncestorEvent event )
{
// Re-adding listener to potentially new parent window
removeListener ();
addListener ();
}
#Override
public void ancestorRemoved ( final AncestorEvent event )
{
// Component was hidden or removed, we need to remove our listener
removeListener ();
}
/**
* Adds window listener.
*/
protected void addListener ()
{
window = SwingUtilities.getWindowAncestor ( SamplePanel.this );
if ( window != null )
{
window.addWindowListener ( windowAdapter );
}
}
/**
* Removes window listener.
*/
protected void removeListener ()
{
if ( window != null )
{
window.removeWindowListener ( windowAdapter );
window = null;
}
}
} );
}
/**
* Sample main method for runing the example.
*
* #param args arguments
*/
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
#Override
public void run ()
{
final JFrame frame = new JFrame ();
final SamplePanel panel = new SamplePanel ();
panel.add ( new JLabel ( "Sample panel content" ) );
frame.add ( panel );
frame.pack ();
frame.setDefaultCloseOperation ( JFrame.DISPOSE_ON_CLOSE );
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
} );
}
}
With this code your panel will automatically register window listener on it's parent window whenever it is displayed. It will also automatically remove the listener once panel is hidden or removed from the window, so potentially this is the best solution you can have.
Be careful though, if you will use that panel multiple times on the same window - all of the instances will add their own listener and will all perform on-close action separately. If you want to perform your on-close operation just once per window/frame/dialog instance - you need to add the listener on the window itself, preferably where it is created not to make it obscure.
You might also notice that in my example there is actually a method that retrieves panel's parent window directly:
window = SwingUtilities.getWindowAncestor ( SamplePanel.this );
Although you can't use it in your panel directly, because at the moment when panel is created it is not yet added into any container, let alone visible window. That is why the solution with ancestor listener that i've shown is required.

repaint is not calling paintComponent

Repaint is not calling PaintComponent.
I tried to call it from another method of the Try class too but it did not work out.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class Try extends JPanel {
int i =0;
#Override
public void paintComponent(Graphics g){
//super.paintComponent(g);
System.out.println("hey");
}
public static void main(String[] args) {
JFrame f=new JFrame();
Try t = new Try();
f.setSize(500,600);//400 width and 500 height
Container contentPane = f.getContentPane();
contentPane.add(new PaintComponent());
f.setVisible(true);//making the frame visible
while(true){
t.repaint();
}
}
}
There are multiple issues some of which are mentioned by #trashgod in comments.
You are calling repaint on the instance which you did NOT add to the content pane - you have a different one there (actually, you have something completely different there - new PaintComponent()).
Do not remove super.paintComponent ( g ); unless you clean up the area on your own (pretty much fill the whole component background if it is opaque), otherwise you will get visual glitches on such components upon repaint.
You are spamming repaint operations which is extremely bad, make at least some delay between the repaints giving Swing time to perform the repaints. The best case is if you repaint component only when it actually will display something different visually. If you need to update the view all the time - at least limit it to 30-60 frames (repaints) per second. Also, some internal Swing optimizations might "eat" some of the repaint calls, so expect that you might not see as many paintComponent calls as a number of repaints you call on the component.
You are working with Swing components outside of Event Dispatch Thread (shortly EDT) which might cause issues. Make sure you always use it to create Swing components and call any methods on them. SwingUtilities helps with that.
Any heavy operations that take a long time (or unknown time) to be completed should be executed outside of EDT, otherwise, your UI will simply hang while you are waiting for that operation to complete because all UI updates are performed on EDT and not anywhere else.
Considering all I said above, this is how your example should look like:
public class Try extends JPanel
{
#Override
public void paintComponent ( final Graphics g )
{
super.paintComponent ( g );
final Graphics2D g2d = ( Graphics2D ) g;
g2d.drawString ( Long.toString ( System.currentTimeMillis () ), 25, 35 );
System.out.println ( "repainted" );
}
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
#Override
public void run ()
{
final JFrame f = new JFrame ();
final Try t = new Try ();
f.getContentPane ().add ( t );
f.setSize ( 500, 600 );
f.setVisible ( true );
new Thread ( new Runnable ()
{
#Override
public void run ()
{
try
{
while ( true )
{
t.repaint ();
Thread.sleep ( 25 );
}
}
catch ( final InterruptedException e )
{
//
}
}
} ).start ();
}
} );
}
}
Hope that clarifies it a bit for you.
There is also SwingWorker class that helps to perform long-running tasks in Swing, but I didn't use it here to keep the example as simple as possible.
Also a side note - you do not need to call repaint () within EDT because it sends the repaint request to EDT on its own, so that method is safe to use on any thread (like I do in the example).

make button unresponsive for a while java [duplicate]

This question already has an answer here:
Using a Swing Timer to hide a notification temporarily
(1 answer)
Closed 9 years ago.
I was trying a simple code in java for test, just a button when u click it it sleeps for 5 seconds, here's the handler
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
try {
System.out.println ("hiiii");
Thread.sleep (5000);
System.out.println ("bye");
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
I want this button doesn't receive any events until it finishes working (5 seconds), I tried to disable and enable it in the handler but in vain
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jButton1.setEnabled(false);
try {
System.out.println ("hiiii");
Thread.sleep (5000);
System.out.println ("bye");
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
jButton1.setEnabled(true);
}
You have to move any long-running task (in your case it is a simple sleep call) outside the EDT (Event Dispatch Thread) - that thread is used to render UI, if you block it with some operation - you block all your UI at once.
Here is a proper example how you can disable/enable button:
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* #author Mikle Garin
* #see http://stackoverflow.com/a/18590057/909085
*/
public class ButtonSleep
{
public static void main ( String[] args )
{
JFrame frame = new JFrame ( "Custom list renderer" );
final JButton button = new JButton ( "Make me sleep 5 seconds" );
button.addActionListener ( new ActionListener ()
{
#Override
public void actionPerformed ( ActionEvent e )
{
button.setEnabled ( false );
new Thread ( new Runnable ()
{
#Override
public void run ()
{
try
{
Thread.sleep ( 5000 );
}
catch ( InterruptedException e1 )
{
//
}
SwingUtilities.invokeLater ( new Runnable ()
{
#Override
public void run ()
{
button.setEnabled ( true );
}
} );
}
} ).start ();
}
} );
frame.add ( button );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
}
Yes, it seems weird, but you have to move your operations to a separate thread (doesn't really matter which thread) and you have to execute any operations with UI (in this case - enabling/disabling button) inside the EDT - that is why i call setEnabled in invokeLater call and not inside the separate thread.
And yes, all Swing component listeners event calls are executed inside EDT from the beginning, so you are already inside EDT when you start executing your code inside actionPerformed method of your action listener - that is why you block the whole UI if you call sleep there.

Java Swing - mouseMoved event is fired slowly

Currently I am experiencing issues with the mouseMoved event in Java - Swing. Briefly, I have got a JPanel and I have attached MouseMotionListener to it, in order to hide or show JscrollPane on the fly:
myPanel.addMouseMotionListener(new MousePresenter());
I have got my own class that implements MouseMotionListener interface:
public class MousePresenter implements MouseMotionListener {
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if (x>20 && x<200) {
hideScrollBar();
}
else {
showScrollBar();
}
}
}
The issue is that the mouseMoved event is not being fired often enough. Is there any related solution to this issue whilst using MouseMotionListener?
Thank you for your time.
The following seems to work just fine for me. Note that the handling of the event is rather fast:
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame( "TestFrame" );
JPanel content = new JPanel( new BorderLayout() );
final JLabel mousePosition = new JLabel( "Unknown" );
content.add( mousePosition, BorderLayout.NORTH );
content.addMouseMotionListener( new MouseMotionAdapter() {
#Override
public void mouseMoved( MouseEvent e ) {
mousePosition.setText( "X: " + e.getX() + " Y: " + e.getY() );
}
} );
frame.setContentPane( content );
frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
frame.pack();
frame.setVisible( true );
}
} );
}
That might not be the case for your hideScrollBar method
A mouse moved event is inherently slowly since it's fired on every pixel change.
The only thing you can do to optimize the whole issue is to optimize what you do inside the callback handler. In your case you do have
if (something)
doA();
else
doB();
This means that in any case you are either trying to show or to hide the scrollbar even when it's already shown or hidden. What you can do is:
if (scrollBarIsVisible && x>20 && x<200) {
hideScrollBar();
scrollBarIsVisible = false;
}
else if (!scrollBarIsVisible) {
showScrollBar();
scrollBarIsVisible = true;
}
So that you only modify the visibility of the element (which can be a heavy operation since it may require to relayout things) when switching from inside the bounds to outside and viceversa. This should lower the computational operations by a lot.
If you all your code is being executed in the Event Dispatch thread it could be causing problems. Have a look at this trail and try to put any code that does a lot of work in a SwingWorker thread.
Your code is not very well optimized. As it is, it will always call either the show or hide Scrollbar methods. You should probably modify it such as it hides it only if visible and it displays it only if hidden.
Problem solved. There was certain performance issue in my app that caused such delays.
Thank you for your effort and piece of information and advice you provided.

Java Swing - Flashing icon not showing up

I am trying to flash the icon to the user using a GlassPane. I am running a javax.swing.Timer which basically performs this:
for (int i = 0; i < 3; i++) {
frame.getGlassPane().setVisible(true);
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
//To change body of catch statement use File | Settings | File Templates.
e1.printStackTrace();
}
frame.getGlassPane().setVisible(false);
}
Unfortunatly, if I sleep the EDT (current thread within the timer), the icon does not show, as in the paintComponent method did not manage to get invoked fully before the thread went to sleep. Therefore, when the next instruction kicks in, the glass pane is hidden, and, as a result, the icon is never shown. Is there a way to achieve what I want using this (similiar) approach?
You could use a javax.swing.Timer
public FlashTimer() {
javax.swing.Timer flashTimer = new javax.swing.Timer(500, new FlashHandler());
flashTimer.setCoalesce(true);
flashTimer.setRepeats(true);
flashTimer.setInitialDelay(0);
}
public class FlashHandler implements ActionListener {
private int counter;
#Override
public void actionPerformed(ActionEvent ae) {
countrol.setVisible(counter % 2 == 0);
counter++;
if (counter > 3) {
((Timer)ae.getSource()).stop();
}
}
}
It should be obvious - use a separate Thread and do the "blinking logic" there but modify the UI in EDT. Here is a simple example (should be enough to understand the idea):
public static void main ( String[] args )
{
JFrame frame = new JFrame ();
final JLabel label = new JLabel ( "X" );
label.setBorder ( BorderFactory.createEmptyBorder ( 90, 90, 90, 90 ) );
frame.add ( label );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
new Thread ( new Runnable ()
{
public void run ()
{
for ( int i = 0; i < 15; i++ )
{
try
{
setVisible ( false );
Thread.sleep ( 500 );
setVisible ( true );
Thread.sleep ( 500 );
}
catch ( InterruptedException e1 )
{
//
}
}
}
private void setVisible ( final boolean visible )
{
SwingUtilities.invokeLater ( new Runnable ()
{
public void run ()
{
label.setVisible ( visible );
}
} );
}
} ).start ();
}

Categories

Resources