putting marks on ImageIcon in JLabel - java

So I'm trying to find a way to modify an image in Java. In other words, if user clicks on the image, a mark will be put at the point where the user just clicked.
I have an ImageIcon which I put in a JLabel.
So far, the approach I took was to use JLayeredPanel to put another JPanel on top of the JLabel and draw on this JPanel:
//...
ImageIcon icon = new ImageIcon("foo.jpg");
JLabel lb = new JLabel(icon);
JPanel glass = new JPanel();
lb.setBounds(0, 0, 100, 100);
glass.setBounds(0, 0, 100, 100);
glass.setOpaque(false);
LayeredPane container = new LayeredPane();
container.add(lb, 1);
container.add(glass, 2);
//...
But this way doesn't seem to work. I never see the background image (the image in lb).
So I was wondering if I'm even on the right track at all? Or is there a cleaner way to achieve this?

There's nothing wrong with using a JLayeredPane or the glass pane for something like this, personally, I find it troublesome, because in a large application, you tend to want to use these layers for any number of things, so it becomes very complicated very fast.
I prefer to keep it "in the family" so to speak...
Personally, I would use a custom component. This isolates the work flow to a very particular location and makes it easier to provide the customisations that you might like...
public class MarkImage {
public static void main(String[] args) {
new MarkImage();
}
public MarkImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage background;
private List<Point> clickPoints;
public TestPane() {
clickPoints = new ArrayList<>(25);
try {
background = ImageIO.read(getClass().getResource("/Miho_Small.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clickPoints.add(e.getPoint());
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g.drawImage(background, x, y, this);
}
g.setColor(Color.RED);
for (Point p : clickPoints) {
g.fillOval(p.x - 4, p.y - 4, 8, 8);
}
}
}
}
I'd also consider using JXLayer (AKA JLayer in Java 7). This is best described as a glass pane for components (on steroids). Check out How to decorate components for more details...
Updated with JLayer Example
This is an example using Java 7's JLayer. There are some slight differences between JLayer and JXLayer, but it wouldn't take much to convert it...
(Sorry, couldn't resist the temptation of having ago)
public class MarkLayer {
public static void main(String[] args) {
new MarkLayer();
}
public MarkLayer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
try {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
JLabel label = new JLabel(new ImageIcon(ImageIO.read(getClass().getResource("/Miho_Small.png"))));
LayerUI<JLabel> layerUI = new MarkLayerUI();
JLayer<JLabel> layer = new JLayer<>(label, layerUI);
frame.add(layer);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
}
});
}
public class MarkLayerUI extends LayerUI<JLabel> {
private Map<JLayer, List<Point>> mapPoints;
public MarkLayerUI() {
mapPoints = new WeakHashMap<>(25);
}
#Override
public void installUI(JComponent c) {
System.out.println("install");
super.installUI(c);
JLayer layer = (JLayer) c;
layer.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK);
}
#Override
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
mapPoints.remove((JLayer) c);
}
#Override
protected void processMouseEvent(MouseEvent e, JLayer<? extends JLabel> l) {
if (e.getID() == MouseEvent.MOUSE_CLICKED) {
List<Point> points = mapPoints.get(l);
if (points == null) {
points = new ArrayList<>(25);
mapPoints.put(l, points);
}
Point p = e.getPoint();
p = SwingUtilities.convertPoint(e.getComponent(), p, l);
points.add(p);
l.repaint();
}
}
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2d = (Graphics2D) g.create();
super.paint(g2d, c);
g2d.setColor(Color.BLUE);
g2d.drawRect(0, 0, c.getWidth() - 1, c.getHeight() - 1);
List<Point> points = mapPoints.get((JLayer) c);
if (points != null && points.size() > 0) {
g2d.setColor(Color.RED);
for (Point p : points) {
g2d.fillOval(p.x - 4, p.y - 4, 8, 8);
}
}
g2d.dispose();
}
}
}
The blue border is renderer as part of the layer, this gives you a guide as to where you can click - I did this for testing and demonstration purposes

You're on the right track with wanting to use another pane. In Java, there actually already is a glass pane that is designed for just this purpose. Read through this tutorial http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html and it should help you understand.

Related

Java how to move a paint componenet [duplicate]

