Java Swing - mouseMoved event is fired slowly - java

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.

Related

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).

Put focus on a JTextField in JDialog when latter made visible

I have a subclass of JDialog... the overridden setVisible method looks like this:
public void setVisible( boolean visible ){
super.setVisible( visible );
if( visible ){
inputJTF.requestFocus();
}
}
In fact when I display the JDialog the focus is on the JTF... but the latter happens also to be the "first" component (NORTH panel) in the JDialog, so that's not surprising.
But my testing code is telling me other things:
EventQueue.invokeAndWait(new Runnable() {
#Override
public void run() {
app.mainFrame.searchDlg.setVisible( true );
// all 3 of these asserts fail...
assertTrue( app.mainFrame.searchDlg.inputJTF.hasFocus() );
Component focusOwner = app.mainFrame.searchDlg.getFocusOwner();
assertFalse( focusOwner == null );
assertTrue( String.format( "# focus owner %s", focusOwner.getClass()), focusOwner == app.mainFrame.searchDlg.inputJTF );
}
});
... so in fact I get told that the "focus owner" is null... and the JTF doesn't have focus. Can anyone explain what's going on?
Most dialogs are modal, which means the statement after the setVisible(true) statement is not executed until the dialog is closed.
By default focus will go to the first component on the dialog.
If for some reason you need focus on a different component then check out Dialog Focus for a solution that allow you to control which component get focus.
This solution uses an AncestorListener to place focus on a component once the component is displayed on a visible dialog/frame.
Aha... this turns out to be one of those "Gotcha" things with JUnit and Java GUI.
The test in my code fails ... but the following code doesn't:
EventQueue.invokeAndWait(new Runnable() {
#Override
public void run() {
searchDlg.setVisible( true );
assertTrue( searchDlg.queryString == null );
}
});
Robot robot = new Robot();
robot.delay( 10 );
EventQueue.invokeAndWait(new Runnable() {
#Override
public void run() {
// now passes
assertTrue( app.mainFrame.searchDlg.inputJTF.hasFocus() );
}
});
... if the robot.delay() is reduced to 1, or absent, the failure happens.
Clearly this is to do with the finite time needed to realise the JDialog.
Rob Camick's answer is interesting, and RequestFocusListener works as intended.
However his answer doesn't actually answer the question: which was: what's going on and why does this test fail?

How can I make a jslider actually affect things as it slides?

I have set up an area where a jslider should alter the delay of some dots in a jpanel here
JSlider source = (JSlider)e.getSource();
if (source == speedSlider) {
if (source.getValueIsAdjusting()) {
GraphicsDisplay.delay += 100000;
}
}
The delay is put into affect by the following
public static boolean stop = true ;
public static long delay = 3000000 ;
public void paint ( Graphics g2 ) {
//code making dots up here...
int a;
if ( !stop ) {
for ( a=0; a<delay; a++ ) ;
moveDot ( ) ;
}
repaint();
}
I can't get the slider to do anything. And I know it has something to do with
if (source.getValueIsAdjusting()) {
GraphicsDisplay.delay += 100000;
}
The problem isn't with the slider, it's with your painting...
Basically, you are blocking the Event Dispatching Thread, preventing it from actually painting...
public void paint ( Graphics g2 ) {
// Let's ignore the fact that you haven't called super.paint here...
//code making dots up here...
int a;
if ( !stop ) {
// Block the EDT, stop all painting and event processing until this for
// exist...
for ( a=0; a<delay; a++ ) ;
moveDot ( ) ;
}
// This is a very bad idea inside any paint method...
repaint();
}
Basically, what's happening, is the RepaintManager is consolidating most of your repaint requests down to as few events as possible, in order to maintain performance. So while you "block" the EDT, you paint requests are been queued, but not processed, the repaint manager is making decisions that may also consolidate those requests down to a few events in order to maintain performance.
A better solution would be to use a Swing Timer. See How to Use Swing Timers
private javax.swing.Timer timer;
//...
timer = new Timer(delay, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
moveDot();
repaint();
}
});
timer.start();
//...
if (source.getValueIsAdjusting()) {
timer.stop();
timer.setDelay(source.getValue());
timer.start();
}
Your use of static variables is also a little scary...
ps- I forgot to mention, you should avoid overriding paint and instead use paintComponent, making sure you call super.paintComponent first...See Perfoming Custom Painting

paintComponent not being called at the right time

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

Switching on a string/implementing button actions

