Before you read, here are some clarifications on what the question is about:
The SSCCE is designed for Java 7. It would be possible to use sun.*.AWTUtilities to adapt it to Java 6, but it does not matter to me how it works on Java 6.
The faulting line is [...]new JDialog(someWindow). Ghosting can be fixed in the SSCCE by simply changing that line to [...]new JDialog().
Why don't top level windows exhibit ghosting?
Expected behavior: final JDialog d = new JDialog() (see SSCCE)
As you can see, the right window has a semitransparent background (as expected).
Actual behavior: final JDialog d = new JDialog(f) (see SSCCE)
In this case, the right window has an opaque background. As a matter of fact, it takes 3-4 repaints due to any reason (easiest to reproduce is repaint on rollover) for the background to become completely opaque.
SSCCE:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.synth.ColorType;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthConstants;
import javax.swing.plaf.synth.SynthContext;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthPainter;
import javax.swing.plaf.synth.SynthStyle;
import javax.swing.plaf.synth.SynthStyleFactory;
public class SynthSSCCE
{
public static void main(String[] args) throws Exception
{
final SynthLookAndFeel laf = new SynthLookAndFeel();
UIManager.setLookAndFeel(laf);
SynthLookAndFeel.setStyleFactory(new StyleFactory());
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
final JFrame f = new JFrame();
{
f.add(new JButton("Works properly"));
f.setUndecorated(true);
f.setBackground(new Color(0, true));
f.setSize(300, 300);
f.setLocation(0, 0);
f.setVisible(true);
}
{
final JDialog d = new JDialog(f);
final JButton btn = new JButton("WTF?");
// uncomment and notice that this has no effect
// btn.setContentAreaFilled(false);
d.add(btn);
d.setUndecorated(true);
d.setBackground(new Color(0, true));
d.setSize(300, 300);
d.setLocation(320, 0);
d.setVisible(true);
}
}
});
}
static class StyleFactory extends SynthStyleFactory
{
private final SynthStyle style = new Style();
#Override
public SynthStyle getStyle(JComponent c, Region id)
{
return style;
}
}
static class Style extends SynthStyle
{
private final SynthPainter painter = new Painter();
#Override
protected Color getColorForState(SynthContext context, ColorType type)
{
if (context.getRegion() == Region.BUTTON && type == ColorType.FOREGROUND)
return Color.GREEN;
return null;
}
#Override
protected Font getFontForState(SynthContext context)
{
return Font.decode("Monospaced-BOLD-30");
}
#Override
public SynthPainter getPainter(SynthContext context)
{
return painter;
}
#Override
public boolean isOpaque(SynthContext context)
{
return false;
}
}
static class Painter extends SynthPainter
{
#Override
public void paintPanelBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
{
final Graphics g2 = g.create();
try
{
g2.setColor(new Color(255, 255, 255, 128));
g2.fillRect(x, y, w, h);
}
finally
{
g2.dispose();
}
}
#Override
public void paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
{
final Graphics g2 = g.create();
try
{
if ((context.getComponentState() & SynthConstants.MOUSE_OVER) == SynthConstants.MOUSE_OVER)
g2.setColor(new Color(255, 0, 0, 255));
else
g2.setColor(new Color(0xAA, 0xAA, 0xAA, 255));
g2.fillRoundRect(x, y, w, h, w / 2, h / 2);
}
finally
{
g2.dispose();
}
}
}
}
And these are my questions...
What is going on? As in, why this exhibits behavior of a custom-painted non-opaque component that forgets to call super?
Why doesn't it happen to TL windows?
What is the easiest way to fix it, aside from not using non-TL windows?
it takes 3-4 repaints due to any reason (easiest to reproduce is repaint on rollover) for the background to become completely opaque.
Check out Backgrounds With Transparency which should give you some insight into the problem.
I've never played with Synth so I don't know if the same solution will work or not.
Why don't top level windows exhibit ghosting?
According to Oracle (Java Tutorials):
Each top-level container has a content pane that, generally speaking,
contains (directly or indirectly) the visible components in that
top-level container's GUI.
http://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
The glass pane is often used to intercept input events occuring over the top-level container, and can also be used to paint over multiple components. It doesnt allow transparency.
Hence how you used
final Graphics g2 = g.create();
If you have the javax.swing.JComponent.paintComponent overrided in a method opposed to creating the graphics object yourself it should mitigate the transparency by super.g();
Fix this by creating a separate method listed above for graphics
Related
I need to get a mirror of JLabel or JTextArea.
http://www.subirimagenes.net/i/150305101716451074.jpg
If I use a JTextArea, I'll need the letters are complety mirrored.
If I use a JLabel, I'll need format and the mirrored letters.
The example was created on Photoshop.
My idea is using graphics(), but I don't have idea how to do it.
Here's the bad news: It's not as straight-forward as we might wish, there's a limitation. In Swing, graphics transformations are applied only on the paint operation, not the general layout and event process. Therefore, in Swing the mirrored component is basically "unusable", it cannot be used for anything else than displaying the mirror image of the primary component. Coordinates of mouse clicks etc. will be wrong.
Therefore, this is all tricky stuff, a bit hackish.
There are multiple ways how you can do that.
One possibility is to use two views on one model and tell the Graphics of one of the views to flip horizontally.
Here's an example how to do so which demonstrates a flipped JEditorPane:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class MirrorText {
public static void main(final String... args) {
SwingUtilities.invokeLater(MirrorText::setupUI);
}
public static void setupUI() {
final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
final JEditorPane mirroredEditor = new JEditorPane("text/html", "") {
protected Graphics getComponentGraphics(final Graphics g) {
return horizontalFlip(super.getComponentGraphics(g), getWidth());
}
};
mirroredEditor.setDocument(editor.getDocument());
final JFrame frame = new JFrame("mirrored label");
final JPanel mirrorPanel = new JPanel(new GridLayout(1, 2));
mirrorPanel.add(new JScrollPane(editor));
mirrorPanel.add(new JScrollPane(mirroredEditor));
frame.add(mirrorPanel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
public static Graphics horizontalFlip(final Graphics g, final int width) {
final Graphics2D g2d = (Graphics2D) g;
final AffineTransform tx = g2d.getTransform();
tx.scale(-1.0, 1.0);
tx.translate(-width, 0);
g2d.setTransform(tx);
return g2d;
}
}
The advantage of this solution is that the mirror entirely the original component's MVC and observers because it is the same type of component (View/Controller) on the very same model.
The disadvantage of this solution is that you have create the mirror component in a way that is very specific to the component that is mirrored.
Another possibility is to create a decorator JComponent Mirror which can mirror an arbitrary other JComponent. This is a bit tricky, as in Java, decorators cannot override methods of the decorated object, and the Mirror needs to be updated (repainted) as well whenever the original component is updated (repainted).
Here's an incomplete example using a Mirror which hooks into the corresponding events. Incomplete because it only hooks into DocumentEvent but should also hook onto other events as well, like CaretEvent. It would be nice if Swing would have something like a PaintEvent. As far as I am aware of, it hasn't. (Well, in fact it has, but there's no corresponding PaintListener and addPaintListener().)
Also incomplete because the Mirror doesn't observe the original component's attributes like size. It only works because the GridLayout on the MirrorPanel keeps the mirror size in sync with the original component.
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
public class MirrorText {
public static void main(final String... args) {
SwingUtilities.invokeLater(MirrorText::setupUI);
}
public static void setupUI() {
final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
final MirrorPanel mirrorPanel = new MirrorPanel(new JScrollPane(editor));
editor.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
public void insertUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
public void removeUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
});
final JFrame frame = new JFrame("mirrored label");
frame.add(mirrorPanel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}
class MirrorPanel extends JPanel {
public MirrorPanel(final JComponent c) {
super(new GridLayout(1, 2));
add(c);
add(new Mirror(c));
}
public void updateMirror() {
repaint();
}
}
class Mirror extends JComponent {
private final JComponent mirroredComponent;
public Mirror(final JComponent mirroredComponent) {
this.mirroredComponent = mirroredComponent;
}
public static Graphics horizontalFlip(final Graphics g, final int width) {
final Graphics2D g2d = (Graphics2D) g;
final AffineTransform tx = g2d.getTransform();
tx.scale(-1.0, 1.0);
tx.translate(-width, 0);
g2d.setTransform(tx);
return g2d;
}
public void paint(final Graphics g) {
mirroredComponent.paint(horizontalFlip(g, mirroredComponent.getWidth()));
}
}
There probably are more possibilities as well. For example, one could override the mirrored component's paint() method to update the mirror component as well. That would get rid of getting notified, but it would lead to unnecessary paint() calls in case painting isn't done due to content change but due to buffer destruction (i.e. other window moved away).
Here's one way to create a mirror image.
Basically, you print the contents of the JTextArea on a BufferedImage. Then you reverse the pixels of the BufferedImage on the X axis.
package com.ggl.testing;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class MirrorImage implements Runnable {
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MirrorImage());
}
#Override
public void run() {
frame = new JFrame("Mirror Image Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new FlowLayout());
JPanel textPanel = new JPanel();
JTextArea textArea = new JTextArea(15, 30);
textPanel.add(textArea);
mainPanel.add(textPanel);
MirrorPanel mirrorPanel = new MirrorPanel();
mirrorPanel.setPreferredSize(textPanel.getPreferredSize());
mainPanel.add(mirrorPanel);
TextListener listener = new TextListener(textArea, mirrorPanel);
textArea.getDocument().addDocumentListener(listener);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
listener.createImage(textArea);
frame.setVisible(true);
}
private class MirrorPanel extends JPanel {
private static final long serialVersionUID = 2496058019297247364L;
private Image image;
public void setImage(Image image) {
this.image = image;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, (getWidth() - image.getWidth(this)) / 2,
(getHeight() - image.getHeight(this)) / 2, this);
}
}
private class TextListener implements DocumentListener {
private JTextArea textArea;
private MirrorPanel mirrorPanel;
public TextListener(JTextArea textArea, MirrorPanel mirrorPanel) {
this.textArea = textArea;
this.mirrorPanel = mirrorPanel;
}
#Override
public void insertUpdate(DocumentEvent event) {
createImage(textArea);
}
#Override
public void removeUpdate(DocumentEvent event) {
createImage(textArea);
}
#Override
public void changedUpdate(DocumentEvent event) {
createImage(textArea);
}
public void createImage(JTextArea textArea) {
BufferedImage img = new BufferedImage(textArea.getWidth(),
textArea.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
textArea.printAll(g2d);
g2d.dispose();
createMirrorImage(img);
mirrorPanel.setImage(img);
}
private void createMirrorImage(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
int[][] pixels = new int[width][height];
for (int i = width - 1; i >= 0; i--) {
int j = width - i - 1;
for (int k = 0; k < height; k++) {
pixels[j][k] = img.getRGB(i, k);
}
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
img.setRGB(i, j, pixels[i][j]);
}
}
}
}
}
So a buddy of mine built a RenderingEngine where it pretty much contains the methods that graphics has. That way you only need to call the RenderingEngines class's methods to render stuff to the screen. Without looking and trying to learn instead of cheating, im trying to replicate it based off my knowledge. I ran into a problem though. All my methods work its just that when the RenderingEngine class get initialized in my game class the graphics g in my RenderingEngine class is null after trying to get the JPanel's graphics. I don't know if i'm not understanding how this works and would love an explanation, ill put the code down below.
RENDERING CLASS BELOW:
package game.Main;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class RenderingEngine extends JPanel
{
private Graphics g;
public RenderingEngine()
{
g = this.getGraphics();
}
public void setColor(Color color)
{
g.setColor(color);
}
public void fillRect(Rectangle rect)
{
g.fillRect(rect.x, rect.y, rect.width, rect.height);
}
public void drawRect(Rectangle rect)
{
g.drawRect(rect.x, rect.y, rect.width, rect.height);
}
public void setStringFont(Font font)
{
g.setFont(font);
}
public void drawString(String string, int x, int y)
{
g.drawString(string, x, y);
}
public void drawImage(BufferedImage image, Rectangle rect)
{
g.drawImage(image, rect.x, rect.y, rect.width, rect.height, this);
}
public void clear(Rectangle rect)
{
g.clearRect(rect.x, rect.y, rect.width, rect.height);
}
public int widthofString(String string, Font font)
{
String text = string;
AffineTransform affinetransform = new AffineTransform();
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
Font f1 = font;
int textwidth = (int)(font.getStringBounds(text, frc).getWidth());
return textwidth;
//int textheight = (int)(font.getStringBounds(text, frc).getHeight());
}
public int heightofString(String string, Font font)
{
String text = string;
AffineTransform affinetransform = new AffineTransform();
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
Font f1 = font;
//int textwidth = (int)(font.getStringBounds(text, frc).getWidth());
int textheight = (int)(font.getStringBounds(text, frc).getHeight());
return textheight;
}
public void paint()
{
}
}
WHERE I INITIALIZE RENDERINGENGINE IN THE GAME CLASS:
private void Load()
{
frame = new JFrame();
frame.setSize(800,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
render = new RenderingEngine();
frame.add(render);
LoadContent();
gameloop = new GameLoop();
gameloop.Run();
}
You might consider it as unsatisfactory when I answer the question about "How to initialize a Graphics g" for a JPanel with ...
Not at all!
You might want to have a look at the Performing Custom Painting article, or the article about Painting in AWT and Swing (which unfortunately was slightly messed up during the transition from Sun to Oracle, but still contains valuable and more detailed information than the other article).
The Graphics of a JPanel is, so to say, managed by the operating system, and represents an area on the screen where the painting may happen. When the panel is not yet visible, the Graphics that is returned by a call to getGraphics() may be null. It may also be null at other occasions. Particularly, when it is accessed from a thread that is not the event dispatch thread.
In general, as a rule of thumb:
Never call getGraphics on a Component!
One possible solution for such a "RenderingEngine" class would be to use the Graphics object of a BufferedImage. This should not be too complicated, but you'll have to consider several aspects here:
When will the image be initialized?
When will the graphics be initialized?
When will the graphics be disposed?
What happens when the panel is resized?
...
However, with this method, it should at least be possible to create such a "RenderingEngine" that is suitable for the intended usage.
I am having a problem with the code below. If I load the image directly into the JPanel I can see it. But when I try to draw it first to the BufferedImage before drawing the BufferedImage on the JPanel the image is not visible. What am I doing wrong?
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import java.awt.*;
import javax.swing.JPanel;
/**
*
* #author Duafeb
*/
public class RTester {
BufferedImage backBuffer;
Graphics2D g2;
Pane pain;
Image img;
public RTester(){
JFrame frame=new JFrame("Sprite Tester");
frame.setSize(1200, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
backBuffer= new BufferedImage(1200,700,BufferedImage.TYPE_INT_RGB);
g2=backBuffer.createGraphics();
pain=new Pane();
frame.add(pain);
Toolkit tk=Toolkit.getDefaultToolkit();
img=tk.getImage(this.getClass().getResource("running.png"));
frame.setVisible(true);
}
public class Pane extends JPanel{
#Override
public void paintComponent(Graphics g){
Graphics2D g3=(Graphics2D)g;
g3.drawImage(backBuffer, 0, 0, this);
}
}
public void display(){
g2.setColor(Color.yellow);
g2.fillRect(0, 0, pain.getWidth(), pain.getHeight());
g2.drawImage(img, 0, 0, pain);
pain.repaint();
}
public static void main(String[] args){
RTester test=new RTester();
test.display();
}
}
There are a few things that don't feel right about this...
The first is, you create a Graphics context to BufferedImage, but never dispose of it. Be careful, on some systems this can prevent the contents from been rendered, but this might relate to the screen device rather than a BufferedImage
For example, if I alter you code to paint the contents directly within the paintComponent method instead of to the BufferedImage, the image will be displayed (all bit a split second after the window becomes visible).
I'm not sure what it is you're trying to achieve by using the BufferedImage, but you could achieve the same thing straight through the paintComponent method
Instead of using Toolkit.getImage, you could use ImageIO.read, which guarantees that when it returns, the image is fully loaded (or will throw an IOException if it fails) or as #Reimeus had previously suggested, using a MediaTracker to ensure that the image is properly loaded before you continue using it.
So, you have four options....
One
Use a MediaTracker to wait for the image to be loaded...
MediaTracker mt = new MediaTracker(frame);
mt.addImage(img, 1);
try {
mt.waitForAll();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Two
Use ImageIO.read instead...
img = ImageIO.read(this.getClass().getResource("running.png"));
Three
Render output directly in the paintComponent method...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g3 = (Graphics2D) g;
g3.setColor(Color.yellow);
g3.fillRect(0, 0, pain.getWidth(), pain.getHeight());
g3.drawImage(img, 0, 0, obsever);
}
Four
Use you own ImageObserver to ensure that when the image is updated, you re-render it to the backing buffer...
private MyImageObsever obsever;
public void display() {
if (obsever == null) {
obsever = new MyImageObsever(this);
}
g2.setColor(Color.yellow);
g2.fillRect(0, 0, pain.getWidth(), pain.getHeight());
g2.drawImage(img, 0, 0, obsever);
pain.repaint();
}
public class MyImageObsever implements ImageObserver {
private RTester tester;
public MyImageObsever(RTester tester) {
this.tester = tester;
}
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
tester.display();
return (infoflags & (ALLBITS|ABORT)) == 0;
}
}
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();
}
}
}
}
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
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.
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
trigger a repaint on rollover
implement the paint to apply the transparent color
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);
}
}
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.
I am currently trying to develop a basic pixel editor application to build up my programming experience with Java. I am designing it so the user has several colour options on, they click on an option and then they can drag over the cells in the grid and they change colour (like a typical image editor, but with a sort of snap on to each grid cell)
Any idea of what Java component, if any, is able to implement this type of grid in Java?
I had thought of each cell being a JButton, but this seemed terribly inefficient and I don't think it would be possible to change the colour of each cell(button) with out individually clicking on each one.
Any help appreciated.
More than a few hundred components is awkward. One easy way to get big pixels is to use drawImage() and scale the mouse coordinates as shown here and here. Here's a simple example.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
/** #see http://stackoverflow.com/questions/2900801 */
public class Grid extends JPanel implements MouseMotionListener {
private final BufferedImage img;
private int imgW, imgH, paneW, paneH;
public Grid(String name) {
super(true);
Icon icon = UIManager.getIcon(name);
imgW = icon.getIconWidth();
imgH = icon.getIconHeight();
this.setPreferredSize(new Dimension(imgW * 10, imgH * 10));
img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) img.getGraphics();
icon.paintIcon(null, g2d, 0, 0);
g2d.dispose();
this.addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics g) {
paneW = this.getWidth();
paneH = this.getHeight();
g.drawImage(img, 0, 0, paneW, paneH, null);
}
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
int x = p.x * imgW / paneW;
int y = p.y * imgH / paneH;
int c = img.getRGB(x, y);
this.setToolTipText(x + "," + y + ": "
+ String.format("%08X", c));
}
#Override
public void mouseDragged(MouseEvent e) {
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Grid("Tree.closedIcon"));
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
}
An option is to use a large Canvas, and intercept events on it. Draw whatever you need to in the paint(g) method.