I'm trying to move the boat on the x-axis (without the keyboard yet). How can I relate the movement/ animation to the boat.png and not to any other image?
public class Mama extends Applet implements Runnable {
int width, height;
int x = 200;
int y = 200;
int dx = 1;
Image img1, img2, img3, img4;
#Override
public void init(){
setSize(627, 373);
Thread t = new Thread(this);
img1 = getImage(getCodeBase(),"Background.png");
img2 = getImage(getCodeBase(), "boat.png");
img3 = getImage(getCodeBase(), "LeftPalm.png");
img4 = getImage(getCodeBase(), "RightPalm.png");
}
#Override
public void paint(Graphics g){
g.drawImage(img1, 0, 0, this);
g.drawImage(img2, 200, 200, this);
g.drawImage(img3, 40, 100, this);
g.drawImage(img4, 450, 130, this);
}
#Override
public void run() {
while(true){
x += dx;
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
System.out.println("Thread generates an error.");
}
}
}
}
There are a few things that stand out...
The "Problem"
As has already been stated, you need to supply variable arguments to the image drawing process. g.drawImage(img2, x, y, this);, this will allow you define where the image should be painted.
While you've implemented Runnable, you've actually not started any threads to call it. This means, nothing is actually changing the variables.
In you start method, you should be calling something like new Thread(this).start().
Recommendations
Although you've tagged the question as Swing, you're using AWT components. This isn't recommended (in fact applets are generally discouraged as they are troublesome - IMHO). The other problem, as you are bound to find out shortly, is they are not double buffered, this generally leads to flickering when performing animation, which isn't desirable.
As a side note, it is also discouraged to override the paint method of top level containers like Applet. Top level containers tend to contain a number additional components, by overriding the paint method like this, you destroy this setup. Also, top level containers don't tend to be double buffered either.
The example below uses a JFrame, but it wouldn't take much to convert it to use a JApplet (just drop the AnimationPanel on to it. This is another reason why extending from top level containers is generally discouraged ;)
public class AnimatedBoat {
public static void main(String[] args) {
new AnimatedBoat();
}
public AnimatedBoat() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int xPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new File("boat.png"));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += direction;
if (xPos + boat.getWidth() > getWidth()) {
xPos = getWidth() - boat.getWidth();
direction *= -1;
} else if (xPos < 0) {
xPos = 0;
direction *= -1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int y = getHeight() - boat.getHeight();
g.drawImage(boat, xPos, y, this);
}
}
}
You need to replace g.drawImage(img2, 200, 200, this); with g.drawImage(img2, x, y, this);

XMotion dealing with JComponents [duplicate]

This question already has answers here:
How to move an image (animation)?
(2 answers)
Closed 6 years ago.
I am having trouble. I want to draw a rect onto my JPanel which is added to my JFrame contentPane. I want that x to be at a set pos but moving -x and restarting where +x begins. i.e. If I have a JPanel that is 800 x 400, I want the rext to take in those parameters but moving along the xaxis (x - Velx) repainting itself at 800 and continuing in the - x direction. I know this isn't sufficient info, none of my books that I have touch base on what I am trying to do so I lack proper terminology.
// Here is a good example of doing this
public class AnimatedBoat {
public static void main(String[] args) {
new AnimatedBoat();
}
public AnimatedBoat() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int xPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new File("boat.png"));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += direction;
// change directions off window width
if (xPos + boat.getWidth() > getWidth()) {
xPos = getWidth() - boat.getWidth();
direction *= -1;
} else if (xPos < 0) {
xPos = 0;
direction *= -1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int y = getHeight() - boat.getHeight();
g.drawImage(boat, xPos, y, this);
}
}
}

How to move an image (animation)?

I'm trying to move the boat on the x-axis (without the keyboard yet). How can I relate the movement/ animation to the boat.png and not to any other image?
public class Mama extends Applet implements Runnable {
int width, height;
int x = 200;
int y = 200;
int dx = 1;
Image img1, img2, img3, img4;
#Override
public void init(){
setSize(627, 373);
Thread t = new Thread(this);
img1 = getImage(getCodeBase(),"Background.png");
img2 = getImage(getCodeBase(), "boat.png");
img3 = getImage(getCodeBase(), "LeftPalm.png");
img4 = getImage(getCodeBase(), "RightPalm.png");
}
#Override
public void paint(Graphics g){
g.drawImage(img1, 0, 0, this);
g.drawImage(img2, 200, 200, this);
g.drawImage(img3, 40, 100, this);
g.drawImage(img4, 450, 130, this);
}
#Override
public void run() {
while(true){
x += dx;
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
System.out.println("Thread generates an error.");
}
}
}
}
There are a few things that stand out...
The "Problem"
As has already been stated, you need to supply variable arguments to the image drawing process. g.drawImage(img2, x, y, this);, this will allow you define where the image should be painted.
While you've implemented Runnable, you've actually not started any threads to call it. This means, nothing is actually changing the variables.
In you start method, you should be calling something like new Thread(this).start().
Recommendations
Although you've tagged the question as Swing, you're using AWT components. This isn't recommended (in fact applets are generally discouraged as they are troublesome - IMHO). The other problem, as you are bound to find out shortly, is they are not double buffered, this generally leads to flickering when performing animation, which isn't desirable.
As a side note, it is also discouraged to override the paint method of top level containers like Applet. Top level containers tend to contain a number additional components, by overriding the paint method like this, you destroy this setup. Also, top level containers don't tend to be double buffered either.
The example below uses a JFrame, but it wouldn't take much to convert it to use a JApplet (just drop the AnimationPanel on to it. This is another reason why extending from top level containers is generally discouraged ;)
public class AnimatedBoat {
public static void main(String[] args) {
new AnimatedBoat();
}
public AnimatedBoat() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int xPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new File("boat.png"));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += direction;
if (xPos + boat.getWidth() > getWidth()) {
xPos = getWidth() - boat.getWidth();
direction *= -1;
} else if (xPos < 0) {
xPos = 0;
direction *= -1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int y = getHeight() - boat.getHeight();
g.drawImage(boat, xPos, y, this);
}
}
}
You need to replace g.drawImage(img2, 200, 200, this); with g.drawImage(img2, x, y, this);