Full disclaimer: I'm a CS student, and this question is related to a recently assigned Java program for Object-Oriented Programming. Although we've done some console stuff, this is the first time we've worked with a GUI and Swing or Awt. We were given some code that created a window with some text and a button that rotated through different colors for the text. We were then asked to modify the program to create radio buttons for the colors instead—this was also intended to give us practice researching an API. I've already handed in my assignment and received permission from my instructor to post my code here.
What's the best way to implement button actions in Java? After some fiddling around, I created the buttons like this:
class HelloComponent3 extends JComponent
implements MouseMotionListener, ActionListener
{
int messageX = 75, messageY= 175;
String theMessage;
String redString = "red", blueString = "blue", greenString = "green";
String magentaString = "magenta", blackString = "black", resetString = "reset";
JButton resetButton;
JRadioButton redButton, blueButton, greenButton, magentaButton, blackButton;
ButtonGroup colorButtons;
public HelloComponent3(String message) {
theMessage = message;
//intialize the reset button
resetButton = new JButton("Reset");
resetButton.setActionCommand(resetString);
resetButton.addActionListener(this);
//intialize our radio buttons with actions and labels
redButton = new JRadioButton("Red");
redButton.setActionCommand(redString);
...
And added action listeners...
redButton.addActionListener(this);
blueButton.addActionListener(this);
...
A stub was already created for the actionPerformed method to give us an idea on how to use it, but since there was only a single button in the template, it wasn't clear how to implement multiple buttons. I tried switching on a String, but quickly realized that, since a String isn't a primitive type, I couldn't use it for a switch statement. I could have improvised with an if-else chain, but this is what I came up with instead. It seems far from elegant, and there must be a better way. If there is, what is it? Is there a way to switch on a string? Or choose an action in a more scaleable fashion?
public void actionPerformed(ActionEvent e){
if (e.getActionCommand().equals(resetString)) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
return;
}
if ( e.getActionCommand().equals(redString) ) {
setForeground(Color.red);
repaint();
return;
}
if ( e.getActionCommand().equals(blueString) ) {
setForeground(Color.blue);
repaint();
return;
}
if ( e.getActionCommand().equals(greenString) ) {
setForeground(Color.green);
repaint();
return;
}
if ( e.getActionCommand().equals(magentaString) ) {
setForeground(Color.magenta);
repaint();
return;
}
if ( e.getActionCommand().equals(blackString) ) {
setForeground(Color.black);
repaint();
return;
}
}
Instead of writing this:
resetButton.addActionListener(this);
You could also write this:
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
resetButtonActionPerformed(evt);
}
});
And instead of writing one big actionPerformed() for all actions, you can (and then have to) write this:
public void resetButtonActionPerformed(ActionEvent evt) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
I don't know if this is the most elegant solution, but at least you no longer have that big if construct.
Two alternate approaches:
Create a new class that implements the Action interface and has a Color field and an actionPerformed method that sets the color
Mantain a HashMap from command names to Color instances and look up the command name in the map
One decent enough approach is to declare an enum whose elements match your strings and switch on valueOf(str) (the linked example shows how to do this with a fair amount of safety).
The reason to avoid anonymous inner classes is probably because the class hasn't had that construct (yet), even though that might be the best solution.
As suggested already, you can use anonymous inner classes to implement the ActionListener interface. As an alternative, you don't have to use anonymous inner classes, but you can use a simple nested class instead:
resetButton = new JButton(new ResetAction());
redButton = new JButton(new ColorAction("Red", Color.red));
and then...
private class ResetAction extends AbstractAction {
public ResetAction() {
super("Reset");
}
public void actionPerformed(ActionEvent e) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
}
private class ResetAction extends AbstractAction {
private Color color;
public ColorAction(String title, Color color) {
super(title);
this.color = color;
}
public void actionPerformed(ActionEvent e) {
setForeground(color);
repaint();
}
}
For why this approach - or any approach involving inner classes - is better than implementing ActionListener in the outer class see "Design Patterns":
"Favor 'object composition' over 'class inheritance'." (Gang of Four 1995:20)
Choosing between anonymous inner classes and these named inner classes is a largely a matter of style, but I think this version is easier to understand, and clearer when there are lots of actions.
Ergh. Don't implement masses of unrelated interfaces in one mega class. Instead, use anoymous inner classes. They are a bit verbose, but are what you want. Use one for each event, then you wont need big if-else chain. I suggest keeping enough code within the inner class to decode the event and call methods that make sense to the target objects. Further, you can parameterise your inner classes. You will probably find you don't need to keep references to the actual widgets around.
In your example you seem to be using a JComponent as a JPanel. There's not much difference, but use JPanel for collecting a block of widgets. Further there is unlikely any need to subclass it, so don't.
So for instance:
addColorButton("Green" , Color.GREEN );
addColorButton("Red" , Color.RED );
addColorButton("Yellow", Color.YELLOW);
addColorButton("Blue" , Color.BLUE );
...
private void addColorButton(String label, Color color) {
JRadioButton button = new JRadioButton(label);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
target.setForeground(color);
target.repaint();
}
});
colorGroup.add(button);
panel.add(button);
}

Categories

Resources