cannot always load the picture on click and hover - java

I am making a launcher application, i wish to change the button depending on the action taken, right now i want to change the button when the following actions happen, either the player hovers over the image, clicks on the image, releases the image click or exits the hover. The issue i have is that it doesn't always load correctly, which is odd.
package LostStory;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
#SuppressWarnings("serial")
public class LaunchPanel extends JComponent {
Image image;
JButton play;
SystemTray tray;
ImageIcon icon = new ImageIcon("res/images/buttons/playNonHover.png");
ImageIcon iconHover = new ImageIcon("res/images/buttons/playHover.png");
ImageIcon iconClick = new ImageIcon("res/images/buttons/playClick.png");
public LaunchPanel(Image img) {
this.image = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
init();
}
public void init() {
play = new JButton("Play Lost Story", icon);
play.setBounds(85, 210 - 75, getWidth() - 165, 50);
play.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
Runtime run = Runtime.getRuntime();
try {
run.exec("notepad");
if (SystemTray.isSupported()) {
tray = SystemTray.getSystemTray();
play.setIcon(play.getIcon());
// Main.getClient().getJFrame().setVisible(false);
// tray.add(img);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
play.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
} // no use
#Override
public void mouseEntered(MouseEvent e) {
if (iconHover.getImageLoadStatus() == 8) {
play.setIcon(iconHover);
play.setIcon(play.getIcon());
System.out.println("Entered");
}
System.out.println(iconHover.getImageLoadStatus());
} // use
#Override
public void mouseExited(MouseEvent arg0) {
if (icon.getImageLoadStatus() == 8) {
play.setIcon(icon);
play.setIcon(play.getIcon());
System.out.println("Exited");
}
System.out.println(icon.getImageLoadStatus());
} // use
#Override
public void mousePressed(MouseEvent arg0) {
if (iconClick.getImageLoadStatus() == 8) {
play.setIcon(iconClick);
play.setIcon(play.getIcon());
System.out.println("Pressed");
}
System.out.println(iconClick.getImageLoadStatus());
} // use
#Override
public void mouseReleased(MouseEvent arg0) {
if (icon.getImageLoadStatus() == 8) {
play.setIcon(icon);
play.setIcon(play.getIcon());
System.out.println("Released");
}
System.out.println(icon.getImageLoadStatus());
} // use
});
add(play);
}
}
The first button always loads (because it is the default one) it returned an '8' when i tried using the IconImage.getImageLoadStatus() so i expected that to be '8' if it was succesfully loaded. (This is not the case though, it will always return 8 as far as i know).
So TL;DR: How do i make sure the ImageIcons are always loaded and will apply correctly?

Don't try to manipulate the icon using the setIcon() method.
A JButton has methods to set the icon for various states setRolloverIcon, setPressedIcon, etc.
Read the API for other properties.
Also, a painting method is for painting only. Never create components from a painting method. Get rid of the init() method from your paintComponent() method.
Components should be created and added to the panel in the constructor of the panel.

Related

Java wait code until JFrame keyPressed

