I am using Java Swing to create a JDialog, and i am trying to create a Show/Hide details button to show/hide a report at the bottom of this JDialog.
It works fine for me, but i want to do this with time, to add a small animation effect while showing/hiding the report, i have used TimerTask but it's just showing the report directly without any slow motion ... Here's my current code :
private void showHideDetailsButtonActionPerformed() {
final MyDialog myDialog = this;
int fullHeight = this.getHeight();
int smallHeight = this.getHeight()/2 - 4;
this.setSize( this.getWidth(), smallHeight ); // By default hide the report.
if( this.getHeight() == smallHeight ) { // Show details.
new Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
while( myDialog.getHeight() < fullHeight ) {
myDialog.setSize( myDialog.getWidth(), myDialog.getHeight() + 1 );
System.out.println( myDialog.getHeight() );
}
}
},
800
);
}
}
Use javax.swing.Timer, not java.util.Timer... or use Trident.
Trying calling myDialog.repaint() after setting the size in the TimerTask's run() method.
Related
When attempting to click on an item in a submenu, it is natural to quickly draw your mouse across the menu items below it. Both Windows and Mac natively handle this by putting a small delay before the a menu is opened. Swing JMenus do not handle this, and the menu the mouse briefly hovers over would be opened before the mouse reaches the intended menu item.
For example, in the image below, if I tried to select Item 3, but in the process my mouse briefly slid across Menu 2, the Menu 1 submenu would disappear before I got to it.
Does anyone have any tips or suggestions for getting around this? My idea was to define a custom MenuUI that added a timer to its mouse handler.
Here is some simple example code that illustrates my problem:
public class Thing extends JFrame {
public Thing()
{
super();
this.setSize(new Dimension(500, 500));
final JPopupMenu pMenu = new JPopupMenu();
for (int i = 0; i < 5; i++)
{
JMenu menu = new JMenu("Menu " + i);
pMenu.add(menu);
for (int j = 0; j < 10; j++)
{
menu.add(new JMenuItem("Item " + j));
}
}
this.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
pMenu.show(Thing.this, e.getX(), e.getY());
}
});
}
public static void main(String[] args)
{
Thing t = new Thing();
t.setVisible(true);
}
}
Call setDelay(delay) on your menu variable, where the delay parameter is the amount of milliseconds to wait for the menu to show, as an int.
This following line of code will set the delay to 1 second, so the user has to mouseover the menu item "Menu n" 1 second, before the submenu is displayed: menu.setDelay(1000);
Here's a snippet of the edited code:
for (int i = 0; i < 5; i++)
{
JMenu menu = new JMenu("Menu " + i);
pMenu.add(menu);
for (int j = 0; j < 10; j++)
{
menu.add(new JMenuItem("Item " + j));
}
menu.setDelay(1000);
}
I came up with a very hacky solution.
I made a UI class that extends BasicMenuUI. I override the createMouseInputListener method to return a custom MouseInputListener instead of the private handler object inside BasicMenuUI.
I then got the code for the MouseInputListener implementation in handler from GrepCode[1], and copied it into my custom listener. I made one change, putting a timer in mouseEntered. My final code for mouseEntered looks like this:
public void mouseEntered(MouseEvent e) {
timer.schedule(new TimerTask() {
#Override
public void run() {
if (menuItem.isShowing())
{
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point menuLoc = menuItem.getLocationOnScreen();
if (mouseLoc.x >= menuLoc.x && mouseLoc.x <= menuLoc.x + menuItem.getWidth() &&
mouseLoc.y >= menuLoc.y && mouseLoc.y <= menuLoc.y + menuItem.getHeight())
{
originalMouseEnteredStuff();
}
}
}
}, 100);
}
Before calling the the original code that was in mouseEntered, I check to make sure the mouse is still within this menu's area. I don't want all the menus my mouse brushes over to pop up after 100 ms.
Please let me know if anyone has discovered a better solution for this.
[1] http://www.grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/7-b147/javax/swing/plaf/basic/BasicMenuUI.java/?v=source
Thank you very much, you saved my day! The solution works as expected but I recommend using the Swing timer to ensure the code is executed by the EDT.
Additionally you should temporary set the menus delay to zero before calling the original stuff. Otherwise the user has to wait twice the delay time.
#Override
public void mouseEntered(MouseEvent e) {
if (menu.isTopLevelMenu() || menu.getDelay() == 0) {
originalMouseEnteredStuff(e);
} else {
final javax.swing.Timer timer = new javax.swing.Timer(menu.getDelay(), new DelayedMouseEnteredAction(e));
timer.setRepeats(false);
timer.start();
}
}
class DelayedMouseEnteredAction implements ActionListener
{
private final MouseEvent mouseEnteredEvent;
private DelayedMouseEnteredAction(MouseEvent mouseEnteredEvent) {
this.mouseEnteredEvent = mouseEnteredEvent;
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
if (menu.isShowing()) {
final Point mouseLocationOnScreen = MouseInfo.getPointerInfo().getLocation();
final Rectangle menuBoundsOnScreen = new Rectangle(menu.getLocationOnScreen(), menu.getSize());
if (menuBoundsOnScreen.contains(mouseLocationOnScreen)) {
/*
* forward the mouse event only if the mouse cursor is yet
* located in the menus area.
*/
int menuDelay = menu.getDelay();
try {
/*
* Temporary remove the delay. Otherwise the delegate would wait the
* delay a second time e.g. before highlighting the menu item.
*/
menu.setDelay(0);
originalMouseEnteredStuff(mouseEnteredEvent);
} finally {
// reset the delay
menu.setDelay(menuDelay);
}
}
}
}
}
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.
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 ();
}
Ok, I have the following code.
public class MyProgressBar extends JPanel implements MyData, Serializable {
/**
*
*/
public static final int MAX = 10000;
public static final int WIDTH = 400;
public static final int HEIGHT = 75;
private JProgressBar MyBar = new JProgressBar( SwingConstants.HORIZONTAL, 0, MAX );
private JFrame MyFrame = new JFrame();
private int MyValue = 0;
private Thread MyThread = new Thread( new ProgressThread() );
public MyProgressBar() {
add(MyBar);
int x = ( MyData.SCREEN.width / 2 ) - ( WIDTH / 2);
int y = ( MyData.SCREEN.height / 2 ) - ( HEIGHT / 2);
this.setBounds( x, y, WIDTH, HEIGHT );
MyFrame.setBounds( x, y, WIDTH, HEIGHT );
MyFrame.setUndecorated(true);
MyFrame.getContentPane().setSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setMinimumSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setVisible(false);
MyFrame.getContentPane().setLayout(null);
MyBar.setStringPainted( true );
MyBar.setBorderPainted( true );
MyBar.setValue( 0 );
MyBar.setBounds( 0, 0, WIDTH, HEIGHT );
MyFrame.add( MyBar );
MyFrame.pack();
MyFrame.repaint();
}
public void MyUpdateBar() {
MyBar.setValue( MyValue );
MyBar.repaint();
MyFrame.repaint();
this.repaint();
//dbug.Message( "MYPROGRESSBAR", "MyUpdateBar", "Value is %3.2f %d", MyBar.getPercentComplete(), MyValue );
}
public void MySetValue( int percent ) {
MyValue = (int)( MAX * ( (double)percent / 100.0 ) );
MyUpdateBar();
//dbug.Message( "MYPROGRESSBAR", "MySetValue", "Value is %3.2f %d percent was %d", MyBar.getPercentComplete(), MyValue, percent );
}
public void CreateAndShow () {
MyFrame.setVisible(true);
MyThread.start();
}
public void HideAndClear () {
MyThread.stop();
//frame.setVisible(false);
}
class ProgressThread implements Runnable {
public void run() {
EventQueue.invokeLater(new Runnable() {
public void run() {
while( MyValue < MyBar.getMaximum() ) {
MyBar.setValue( MyValue );
MyBar.repaint();
MyFrame.repaint();
dbug.Message( "MYPROGRESSBAR", "THREAD", "Value is %3.2f %d", MyBar.getPercentComplete(), MyValue );
}
}
});
}
}
}
As you can see, I have created a class that I want to have show the progress. What happens is I instantiate the class. Load my XML file, then as I am parsing data, I am calling to update the MyValue which I see when I let my dbug messages come out. However, the bar itself does not even show until it is 100% complete. I have read about threading and following someone else's example and if I left it as his example it worked. If I made a few tweaks (changing a loop in the thread to populate the setvalue of the progress bar to read a value) it does not even show until it is 100.
What did I do wrong?
Thanks!
You thread executes SwingUtilities.invokeLater. You're effectively running on Swing's Event Dispatch Thread. Not sure what are you trying to achieve. But it looks like you are blocking EDT and your while loop is not updated as MySetValue is not executed.
Consider using SwingWorker for lengthy operations. How to Use Progress Bars demonstrates use of SwingWorker with JProgressBar.
Make sure you call setValue method from the Event Dispatch Thread. You can use SwingUtilities.invokeLater for that. Read more about Threads and Swing.
Consider this simplified sample:
public static void main(String[] arguments) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
final JProgressBar bar = new JProgressBar(0, 100);
Thread t = new Thread(){
public void run(){
for(int i = 0 ; i < 100 ; i++){
final int percent = i;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
bar.setValue(percent);
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
};
frame.add(bar);
frame.pack();
frame.setVisible(true);
t.start();
}
The problem is that you use a loop in the EDT that updates the progress. Until that loop exits, the EDT cannot dispatch events (like repaint, revalidate, invokeLater, mouse events, key events, etc...) preventing it from refreshing the progress bar.
You should try to find a way to let the EDT dispatch its events between each update of the progress bar. Ideally, you move your "work" outside the EDT with a SwingWorker, and meanwhile the progressbar get updated through property change listeners in the EDT.
For your information, in Java, methods and variables starts with a lower case letter. Your code is really hard to read for others.
So, I tried to follow the tutorial and here is where I am at.
Ok, I have tried following tutorials but I keep getting lost somewhere. What I need is a class that creates and displays a progress bar (JProgressBar) that I can set the value of as I iterate over data loaded from a file and place into the database memory. My problems come that every example I have found has some kind of counter that fills the progress bar and executes from a "main" function. Every time I alter that tutorial to be a class that I can call at will and display the bar, I do not get the bar showing (ie the frame comes up but the bar does not even look like it is added to the frame until after the iteration is done). I have tried using SwingUtilities.invokeLater and SwingWorker (lastest attempt at class below) all having the same issue. To make matters worse, I can do a dbug.myMessage (basically sends to System.out) and see a message that shows that the bar is changing in memory just not showing. I am obviously missing something probably simple but I can't think of what it is.
Oh, one other thing, if I leave the tutorial as is (http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/uiswing/examples/components/ProgressBarDemo2Project/src/components/ProgressBarDemo2.java) and just change the main to a createAndShow method, it works but of course it does not do what I need it to do.
I did post another question about this but have altered the class so much I thought it best to post a new question.
So, here is my altered code that does not seem to work:
public class MyProgressBar extends JPanel implements PropertyChangeListener,
MyData,
Serializable {
/**
*
*/
private static final long serialVersionUID = -1632492668549544408L;
private MyDebug dbug = new MyDebug( MyData.MYDEBUGCHECK.MYPROGRESSBAR.getOn() );
public static final int MAX = 100;
public static final int WIDTH = 400;
public static final int HEIGHT = 75;
private JProgressBar myBar = new JProgressBar( SwingConstants.HORIZONTAL, 0, MAX );
private JFrame myFrame = new JFrame();
public Task task;
class Task extends SwingWorker<Void, Void> {
public int myValue = 0;
#Override
public Void doInBackground() {
//Initialize progress property.
setProgress(0);
while (myValue < 100) {
//Make random progress.
//myValue += random.nextInt(10);
setProgress( Math.min( myValue, 100 ) );
dbug.myMessage( "MYPROGRESSBAR", "doInBackground", "Value is %3.2f %d", myBar.getPercentComplete(), myValue );
myBar.repaint();
}
return null;
}
public void done() {
}
public void mySetValue( int percent ) {
myValue = (int)( MAX * ( (double)percent / 100.0 ) );
dbug.myMessage( "MYPROGRESSBAR", "mySetValue", "Value is %3.2f %d percent was %d", myBar.getPercentComplete(), myValue, percent );
}
}
public MyProgressBar() {
add(myBar);
int x = ( MyData.SCREEN.width / 2 ) - ( WIDTH / 2);
int y = ( MyData.SCREEN.height / 2 ) - ( HEIGHT / 2);
this.setBounds( x, y, WIDTH, HEIGHT );
myFrame.setBounds( x, y, WIDTH, HEIGHT );
myFrame.setUndecorated(true);
myFrame.getContentPane().setSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setMinimumSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setVisible(false);
myFrame.getContentPane().setLayout(null);
myFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
myBar.setStringPainted( true );
myBar.setBorderPainted( true );
myBar.setValue( 0 );
myBar.setBounds( 0, 0, WIDTH, HEIGHT );
myBar.addPropertyChangeListener( this );
myFrame.add( myBar );
//Create and set up the content pane.
//JComponent newContentPane = new MyProgressBar();
JComponent newContentPane = myBar;
newContentPane.setOpaque(true); //content panes must be opaque
myFrame.setContentPane(newContentPane);
myFrame.pack();
}
public void createAndShow () {
//Display the window.
myFrame.setVisible(true);
myFrame.repaint();
}
public void hideAndClear () {
//myFrame.setVisible(false);
}
#Override
public void propertyChange(PropertyChangeEvent args) {
dbug.myMessage( "MYPROGRESSBAR", "propertyChange", "Value is %s", args.getPropertyName() );
if ( "progress" == args.getPropertyName() ) {
int progress = (Integer) args.getNewValue();
//myBar.setValue(progress);
}
}
public void start () {
//Instances of javax.swing.SwingWorker are not reusuable, so
//we create new instances as needed.
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
}
}
The below snippet updates the progress bar while in progress
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressBar.setValue((int)percentage);
//below code to update progress bar while running on thread
progressBar.update(progressBar.getGraphics());}
});
Dynamic progress update of the progress bar is achieved via the below code:
int progress = Math.round(((float)finished/(float)(total)) * 100);
uploadPrgressBar.setIndeterminate(progress == 0);
uploadPrgressBar.setValue(progress);
uploadPrgressBar.update(uploadPrgressBar.getGraphics());
Call the method containing the above code in the loop(code logic) and it will dynamically update progress bar after each iteration
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