Reputation: 53
So I have this login form and I have a "user photo." I'm trying to make it so that when you hover the mouse over the photo area, a transparent label with a colored background will appear (to give the effect of "selecting the photo"). It looks like this:
And once you move your mouse off it, it goes back to being "deselected."
Now my problem is, if you hover your mouse over the login button first then move your mouse over the photo, a "ghost login button" appears. It looks like this:
I don't know why this is happening. Can someone help? Here is the relevant code:
package com.stats;
public class Stats extends JFrame implements Serializable {
private JLabel fader;
public Stats() {
try {
Image image = ImageIO.read(new File(System.getenv("APPDATA")
+ "\\Stats\\Renekton_Cleave.png"));
JLabel labelUserPhoto = new JLabel(new ImageIcon(image));
fader = new JLabel();
fader.setBounds(97, 44, 100, 100);
fader.setOpaque(true);
fader.setBackground(new Color(0, 0, 0, 0));
labelUserPhoto.setBounds(97, 44, 100, 100);
PicHandler ph = new PicHandler();
contentPane.add(fader);
contentPane.add(labelUserPhoto);
fader.addMouseMotionListener(ph);
} catch(Exception e) {
e.printStackTrace();
}
}
private class PicHandler implements MouseMotionListener {
public void mouseDragged(MouseEvent e) { }
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
System.out.println("x: " + x + ", y: " + y);
if ((x > 16 && x < 80) && (y > 16 && y < 80)) {
if (!fader.isOpaque()) {
fader.setOpaque(true);
fader.setBackground(new Color(0, 0, 0, 40));
fader.repaint();
}
} else {
if (fader.isOpaque()) {
fader.setOpaque(false);
fader.repaint();
}
}
}
}
Upvotes: 2
Views: 3514
Reputation: 405
I had a similar problem with ghosted images after something moved or was resized. @MadProgrammer has some really good points, but in the end, they didn't work for me (possibly because I had multiple layers using 0.0 alpha value colors, some of which also had images in them, so I couldn't set a composite value for the whole component). In the end, what fixed it was a simple call to
<contentpane>.repaint()
after all the draw commands were executed.
Upvotes: 0
Reputation: 51525
Since jdk7 there a new mechanism to apply visual decorations (and listening to events for child components): that's the JLayer/LayerUI pair.
In your case a custom layerUI would
Below is an example, similar to the WallPaperUI in the tutorial:
// usage: create the component and decorate it with the custom ui
JLabel label = new JLabel(myIcon);
content.add(new JLayer(label, new RolloverUI()));
// custom layerUI
public static class RolloverUI extends LayerUI<JComponent> {
private Point lastMousePoint;
private JLayer layer;
/**
* Implemented to install the layer and enable mouse/motion events.
*/
@Override
public void installUI(JComponent c) {
super.installUI(c);
this.layer = (JLayer) c;
layer.setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK
| AWTEvent.MOUSE_EVENT_MASK);
}
@Override
protected void processMouseMotionEvent(MouseEvent e,
JLayer<? extends JComponent> l) {
updateLastMousePoint(e.getPoint());
}
@Override
protected void processMouseEvent(MouseEvent e,
JLayer<? extends JComponent> l) {
if (e.getID() == MouseEvent.MOUSE_EXITED) {
updateLastMousePoint(null);
} else if (e.getID() == MouseEvent.MOUSE_ENTERED) {
updateLastMousePoint(e.getPoint());
}
}
/**
* Updates the internals and calls repaint.
*/
protected void updateLastMousePoint(Point e) {
lastMousePoint = e;
layer.repaint();
}
/**
* Implemented to apply painting decoration below the component.
*/
@Override
public void paint(Graphics g, JComponent c) {
if (inside()) {
Graphics2D g2 = (Graphics2D) g.create();
int w = c.getWidth();
int h = c.getHeight();
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .5f));
g2.setPaint(new GradientPaint(0, 0, Color.yellow, 0, h,
Color.red));
g2.fillRect(0, 0, w, h);
g2.dispose();
}
super.paint(g, c);
}
protected boolean inside() {
if (lastMousePoint == null || lastMousePoint.x < 0
|| lastMousePoint.y < 0)
return false;
Rectangle r = layer.getView().getBounds();
r.grow(-r.width / 10, -r.height / 10);
return r.contains(lastMousePoint);
}
}
Upvotes: 0
Reputation: 347204
I can see a number of issues with your example, but the most significant is the use of a color with an alpha value.
fader.setBackground(new Color(0, 0, 0, 40));
Swing doesn't render components with alpha based colors well (within this context). By making component opaque and then setting the background color to use an alpha value, you are telling Swing that it doesn't need to worry about painting what's underneath your component, which isn't true...
The Graphics
context is also a shared resource, meaning that anything that was painted before your component is still "painted", you need to clear the Graphics
context before painting.
This example uses a rather nasty trick to get it's work done. Because all the painting occurs within the UI delegate, if we were simply to allow the default paint chain to continue, we wouldn't be able to render underneath the icon. Instead, we take over control of the "dirty" details and paint the background on the behalf the parent.
This would be simpler to achieve if we simple extended from something like JPanel
and painted the image ourselves
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FadingIcon {
public static void main(String[] args) {
new FadingIcon();
}
public FadingIcon() {
startUI();
}
public void startUI() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
BufferedImage img = null;
try {
img = ImageIO.read(new File("C:\\Users\\swhitehead\\Documents\\My Dropbox\\Ponies\\SmallPony.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setLayout(new GridBagLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new FadingLabel(new ImageIcon(img)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class FadingLabel extends JLabel {
private boolean mouseIn = false;
private MouseHandler mouseHandler;
public FadingLabel(Icon icon) {
super(icon);
setBackground(Color.RED);
super.setOpaque(false)(
}
@Override
public void setOpaque(boolean opaque) {
}
@Override
public final boolean isOpaque() {
return false;
}
protected MouseHandler getMouseHandler() {
if (mouseHandler == null) {
mouseHandler = new MouseHandler();
}
return mouseHandler;
}
@Override
public void addNotify() {
super.addNotify();
addMouseListener(getMouseHandler());
}
@Override
public void removeNotify() {
removeMouseListener(getMouseHandler());
super.removeNotify();
}
@Override
protected void paintComponent(Graphics g) {
if (mouseIn) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
getUI().paint(g, this);
}
public class MouseHandler extends MouseAdapter {
@Override
public void mouseEntered(MouseEvent e) {
mouseIn = true;
repaint();
}
@Override
public void mouseExited(MouseEvent e) {
mouseIn = false;
repaint();
}
}
}
}
I would also recommend that you take the time to learn how to use appropriate layout managers, they will save you a lot of hair pulling later
Check out A Visual Guide to Layout Managers and Laying Out Components Within a Container
Upvotes: 3
Reputation: 80
Since I can't comment I have to put this as an answer:
As trashgod mentioned, this problem can be caused by a missing super.paintComponent(g) call however you don't seem to be overriding the paintComponent method at all (at least you didn't show it here). If you do override the paintComponent method of a JFrame or any JPanels, you need to have:
public void paintComponent(Graphics g) {
super.paintComponent(g);
//rest of your drawing code....
}
But if you haven't used one at all then the problem may be caused by something else.
Upvotes: 0