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.
Related
I have a JLabel image on a JFrame that I want too brighten upon hovering, have tried a few things however nothing seems to be working, here's my current code:
public class DivinationLogo extends JLabel {
private BufferedImage logo;
public DivinationLogo() {
super();
try {
logo = ImageIO.read(getClass().getResourceAsStream("/assets/images/logo.png"));
} catch (IOException e) {
e.printStackTrace();
}
// setIcon(new ImageIcon(logo));
setIconTextGap(0);
setBorder(null);
setText(null);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Functions.openWebPage(Config.website);
}
#Override
public void mouseEntered(MouseEvent e) {
Functions.brightnessControl(logo, .3F);
}
#Override
public void mouseExited(MouseEvent e) {
Functions.brightnessControl(logo, 1.0F);
}
});
setBounds(-5, 1, 247, 106);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(logo, -5, 1, null);
}
}
Functions.brightnessControl:
public static Image brightnessControl(Image image, float brightness) {
BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics bg = bi.getGraphics();
if (bi.getColorModel().hasAlpha()) {
System.out.println("Image has got an alpha channel");
}
bg.drawImage(image, 0, 0, null);
bg.dispose();
RescaleOp rescaleOp = new RescaleOp(brightness, 0, null);
rescaleOp.filter(bi, bi);
image = bi;
return bi;
}
Currently, it does nothing which confuses me. If anyone knows how this is achieved, please feel free too let me know :)
Ok, so the basic idea is, when the mouse enters the component, you want to generate a "bright" version of the component (I know, obvious, but bear with me)
The problem is, you have a Graphics context and you need a image :/
The following is not overly optimised, but without spending a lot of time adding in PropertyChange support, this will give you the basic idea.
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.setLayout(new GridBagLayout());
JLabel label = new BrightLabel();
label.setIcon((new ImageIcon(ImageIO.read(Main.class.getResource("background.png")))));
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class BrightLabel extends JLabel {
private MouseAdapter mouseHandler;
private boolean isHover = false;
#Override
public void addNotify() {
super.addNotify();
mouseHandler = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
isHover = true;
repaint();
}
#Override
public void mouseExited(MouseEvent e) {
isHover = false;
repaint();
}
};
System.out.println("...");
addMouseListener(mouseHandler);
}
#Override
public void removeNotify() {
super.removeNotify();
if (mouseHandler != null) {
removeMouseListener(mouseHandler);
}
mouseHandler = null;
}
#Override
protected void paintComponent(Graphics g) {
if (!isHover) {
super.paintComponent(g);
return;
}
BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
super.paintComponent(g2d);
g2d.dispose();
Image bright = brightnessControl(img, 1.3f);
g.drawImage(bright, 0, 0, this);
}
public Image brightnessControl(Image image, float brightness) {
BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics bg = bi.getGraphics();
if (bi.getColorModel().hasAlpha()) {
System.out.println("Image has got an alpha channel");
}
bg.drawImage(image, 0, 0, null);
bg.dispose();
RescaleOp rescaleOp = new RescaleOp(brightness, 0, null);
rescaleOp.filter(bi, bi);
image = bi;
return bi;
}
}
}
Essentially, when the mouse enters or exists the component, we set the isHover property. We then call repaint to trigger a new paint pass.
When the component is painted, if isHover is true, we take over the painting process slightly, generating our own buffer and painting the component to it. This gives us our base image. From there we brighten the image and paint it to the Graphics context
Arguments in java are passed by value so your the function you have to brighten the image doesn't have an effect on the image you passed to it.
You're missing the repaint() call after changing the image.
You definitely don't want to prepare the hover image every time you hover over the image. Pre-calculate the hover in the constructor and assign it to a separate image.
Create a member variable for if hover is true and use that in the paint method to figure out which image to paint.
The questions says it all really, I want the make the background of a JTextField semi-transparent and I am using a timer to make it flash.
So I have found that using the traditional textField.setBackground() yeilds a strange graphical glitch where with each flash the text field is darker than it should be. (see below)
strange graphical glitch
So after searching online I tried to override the paint method of the JTextField with the following code:
name = new JTextField(15) {
#Override
protected void paintComponent(Graphics g) {
g.setColor(this.getBackground());
g.fillRect(getX(), getY(), getWidth(), getHeight());
super.paintComponent(g);
}
};
Also, some recommended that I set the text field opaque boolean to false. This I did and to no avail, now there is not even any red flashing and I simply get this:
fields with field.setOpaque(false);
Just in case it helps, here is the code I am using to make the fields flash.
public void flashField(JTextField field, Color flashColor, final int flashDelay, final int numberOfFlashes) {
timers.add(new Timer(flashDelay, new ActionListener() {
int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
counter++;
if (counter % 2 == 0)
field.setBackground(
new Color(flashColor.getRed(), flashColor.getBlue(), flashColor.getGreen(), 50));
else
field.setBackground(Color.WHITE);
if (counter == (numberOfFlashes * 2) + 1) {
((Timer) e.getSource()).stop();
}
field.repaint();
}
}));
timers.get(timers.size() - 1).start();
}
So after searching online I tried to override the paint method
Also, some recommended that I set the text field opaque boolean to false.
Well you need to do both at the same time. Something like:
JPanel panel = new JPanel()
{
protected void paintComponent(Graphics g)
{
g.setColor( getBackground() );
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
panel.setOpaque(false); // background of parent will be painted first
panel.setBackground( new Color(255, 0, 0, 20) );
frame.add(panel);
Check out Background With Transparency for why this is necessary and for a simple reusable solution that you can use on any component without doing custom painting every time.
Here is an adapted code where Textfield keeps on flashing every 200ms and is transparent.
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransparentTextFieldExample {
public static void main(String[] args) {
new TransparentTextFieldExample();
}
public TransparentTextFieldExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
TransparentTextField ttf = new TransparentTextField("Some text!", 20);
panel.add(ttf);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
flashField(ttf, Color.RED, 1, 20);
}
});
}
public void flashField(JTextField field, java.awt.Color flashColor, final int flashDelay, final int numberOfFlashes) {
Timer tm = new Timer(flashDelay, new ActionListener() {
int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (counter % 2 == 0) {
field.setBackground(flashColor);
} else {
field.setBackground(Color.WHITE);
}
if (counter == (numberOfFlashes * 2) + 1) {
System.out.println("Inside");
//((Timer) e.getSource()).stop();
// break;
}
field.repaint();
try {
Thread.sleep(200l);
} catch (Exception ex) {
}
counter++;
}
});
tm.start();
}
public class TransparentTextField extends JTextField {
public TransparentTextField(String text) {
super(text);
init();
}
public TransparentTextField(int columns) {
super(columns);
init();
}
public TransparentTextField(String text, int columns) {
super(text, columns);
init();
}
protected void init() {
setOpaque(false);
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
super.paint(g2d);
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g2d);
g2d.dispose();
}
}
}
here's the relevant code from the form where JComponents should be drawn - this code creates and adds to the form a JComponent and also adds it to an ArrayList, as I need to be able to destroy the components later.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;
public class frmMain extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
/**
* Creates new form frmMain
*/
public frmMain() {
initComponents();
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
// Pocitadlo tmp = new Pocitadlo(100, 40, 40, getFont());
// tmp.addActionListener(this);
// this.add(tmp);
// tmp.start();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int val, x, y;
val = 20;
x = (this.getWidth() / 2) - 10 + rnd.nextInt(20);
y = (this.getHeight() / 2) - 10 + rnd.nextInt(20);
try{
val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(this);
poc.add(tmp);
tmp.setVisible(true);
tmp.start();
this.add(tmp);
this.setTitle("Počet počítadiel: " + this.getComponentCount());
repaint();
tmp.repaint();
} catch(Exception e){
JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
}
}
#Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("counterFinished")){
Pocitadlo tmp = (Pocitadlo)e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
this.remove(tmp);
}
}
and here is the code of the JComponents i'm adding and naiively expect to be drawn:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
import javax.swing.Timer;
public class Pocitadlo extends JComponent implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f){
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
public void start(){
isRunning = true;
valueTimer.start();
}
public void stop(){
isRunning = false;
valueTimer.stop();
}
#Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("valueTimer")){
value--;
if(actionListener != null){
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if(value == 0 && actionListener != null){
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
}
if(value < maxValue / 2 && !blinkTimer.isRunning()) blinkTimer.start();
}
if(e.getActionCommand().equals("blinkTimer")){
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener){
actionListener = listener;
}
#Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if(value > maxValue) value = maxValue;
repaint();
}
#Override
public void paintComponent(Graphics g){
g.setColor(Color.red);
g.fillRect(getX(), getY(), getWidth(), getHeight());
if(isDisplayed){
g.setColor(Color.green);
g.fillRect(getX(), getY(), getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
g.drawString(value + "/" + maxValue, 0, 0);
}
//super.paintComponent(g);
}
}
this results in nothing - the JComponents are created and correctly added at least to the ArrayList, and its paintComponent method run, as well as the timer events, components are also correctly removed at least from the ArrayList when the countdown reaches 0, but doesn't draw anything and I have no idea why.
please note that the commented code in frmMain constructor kind of works, and if uncommented, it draws the JComponent... well... just the rectangles, doesn't draw any text, but I suppose that's another problem, however, if you're willing to help with that one too, I'd be glad.
also, if I'm using action events and listeners incorrectly, please ignore that, I'm a beginner at Java and would like to know how to do it correctly, but later, right now I just need to get this code to work so please focus on the issue, thank you.
yes, there's some dead things, like the font passed to the component, those are remains of my attempts to get the drawString to work.
You project is suffering from a number of really basic misconceptions about how painting in Swing works.
Because (at the time I tested it) you hadn't provided the full code, I had to make some assumptions.
Your expection is that the content pane of the JFrame is using a null layout. I'd find it difficult to see how you would mange this and have other components on the frame. In any case, I would isolate a separate container to act as the "main" view for your Pocitadlo. This allows you to continue using layout managers to manage the other components as well.
I'm a little concerned that you trap the entire Pocitadlo generation code in a try-catch simply to catch a numeric conversion. Since you've already applied a default value, it would be reasonable to trap the conversion in its own try-catch and ignore it if it fails (displaying a message as required). Now I'm coming in from the outside, so you may see a need for this, it just caught my eye...
Your valueTimer actionPerformed code is slightly erronous, as it's possible for the blinkTimer to continue running even after the valueTimer has completed. I corrected this by merging two of you if statements into a if-else statement instead...
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
I, personally, would use a JPanel over a JComponent. JPanel is opaque to start with.
The main problem is in you paintComponent method.
You should always call super.paintComponent, and on opaque components it should be called first.
You seem to think you have to correct for the components position when painting, for example g.fillRect(getX(), getY(), getWidth(), getHeight());. This is actually wrong. The graphics context has already been translated so that the x/y position of the component/graphics is now equal to 0x0. Have a read through Painting in AWT and Swing for a better explanation, but basically, this means, the top/left corner of you paint area is now 0x0.
You seem to think that text is painted from the top/left corner of the first character. This is actually wrong. Text is painted along it's base line. You need to compenstate for this by adjusting the y-position by the Fonts ascent value.
.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestRepaint01 {
public static void main(String[] args) {
new TestRepaint01();
}
public TestRepaint01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
MainFrame frame = new MainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainFrame extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
private JPanel body;
/**
* Creates new form frmMain
*/
public MainFrame() {
setLayout(new BorderLayout());
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
body = new JPanel(null);
add(body);
JButton btn = new JButton("Add");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int val, x, y;
val = 20;
x = (getWidth() / 2) - 10 + rnd.nextInt(20);
y = (getHeight() / 2) - 10 + rnd.nextInt(20);
// try {
// val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(MainFrame.this);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
poc.add(tmp);
tmp.start();
body.add(tmp);
body.repaint();
setTitle("Počet počítadiel: " + getComponentCount());
// } catch (Exception e) {
// JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
// }
}
});
add(btn, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("counterFinished")) {
System.out.println("Counter finished");
Pocitadlo tmp = (Pocitadlo) e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
body.remove(tmp);
// body.revalidate();
body.repaint();
}
}
}
public class Pocitadlo extends JPanel implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f) {
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
#Override
public void removeNotify() {
super.removeNotify();
stop();
blinkTimer.stop();
}
public void start() {
isRunning = true;
valueTimer.start();
}
public void stop() {
isRunning = false;
valueTimer.stop();
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("valueTimer")) {
value--;
System.out.println("value = " + value);
if (actionListener != null) {
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
}
if (e.getActionCommand().equals("blinkTimer")) {
System.out.println("Blink");
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener) {
actionListener = listener;
}
#Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if (value > maxValue) {
value = maxValue;
}
repaint();
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
// location bad
g.fillRect(0, 0, getWidth(), getHeight());
if (isDisplayed) {
g.setColor(Color.green);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
FontMetrics fm = g.getFontMetrics();
g.drawString(value + "/" + maxValue, 0, fm.getAscent());
}
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
}
}
None of the problems you've encountered are super critical (sure, you program doesn't do what you want) and suggest that you've reached a pinnacle moment in your code abilities. I say this, because I made the exact same mistakes... ;)
Take a closer look at 2D Graphics and Custom Painting
I'd also take a look at Code Conventions for the Java Programming Language as you simply annoy people if you don't follow them ;)
I can't post any code right now, since the computer I'm programming on has no internet connection, and I absolutely refuse to write it out on this phone.
Basically, I have a JPanel (which implements mouseListener), which contains a Component in its contentPane. The JPanel is listening for mouse events on the Component.
When I draw to the panel, it works fine except that the area under the Component (which is visible but not painting anything) just shows the Panel's background (a standard colour fill) and not the image I drew on top of it.
I get the feeling that I'm missing something fundamental to do with mouseListeners...
OK, here's the whole class, now that my computer's working as intended again:
(Also, it seems I was using Labels, not Components. Sorry about that.)
import java.awt.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.util.*;
public class PictureViewer extends Container implements MouseListener, ComponentListener
{
java.util.List<Image> images;
public Component leftSide, rightSide;
int currentImage;
boolean leftMoused, rightMoused;
boolean mouseDown;
Image leftTab, rightTab, noImage;
public PictureViewer()
{
setVisible(true);
setBackground(Color.BLUE);
addComponentListener(this);
images = new ArrayList<Image>();
leftSide = new Label();
leftSide.setLocation(0, 0);
leftSide.setSize(getWidth() / 2, getHeight());
leftSide.addMouseListener(this);
add(leftSide);
rightSide = new Label();
rightSide.setLocation(getWidth() / 2, 0);
rightSide.setSize(getWidth() / 2, getHeight());
rightSide.addMouseListener(this);
rightSide.setVisible(false);
add(rightSide);
noImage = Toolkit.getDefaultToolkit().getImage(getClass().getResource("Images/No Picture.png"));
leftTab = Toolkit.getDefaultToolkit().getImage(getClass().getResource("Images/Left Tab.png"));
rightTab = Toolkit.getDefaultToolkit().getImage(getClass().getResource("Images/Right Tab.png"));
}
public void addImage(Image image)
{
images.add(image);
}
public void clear()
{
images.clear();
}
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
Image imageToDraw;
if (images.size() > 0)
{
imageToDraw = images.get(currentImage);
}
else
{
imageToDraw = noImage;
}
g2d.drawImage(imageToDraw, getX(), getY(), getWidth(), getHeight(), 0, 0, imageToDraw.getWidth(this), imageToDraw.getHeight(this), this);
g2d.draw(new Rectangle(0, 0, 20, 20));
if (leftMoused)
{
g2d.drawImage(leftTab, getX() + 8, getY() + (int)(getSize().getHeight() - leftTab.getHeight(this) / 2), this);
}
}
public void componentHidden(ComponentEvent e){}
public void componentShown(ComponentEvent e){}
public void componentMoved(ComponentEvent e)
{
componentResized(e);
}
public void componentResized(ComponentEvent e)
{
leftSide.setLocation(getLocation());
leftSide.setSize(getWidth() / 2, getHeight());
rightSide.setLocation((int)(getLocation().getX() + (getWidth() / 2)), (int)getLocation().getY());
rightSide.setSize(leftSide.getSize());
System.out.println(getSize());
repaint();
}
public void mouseReleased(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
public void mousePressed(MouseEvent e){}
public void mouseEntered(MouseEvent e)
{
if (e.getComponent() == leftSide){
leftMoused = true;
System.out.println("Left");}
else {
rightMoused = true;
System.out.println("Right");}
repaint();
}
public void mouseExited(MouseEvent e)
{
if (e.getComponent() == leftSide)
leftMoused = false;
else
rightMoused = false;
repaint();
}
}
The component will also have a paintComponent method which by default will paint the container's background. You will need to override the method, setOpaque to false (depending on the component), or something else to keep painting from happening.
However, it sounds like what you really want is to add mouse listener to the panel and have it listen to a defined boundary instead the panel rather than adding a component to the panel.
I can't post any code right now
You wrote custom code that doesn't work and you expect us to guess what that code looks like? We don't have time to spend guessing what mistake you might have made. The point of the forums is to make it easy for us to answer the question which means you need to provide all the information we need to help solve the problem.
That is why you need to provide a SSCCE that demonstrates the problem.
I'm trying to make an applet which I can simply drag an image. And I want image object to listen events. So here is the applet code which simple run in a thread:
import java.awt.*;
import java.net.URL;
import javax.swing.JApplet;
public class Client extends JApplet implements Runnable {
private static final long serialVersionUID = 1L;
MediaTracker mediaTracker;
Image [] imgArray;
Tas t1;
public void init()
{
mediaTracker = new MediaTracker(this);
imgArray = new Image[1];
URL base = getCodeBase();
imgArray[0] = getImage(base,"okey.png");
mediaTracker.addImage(imgArray[0],1);
try {
mediaTracker.waitForAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
t1 = new Tas(this, new Rectangle(0, 0, imgArray[0].getWidth(this), imgArray[0].getHeight(this)), imgArray[0]);
Thread t = new Thread(this);
t.start();
}
public void paint(Graphics g)
{
t1.paint(g);
}
#Override
public void run() {
while(true){
//System.out.println("run");
repaint();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
And the class of object which holds image is:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Movable extends JPanel implements MouseListener {
public Client mainObj;
public Rectangle rect;
public Image image;
public Movable(Client mainObj, Rectangle rect, Image image) {
this.mainObj = mainObj;
this.rect = rect;
this.image = image;
addMouseListener(this);
}
public void paint(Graphics g) {
g.drawImage(image, rect.x, rect.y, rect.width, rect.height, this);
}
#Override
public void mouseClicked(MouseEvent arg0) {
System.out.println("clicked");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
System.out.println("pressed");
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
#SuppressWarnings("serial")
class Tas extends Movable{
public String name = "";
public Tas(Client mainObj, Rectangle rect, Image image) {
super(mainObj, rect, image);
}
}
I can see the image in my applet but nothing happens when I click in or out of the image. So what's wrong with this code.
Assuming that Tas in code #1 is Moveable in code #2...
You don't actually use the Moveable as a Component, but instead ask it to paint itself onto the Applet's graphics context, here:
public void paint(Graphics g)
{
t1.paint(g);
}
Instead you should add an instance of Moveable onto the Applet's container, wherein painting will become automatic, and it will start to receive mouse events. You can also remove that paint() method then too.
First of all you should never override the paint method of a top level container (JApplet, JFrame, JDialog).
Then to do custom painting on other Swing components you override the paintComponent() method of the component, NOT the paint() method. Read the Swing tutorial on Custom Painting. So first fix those problems.
I'm not sure what the point of the Thread is but remove it from your code until you solve your other problems. If you are trying to do animation, then you should be using a Swing Timer, not a Thread.
If you want to see some code for dragging components you can take a look at Moving Windows for some generic code.
This is a working solution. It is not an applet but you can easily convert that. Hope it helps :
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class ImagePanel extends JPanel {
Image image;
Point2D axis = new Point2D.Double();
boolean drag = false;
Point2D dragPoint = new Point2D.Double();
public ImagePanel(Image image) {
this.image = image;
setPreferredSize(new Dimension(300,300));
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
drag = true;
dragPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
drag = false;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (drag) {
axis.setLocation(axis.getX()
+ (e.getPoint().x - dragPoint.getX()), axis.getY()
+ (e.getPoint().y - dragPoint.getY()));
dragPoint = e.getPoint();
repaint();
}
}
});
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(image, (int) axis.getX(), (int) axis.getY(), null);
}
public static void main(String[] args) {
try {
JFrame f = new JFrame();
f.getContentPane().add(
new ImagePanel(ImageIO.read(new File("image.jpg"))));
f.pack();
f.setVisible(true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
The simple answer is - you don't have code to do anything in mousePressed() or mouseReleased().
There are lots of other problems in the code though...
Simplest solution I could come up with -
public class Client extends JApplet {
private MouseInputAdapter myMouseListener = new MyMouseListener();
public void init() {
// usually a very bad idea, but needed here
// since you want to move things around manually
setLayout(null);
// assuming this will get used often, so making it a method.
addLabelForImage(getImage(getCodeBase(), "okay.png"));
}
private void addLabelForImage(Image image) {
ImageIcon icon = new ImageIcon(image);
JLabel l = new JLabel(icon);
add(l);
l.setSize(l.getPreferredSize());
// you'll probably want some way to calculate initial position
// of each label based on number of images, size of images,
// size of applet, etc. - just defaulting to 100,100 now.
l.setLocation(100, 100);
l.addMouseListener(myMouseListener);
l.addMouseMotionListener(myMouseListener);
}
// Made this a MouseInputAdapter because I assume you may want to handle
// other types of mouse events later...
private static class MyMouseListener extends MouseInputAdapter {
#Override
public void mouseDragged(MouseEvent e) {
// when the mouse is dragged over a the component this listener is
// attached to (ie - one of the labels) convert the point of the mouse
// event from the internal component coordinates (0,0 is upper right
// corner of each label), to it's parent's coordinates (0,0 is upper
// right corner of the applet), and set the components location to
// that point.
Component theLabel = e.getComponent();
Container theApplet = theLabel.getParent();
Point labelPoint = e.getPoint();
Point appletPoint = SwingUtilities.convertPoint(
theLabel, labelPoint, theApplet );
theLabel.setLocation(appletPoint);
}
}
}