I'm using a JFrame and I wanted to display an image and pause the code until the user presses ANY key. After that key being pressed the image would close and the code would continue running.
What I did:
Created a flag
final boolean[] flag = {true};
Added a addKeyListener to the JFrame object that would change the flag
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
frame.setVisible(false);
frame.dispose();
flag[0] = false;
}
});
Wait loop until flagged
while (flag[0]){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
This is working, but I understand that it is a bit resourceful.
Is there any other way of making the wait loop? Is there any listener of the listener?
2nd try, using CountDownLatch:
Set the latch
final CountDownLatch latch = new CountDownLatch(1);
CountDown
for (JFrame frame : framesList) {
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
frame.setVisible(false);
frame.dispose();
latch.countDown();
}
});
Wait
latch.await();
So, you want to display an image and have the execution stop until the window is closed. This just screams modal dialog to me. A modal dialog will stop the code execution from where it is made visible, it will do it in such away so as not to block the Event Dispatching Thread and make your entire problem come to a screaming halt and hang the program. See How to use dialogs for more details...
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
BufferedImage img = ImageIO.read(...);
ImageShower.show(null, img);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public static class ImageShower extends JPanel {
private JLabel label = new JLabel();
public ImageShower() {
setLayout(new BorderLayout());
add(label);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
am.put("close", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
Window window = SwingUtilities.windowForComponent(ImageShower.this);
if (window != null) {
window.dispose();
}
}
});
}
public void setImage(Image img) {
label.setIcon(new ImageIcon(img));
}
public static void show(Component owner, Image img) {
Window parent = null;
if (owner != null) {
parent = SwingUtilities.windowForComponent(owner);
}
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
Window window = SwingUtilities.windowForComponent(btn);
if (window != null) {
window.dispose();
}
}
});
JDialog dialog = new JDialog(parent, Dialog.ModalityType.APPLICATION_MODAL);
ImageShower shower = new ImageShower();
shower.setImage(img);
dialog.add(shower);
dialog.add(close, BorderLayout.SOUTH);
dialog.getRootPane().setDefaultButton(close);
dialog.pack();
dialog.setLocationRelativeTo(owner);
dialog.setVisible(true);
}
}
}
"But wait, may images are large and take time to load and I don't want to freeze the UI while the load"...
Okay, for that, I'd look towards using a SwingWorker, which can load the image in the background but which provides simple methods for ensuring the the image is displayed within the context of the EDT properly...
public class ImageLoadAndShow extends SwingWorker<Void, Image> {
#Override
protected Void doInBackground() throws Exception {
BufferedImage img = ImageIO.read(...);
publish(img);
return null;
}
#Override
protected void process(List<Image> chunks) {
Image img = chunks.get(chunks.size() - 1);
ImageShower.show(null, img);
}
}
Not, if the image fails to load, you won't know about it, as the doInBackground method will pass the Exception out of the method. You'd need to use a combination of a PropertyChangeListener and the SwingWorkers get method to trap it, just remember, get is blocking, so calling it inside the context of the EDT will block until the worker completes
"But I need to carry out other operations when the dialog is closed"
There are a few ways you might be able to achieve this, depending on what it is you want to do, for this example, I've stuck with the SwingWorker, because it was easy to copy and paste the basic structure, but you could use a Runnable wrapped in a Thread
public class ImageLoadShowAndWait extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
BufferedImage img = ImageIO.read(...);
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
ImageShower.show(null, img);
}
});
return null;
}
}
Now, if none of that does what you want...then I'd like to know what it is you're actually doing :P, have a look at Foxtrot which provides an API which allows you to execute code asynchronisly within the EDT without blocking it (entirly), but which will stop the code execution at the point it's called until it completes
The thing is that I wanted it to close the JFrame when ANY key is pressed
KeyListener is going to give you issues, maybe not today, maybe not tomorrow, but it will blow up in your face eventually. The example I've provide binds the Escape key to dispose of the window. It also makes the "Close" button the default button, which provides Space and/or Enter keys as well and a nice visual queue to the user.
If you want to use KeyListener, that's up to you, but your core problem doesn't seem to revolve around it, but the ability to display a window and pause the code execution till it's closed

New JFrame is opening in disabled state when opened on a button click

