Reputation: 141
I'm working on a custom menu for a game and a problem occured to me by trying to draw the components. The idea is, to have either a picture or a video running in the background with a panel on top of it containing some custom menu-items.
The panel which contains the menu-items shall be semi-transparent and thats the problem I guess.
Since it's a bit hard to explain I've build an example wich demonstrates the issue. Redrawing the menu-item on mouseover works without any problems but it appears that the redraw also kinda affects the parts of the panel which should not be drawn since the shape of the actual menu-item doesn't fit the panels rectangle shape. This results in my menu-item having ugly dark edges which should not be there (top-right and bottom-left).
Hope you guys know a solution for that cause I start getting really annoyed. I tried everything I found here or somewhere else in the web, but it appears all the other problems only had to deal with redrawing one shape which does not change.
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class TransparentDrawTest extends JPanel implements MouseListener {
private final static Dimension SIZE = new Dimension(400, 50);
private boolean isSelected;
private boolean isClicked;
public static void main(String[] args) {
JFrame testFrame = new JFrame();
testFrame.setSize(500, 100);
testFrame.setResizable(false);
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testFrame.setLocationRelativeTo(null);
testFrame.setBackground(Color.WHITE);
JPanel semiTransPanel = new JPanel();
semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.6f));
semiTransPanel.setOpaque(true);
semiTransPanel.add(new TransparentDrawTest());
testFrame.add(semiTransPanel);
testFrame.setVisible(true);
}
public TransparentDrawTest() {
this.addMouseListener(this);
this.setSize(SIZE);
this.setMinimumSize(SIZE);
this.setMaximumSize(SIZE);
this.setPreferredSize(SIZE);
this.setOpaque(false);
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
g2d.fillRect(0, 0, 400, 50);
int[] pointsX;
int[] pointsY;
if (this.isSelected) {
pointsX = new int[] { 2, 348, 398, 48 };
pointsY = new int[] { 2, 2, 48, 48 };
}
else {
pointsX = new int[] { 2, 298, 348, 48 };
pointsY = new int[] { 2, 2, 48, 48 };
}
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(5));
g2d.setColor(Color.WHITE);
g2d.drawPolygon(pointsX, pointsY, 4);
if (this.isClicked)
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
else
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g.fillPolygon(pointsX, pointsY, 4);
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
this.isSelected = true;
this.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
this.isSelected = false;
this.repaint();
}
@Override
public void mousePressed(MouseEvent e) {
this.isClicked = true;
this.repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
this.isClicked = false;
this.repaint();
}
}
Upvotes: 0
Views: 361
Reputation: 347314
There are at least two things I can see which will give you trouble...
Firstly, you are changing the alpha composite of the Graphics
context, but you are not resting it....
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
The Graphics
context is a shared resource, all components being painted in the current paint cycle will be given the same instance of Graphics
, meaning that any changes you make to it will be passed to the other components being painted.
Instead, you should create a temporary copy of the Graphics
context and use that instead...
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
//...
// If you create it, you must dispose of it...
g2d.dispose();
Second of all, you are setting the background color of a opaque component to a semi transparent value...
semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.6f));
The problem is, Swing only treats components and opaque or transparent. By doing this, Swing won't know that it should also update the components below this one. Basically it will think that the component is opaque (because it is) and will not update the components below...
Basically, you will need to do something simular to what you've done with TransparentDrawTest
panel, and fake it, for example...
JPanel semiTransPanel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
};
semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f));
semiTransPanel.setOpaque(false);
I also can't figure out why you are doing this...
g2d.fillRect(0, 0, 400, 50);
In your TransparentDrawTest
pane...
You should be calling super.paintComponent(g)
first, this is actually what this method does anyway, it clears the Graphics
context for painting...amongst other things ;)
Updated with example code...
This is the example code I've been using to test you code with...
Ps: I changed the background color of the semi transparent panel so I can see the difference...
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TransparentDrawTest extends JPanel implements MouseListener {
private final static Dimension SIZE = new Dimension(400, 50);
private boolean isSelected;
private boolean isClicked;
public static void main(String[] args) {
JFrame testFrame = new JFrame();
testFrame.setSize(500, 100);
testFrame.setResizable(false);
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testFrame.setLocationRelativeTo(null);
testFrame.setBackground(Color.WHITE);
JPanel semiTransPanel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getBackground());
g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f));
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
};
semiTransPanel.setOpaque(false);
semiTransPanel.setBackground(Color.RED);
semiTransPanel.add(new TransparentDrawTest());
testFrame.add(semiTransPanel);
testFrame.setVisible(true);
}
public TransparentDrawTest() {
this.addMouseListener(this);
this.setSize(SIZE);
this.setMinimumSize(SIZE);
this.setMaximumSize(SIZE);
this.setPreferredSize(SIZE);
this.setOpaque(false);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int[] pointsX;
int[] pointsY;
if (this.isSelected) {
pointsX = new int[]{2, 348, 398, 48};
pointsY = new int[]{2, 2, 48, 48};
} else {
pointsX = new int[]{2, 298, 348, 48};
pointsY = new int[]{2, 2, 48, 48};
}
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(5));
g2d.setColor(Color.WHITE);
Composite comp = g2d.getComposite();
if (this.isClicked) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
} else {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
}
g2d.fillPolygon(pointsX, pointsY, 4);
g2d.setComposite(comp);
g2d.drawPolygon(pointsX, pointsY, 4);
g2d.dispose();
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
this.isSelected = true;
this.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
this.isSelected = false;
this.repaint();
}
@Override
public void mousePressed(MouseEvent e) {
this.isClicked = true;
this.repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
this.isClicked = false;
this.repaint();
}
}
Upvotes: 2
Reputation: 324157
See Backgrounds With Transparency for the probable problem and a simple solution:
//testFrame.add(semiTransPanel);
testFrame.add( new AlphaContainer(semiTransPanel) );
Upvotes: 0