Add an Background image to a Panel

I have a JPanel, and i want to add an image as its background. How can i do that ?
frame = new JFrame("Some frame");
panel1 = new JPanel();
panel1.setBorder(new EmptyBorder(5, 5, 5, 5));
// NEED TO ADD AN IMAGE TO THIS PANEL
panel1.setLayout(cardlayout);
frame.getContentPane().add(panel1);
frame.setLocationByPlatform(true);
frame.setVisible(true);
I need to add an image to the panel and how can i do it ?
UPDATE 1
panel1 = new JPanel()
{
private static final long serialVersionUID = 1L;
#Override
public void paintComponent(Graphics g)
{
g.drawImage(Toolkit.getDefaultToolkit().createImage("1.jpg"), 0, 0, null);
}
};
You need to override the method paintComponent(Graphics g) of JPanel and use drawImage() on the Graphics object g as in this example.
Also, check these two examples by #trashgod:
example.
example.
You have a resource location problem.
Toolkit#createImage may return an empty image if the resource can not be found.
I suggest you use the ImageIO API instead, it supports a wider range of image formats, but will also throw an exception if the image is not found or can not be loaded.
How you load the image will also depend on where the image is.
If the image exists on the file system, you can simply use a File object reference, if the image is an embedded resource (within you application), you will need to use Class#getResource to obtain a URL to it.
public class TestGraphics {
public static void main(String[] args) {
new TestGraphics();
}
public TestGraphics() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new PaintTest());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintTest extends JPanel {
private BufferedImage image;
public PaintTest() {
setLayout(new BorderLayout());
try {
// Use this if the image exists within the file system
image = ImageIO.read(new File("/path/to/image/imageName.png"));
// Use this if the image is an embedded resource
// image = ImageIO.read(getClass().getResource("/path/to/resource/imageName.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return image == null ? super.getPreferredSize() : new Dimension (image.getWidth(), image.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight()- image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
}
}
}

Fast switching between two images?

I want to make an object to open mouth and close it using two images that switch quickly. I tried with a for loop but it lagged my game.
if(direction == Constant.UP){
ImageIcon i = new ImageIcon("src\\images\\pacman up.png");
image = i.getImage();
ImageIcon i2 = new ImageIcon("src\\images\\pacman left.png");
image = i2.getImage();
}
G.drawImage(image, x, y, 20,20,null);
Any animation in Swing needs to take into consideration the Event Dispatching Thread.
You should NEVER perform any action within the content of the EDT that may block it (such as loops or I/O) as this will prevent the EDT from (amongst other things) processing paint requests.
You should always use a surface capable of supporting double buffer, such as JPanel as this will help eliminate flickering
The following uses a javax.swing.Timer to switch between the two images...
public class TestPacMan {
public static void main(String[] args) {
new TestPacMan();
}
public TestPacMan() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PacManPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PacManPane extends JPanel {
private BufferedImage pacOpened;
private BufferedImage pacClosed;
private BufferedImage frame;
private boolean opened = true;
public PacManPane() {
try {
pacOpened = ImageIO.read(new File("PC-Closed.png"));
pacClosed = ImageIO.read(new File("PC-Opened.png"));
frame = pacOpened;
} catch (IOException exp) {
exp.printStackTrace();
}
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opened = !opened;
frame = opened ? pacOpened : pacClosed;
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (frame != null) {
int x = (getWidth() - frame.getWidth()) / 2;
int y = (getHeight() - frame.getHeight()) / 2;
g2d.drawImage(frame, x, y, this);
}
g2d.dispose();
}
}
}
don't create the icon each time. create the two images at startup and just switch back
and forth at runtime.
if(direction == Constant.UP){
image = open;
}else {
image = closed;
}
G.drawImage(image, x, y, 20,20,null);

Categories