I am stuck with a very unusual situation. I have a class "ScreenSizeSelector" which has a method 'getSelectedScreenSize'. The method's work is to create a UI, user drags the UI and method return back size of window.
Now I am calling the method of class in following ways:
A simple class (non GUI)
On the button click from a JFrame
In the first case, it is working perfectly fine (i.e. size selector window opens, user drags it, resize it and it is giving back window coordinates) but in second case, window opens but in disabled mode, user is not able to perform any operation on the window, not even able to close the window.
Here is the code I am using
ScreenSizeSelector class :
package screenrecorder;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.Border;
class ScreenSizeSelector {
private JFrame sizeSelectorWindow;
private JButton btnOk;
private Border emptyBorder;
private Rectangle screenArea = null;
private static Object lock = new Object();
public Rectangle getSelectedScreenSize(){
screenSizeSelectorUI();
Thread t = new Thread() {
public void run() {
synchronized(lock) {
while (sizeSelectorWindow.isVisible())
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return screenArea;
}
public void screenSizeSelectorUI() {
emptyBorder = BorderFactory.createEmptyBorder();
sizeSelectorWindow = new JFrame("Select screen area");
btnOk = new JButton("Start");
sizeSelectorWindow.setUndecorated(true);
sizeSelectorWindow.getRootPane().setWindowDecorationStyle(3);
sizeSelectorWindow.setBackground( new Color(0, 0, 0, 0) );
sizeSelectorWindow.setSize(400,400);
sizeSelectorWindow.addWindowListener(new WindowEventHandler());
sizeSelectorWindow.setAlwaysOnTop(true);
sizeSelectorWindow.setLocationRelativeTo(null);
btnOk.setToolTipText("Click this button after deciding the screen area");
btnOk.addActionListener(new ButtonEventHandler());
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
buttonPanel.setBackground(new Color(0,0,0,0));
buttonPanel.add(btnOk);
sizeSelectorWindow.add(buttonPanel,BorderLayout.SOUTH);
sizeSelectorWindow.setVisible(true);
sizeSelectorWindow.setEnabled(true);
}
class ButtonEventHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int x = (int)(sizeSelectorWindow.getBounds().getX());
int y = (int) (sizeSelectorWindow.getBounds().getY());
int width = sizeSelectorWindow.getWidth();
int height = sizeSelectorWindow.getHeight();
screenArea = new Rectangle(x,y,width,height);
sizeSelectorWindow.dispatchEvent(new WindowEvent(sizeSelectorWindow, WindowEvent.WINDOW_CLOSING));
}
}
class WindowEventHandler implements WindowListener{
#Override
public void windowOpened(WindowEvent e) {
}
#Override
public void windowClosing(WindowEvent e) {
synchronized (lock) {
sizeSelectorWindow.setVisible(false);
lock.notify();
}
}
#Override
public void windowClosed(WindowEvent e) {
}
#Override
public void windowIconified(WindowEvent e) {
sizeSelectorWindow.setState(JFrame.NORMAL);
Toolkit.getDefaultToolkit().beep();
}
#Override
public void windowDeiconified(WindowEvent e) {}
#Override
public void windowActivated(WindowEvent e) {}
#Override
public void windowDeactivated(WindowEvent e) {}
}
}
Test1 class :
package screenrecorder;
import java.awt.Rectangle;
public class Test1{
public static void main(String[] args){
System.out.println(new ScreenSizeSelector().getSelectedScreenSize());
}
}
Test2 class :
package screenrecorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Test2 extends JFrame{
public Test2(){
JButton btn = new JButton("Click ME");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(new ScreenSizeSelector().getSelectedScreenSize());
}
});
getContentPane().add(btn);
setSize(100,100);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args){
new Test2();
}
}
Any help is appreciated.
when you click the button, the action listener waits for the getSelectedScreenSize() function to return. and the getSelectedScreenSize() function is waiting for the second window created by screenSelectorUI() to be invisible. screenSelectorUI() does create a second window, but you set the color like this:
sizeSelectorWindow.setBackground( new Color(0, 0, 0, 0) );
if you look at the color constructor javadocs:
public Color(int r,
int g,
int b,
int a)
Creates an sRGB color with the specified red, green, blue, and alpha values in the range (0 - 255).
Parameters:
r - the red component
g - the green component
b - the blue component
a - the alpha component
you set the alpha value to 0, making it completely invisible. (alpha value is transparency) also, this second window is undecorated and does not exit on close, so you don't even know it's there at all.
what I don't get is how test1 worked at all.
side note: when I try test 1 on mac it only shows the button and all I can do is click it. the button will disappear, but the application will still be running.
This is basically a total guess, but a lot of the swing components make requests to the operating system, not commands. sort of like saying, "hey can I please be resized to 400, 400?" the OS doesn't technically have to do what you say. and I was reading How does Java handle multithreading? which says that multithreading really depends on the OS. I have a feeling it just messes up somewhere when screenSelectorUI() is called by itself, but somehow gets it right when it's inside the thread of some button.

ScrollPane with BufferedImage is not scrolling

