Reputation: 328
I have a circular JButton constructed using a circular PNG image with a transparent area.
I want to fill the transparent area of the JButton image with a given colour - but something other than the opaque background colour of the JPanel which contains the JButton. I want to do this programmatically in Java rather than providing pre-coloured images from a graphics package.
I've got as far as the code below, which simply allows the orange background of the opaque panel to colour the transparent area. I can't figure out, though, how to keep the panel background as orange but fill the image transparency with, say, blue (or another colour for rollover and pressed effects).
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.DefaultButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class App extends JFrame implements ActionListener
{
public App()
{
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(makeContentPane());
}
private final JPanel makeContentPane()
{
JPanel contentPane = new JPanel();
BufferedImage bi = null;
try
{
bi = ImageIO.read(new URL("http://features.advisorwebsites.com/sites/default/files/users/AdvisorWebsitesFeatures/icones/1384148625_twitter_circle_gray.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
ImageIcon icon = new ImageIcon(bi);
MyButton myButton = new MyButton(icon);
myButton.addActionListener(this);
contentPane.add(myButton);
contentPane.setBackground(Color.ORANGE);
contentPane.setOpaque(true);
return contentPane;
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
App app = new App();
app.pack();
app.setVisible(true);
}
});
}
class MyButton extends JButton
{
public MyButton(ImageIcon icon)
{
super(icon);
setMargin(new Insets(0, 0, 0, 0));
setFocusable(false);
setContentAreaFilled(false);
setBorderPainted(false);
setModel(new DefaultButtonModel());
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
}
@Override
public void actionPerformed(ActionEvent arg0)
{
System.out.println("You clicked me");
}
}
I'm guessing I might need to apply Graphics2D transformations to my transparent image to create a set of three new images (for the normal, rollover and pressed states of my JButton). Is this the appropriate way forward and, if so, can you provide me with a code example for the bit I'm missing ?
Thanks
Upvotes: 1
Views: 173
Reputation: 347204
Okay, this is little more then you're asking for, but it demonstrates a means by which you can generate a shape which matches the shape of a image, based on its alpha, all through the magic of AlphaComposite
...
So, basically, we're going to take the original image on the left and turn into the image on the right...
This example generates two images, a "normal" and a "roll over" which is easily applied to a JButton
, for example...
Normal...
Roll over...
Now, if for some reason, you wanted to know when the button was "rolled over", you could simply add a ChangeListener
to the ButtonModel
, for example...
JButton btn = new JButton(new ImageIcon(normal));
btn.setRolloverIcon(new ImageIcon(rollOver));
btn.setRolloverEnabled(true);
btn.getModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
System.out.println("Change: " + model.isRollover());
}
});
And the runnable example...
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
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.ButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class JavaApplication24 {
public static void main(String[] args) {
new JavaApplication24();
}
public JavaApplication24() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(JavaApplication24.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
public TestPane() throws IOException {
setLayout(new GridBagLayout());
BufferedImage source = ImageIO.read(...));
// This the shape we want the source to be clipped to
int size = Math.min(source.getWidth(), source.getHeight());
BufferedImage mask = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D maskg = mask.createGraphics();
applyQualityRenderingHints(maskg);
maskg.setColor(Color.WHITE);
maskg.fillOval((source.getWidth() - size) / 2, (source.getHeight() - size) / 2, size, size);
maskg.dispose();
// This will mask the source to the shape we've defined
BufferedImage masked = applyMask(source, mask, AlphaComposite.DST_ATOP);
BufferedImage normal = makeOutline(masked, Color.BLACK);
BufferedImage rollOver = makeOutline(masked, Color.RED);
JButton btn = new JButton(new ImageIcon(normal));
btn.setRolloverIcon(new ImageIcon(rollOver));
btn.setRolloverEnabled(true);
btn.getModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
System.out.println("Change: " + model.isRollover());
}
});
add(btn);
}
protected BufferedImage makeOutline(BufferedImage original, Color color) {
// This generates a image which is completely filled with the provided color
BufferedImage outline = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D outlineg = outline.createGraphics();
applyQualityRenderingHints(outlineg);
outlineg.setColor(color);
outlineg.fillRect(0, 0, outline.getWidth(), outline.getHeight());
outlineg.dispose();
// This applies a AlphaComposite to mask the outline with the shape
// of the original image
outline = applyMask(original, outline, AlphaComposite.SRC_ATOP);
// Now we make it slightly larger...
double scale = 1.05;
outline = getScaledInstanceToFit(outline, scale);
// And we combine the images
outlineg = outline.createGraphics();
int x = (outline.getWidth() - original.getWidth()) / 2;
int y = (outline.getHeight() - original.getHeight()) / 2;
outlineg.drawImage(original, x, y, this);
outlineg.dispose();
return outline;
}
public BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage) {
return applyMask(sourceImage, maskImage, AlphaComposite.DST_IN);
}
public BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage, int method) {
BufferedImage maskedImage = null;
if (sourceImage != null) {
int width = maskImage.getWidth(null);
int height = maskImage.getHeight(null);
maskedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D mg = maskedImage.createGraphics();
applyQualityRenderingHints(mg);
int x = (width - sourceImage.getWidth(null)) / 2;
int y = (height - sourceImage.getHeight(null)) / 2;
mg.drawImage(sourceImage, x, y, null);
mg.setComposite(AlphaComposite.getInstance(method));
mg.drawImage(maskImage, 0, 0, null);
mg.dispose();
}
return maskedImage;
}
public void applyQualityRenderingHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
// g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
public BufferedImage getScaledInstanceToFit(BufferedImage img, double scale) {
int width = (int)(img.getWidth() * scale);
int height = (int)(img.getHeight()* scale);
return getScaledInstanceToFit(img, new Dimension(width, height));
}
public BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {
double scaleFactor = getScaleFactorToFit(new Dimension(img.getWidth(), img.getHeight()), size);
return getScaledInstance(img, scaleFactor);
}
public double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
public double getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
public BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
}
protected BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) {
BufferedImage imgScale = img;
int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
} else {
imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
}
return imgScale;
}
protected BufferedImage getScaledDownInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
if (targetHeight > 0 || targetWidth > 0) {
int w, h;
if (higherQuality) {
w = img.getWidth();
h = img.getHeight();
} else {
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
} else {
ret = new BufferedImage(1, 1, type);
}
return ret;
}
protected BufferedImage getScaledUpInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
w = img.getWidth();
h = img.getHeight();
} else {
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w < targetWidth) {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h < targetHeight) {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp = null;
} while (w != targetWidth || h != targetHeight);
return ret;
}
}
}
This code borrows heavily from my personal library code, so it might be a little convoluted ;)
Upvotes: 1
Reputation: 999
try to override paintComponent()
in your custom JButton
Here's what I tried
class MyButton extends JButton {
public MyButton(ImageIcon icon) {
super(icon);
setMargin(new Insets(0, 0, 0, 0));
setFocusable(false);
setContentAreaFilled(false);
setBorderPainted(false);
setModel(new DefaultButtonModel());
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
public void paintComponent(Graphics g) {
g.setColor(Color.green);
g.fillOval(0, 0, this.getWidth(), this.getHeight());
super.paintComponent(g);
}
}
And here's the result:
EDIT:
To get it to change color depending on mouse movement, you need to add a MouseListener to the JButton and add a Color
attribute to the class, when a MouseEvent is fired you change the color. Also don't forget to set the graphics to that color in paintComponent()
class MyButton extends JButton {
Color color = Color.GREEN;
public MyButton(ImageIcon icon) {
super(icon);
setMargin(new Insets(0, 0, 0, 0));
setFocusable(false);
setContentAreaFilled(false);
setBorderPainted(false);
setModel(new DefaultButtonModel());
setCursor(new Cursor(Cursor.HAND_CURSOR));
this.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
color=Color.RED;
}
@Override
public void mouseReleased(MouseEvent e) {
color=Color.BLUE;
}
@Override
public void mouseEntered(MouseEvent e) {
color=Color.BLUE;
}
@Override
public void mouseExited(MouseEvent e) {
color=Color.GREEN;
}
});
}
@Override
public void paintComponent(Graphics g) {
g.setColor(color);
g.fillOval(0, 0, this.getWidth(), this.getHeight());
super.paintComponent(g);
}
}
Upvotes: 2