I am making program which will show zoomed in picture in scrolled frame and allow user to change single pixels of buffered image. I am trying to add panel with buffered image to scrolledPane but scroll bars wont appear.
Here is my panel:
package biometria1;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class noobPanel extends JPanel implements MouseListener{
private BufferedImage background;
int scale = 8;
noobPanel self;
public noobPanel(File file){
self = this;
try {
background = ImageIO.read(file);
} catch (IOException ex) {
Logger.getLogger(noobPanel.class.getName()).log(Level.SEVERE, null, ex);
}
addMouseListener(this);
}
public BufferedImage getImage(){
return this.background;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, background.getWidth()*8, background.getHeight()*8, 0, 0, background.getWidth(), background.getHeight(), this);
}
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX()/scale;
int y = e.getY()/scale;
System.out.println("before:"+background.getRGB(x, y));
background.setRGB(x, y, 100);
System.out.println("after:"+background.getRGB(x, y));
self.repaint();
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseEntered(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseExited(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
and thats how i add this panel in frame:
File image = chooser.getSelectedFile();
obraz = new noobPanel(image);
scrollPanel= new JScrollPane(obraz);
// 8 because i scale its 8 times bigger
obraz.setPreferredSize(new Dimension(obraz.getWidth()*8, obraz.getHeight()*8));
self.setSize(860, 640);
self.add(scrollPanel);
self.pack();
self.revalidate();
self.repaint();
Could u tell me what I am doing wrong? Thanks for any advice
You never set the size of your panel (preferred or otherwise). So the new Dimension(width*8, height*8) ist still 0,0.
So you can either do what Andrew Thompson suggested and override getPreferredSize() or just set your preferred size in your panels constructor, probably after loading the image since that's what actually matters?
Oh and why do you define the scale and then not use it? I'm talking about the *8 instead of *scale.
Also: This is a little off topic but why do you keep an extra reference "self" around? Seems like unnecessary overhead... You can call all those methods without the "self." in front of them and even if you couldn't there's already the "this" keyword for that. I mean yeah it's only around 4 bytes or something but it looks odd.

Pressing both mouse buttons not working correctly

i have to check if i pressed left, right or both buttons on my mouse, here is a sample code:
package sandbox;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Sandbox extends JFrame {
public static void main(String[] args) {
Sandbox s = new Sandbox();
s.setVisible(true);
}
public Sandbox() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 200);
setResizable(false);
setTitle("Frame");
setLayout(null);
JButton but = new JButton("click me");
but.setBounds(0, 0, 120, 50);
but.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if(SwingUtilities.isLeftMouseButton(e) | SwingUtilities.isRightMouseButton(e))
System.out.println("both");
else if(SwingUtilities.isLeftMouseButton(e))
System.out.println("left");
else if(SwingUtilities.isRightMouseButton(e))
System.out.println("right");
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
add(but);
}
}
i have to make it using SwingUtilities, but there is a problem if i press left or right single button it will print "both", not "left" or "right", if i used MouseEvent class it was working properly but i need it with SwingUtilities class if it's possible, thanks.
EDIT: using MouseEvent class it's working right:
if(e.getModifiersEx() == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK))
...
Don't check using OR (|) but use AND (& or better the short cut version &&) instead:
if(SwingUtilities.isLeftMouseButton(e) && SwingUtilities.isRightMouseButton(e))
...
Update:
For checking if both buttons are pressed, SwingUtilities doesn't seem to be the right tool. The isXxxxMouseButton(...) methods do the same as you do, they check the button masks, but use different masks which seem not be set when both buttons are pressed.
As an example, isRightMouseButton() is implemented as follows:
public static boolean isRightMouseButton(MouseEvent anEvent) {
return ((anEvent.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK);
}
As you can see, the implementation isn't that complext, so you might be better off to just do the mask check yourself.
mouse listene event on button
but.addMouseListener(new MouseListener() {
mouse listener event on fame.
this.addMouseListener(new MouseListener() {
Please check with '||' not '|', don't use the && because it is not possible to press both buttons at the same time
if(SwingUtilities.isLeftMouseButton(e) || SwingUtilities.isRightMouseButton(e))
You will get result
both
left
both
left
both
right

Java: Strange behavior of JEditorPane wrapped inside JScrollPane

I'm trying to build a simple help system to my software.
The help system built from JEditorPane(Loaded with HTML file) wrapped inside of JScrollPane, inside of the same window there is a JLabel.
When the user move the mouse over the JEditorPane on a specific word - more explanations appear in the JLabel.
I succeed doing it, but the problem is, that for some reason it work just on the beginning of the text.(the HTML file is long and must be scrolled...)
After i scroll down the page and hover over a word, it throw me BadLocationException.
On the code below there is a JEditorPane wrapped inside JScrollPane.
When the user move the mouse it print the current letter which the mouse point on.(on the help system i find the value of the word by this position and print explanations to the JLabel according to it)
But, as i said it work just on the beginning of the text.
Why ?
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.Point;
import java.io.IOException;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.text.BadLocationException;
public class JEditorPaneTestApp extends JFrame {
private JEditorPane editorPan;
private JScrollPane scrollPan;
public JEditorPaneTestApp() {
super();
try {
editorPan = new javax.swing.JEditorPane("file:///path/toHTML/file/helpFile.html");
}
catch (IOException e) {e.printStackTrace();}
scrollPan = new JScrollPane(editorPan);
this.add(scrollPan);
editorPan.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseMoved(java.awt.event.MouseEvent evt) {
Point p = new Point(evt.getX(), evt.getY());
int pos = editorPan.viewToModel(p);
try {
System.out.println(editorPan.getText(pos--, pos).charAt(0));
}
catch (BadLocationException e1) {
System.out.println("Invalid location");/* e1.printStackTrace();*/
}
}
});
scrollPan.setViewportView(editorPan);
this.add(scrollPan);
//
this.getContentPane().setLayout(new LayoutManager() {
#Override public Dimension preferredLayoutSize(Container arg0) {return null;}
#Override public Dimension minimumLayoutSize(Container arg0) {return null;}
#Override public void removeLayoutComponent(Component arg0) {}
#Override public void addLayoutComponent(String arg0, Component arg1) {}
#Override public void layoutContainer(Container conter) {
scrollPan.setBounds(0, 0, conter.getWidth(), conter.getHeight());
}
});
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
public static void main(String[] args) {
JEditorPaneTestApp test = new JEditorPaneTestApp();
}
}
Thanks
System.out.println(editorPan.getText(pos--, pos).charAt(0));
should be:
System.out.println(editorPan.getText(pos--, 1).charAt(0));

Categories

Resources