I´m writing a program where images are displayed on a JPanel, but I´m having trouble replacing the existing image when I open a new one. Is there a easy way to remove the old image from the ImageIcon and replace with the new one? I thought that something like mp.remove(pic); would work here, but it says that it´s not supported for ImageIcon?
class MapPanel extends JPanel {
public MapPanel(String filename) {
if(mp == null) {
pic = new ImageIcon(filename);
int w = pic.getIconWidth();
int h = pic.getIconHeight();
setPreferredSize(new Dimension(w, h));
setMinimumSize(new Dimension(w, h));
setMaximumSize(new Dimension(w, h));
setLayout(null);
}
else { int confirm =
JOptionPane.showConfirmDialog(MapProgram.this, "Unsaved changes, " +
"do you really want to open a new map?",
"New map", JOptionPane.OK_CANCEL_OPTION);
if (confirm != JOptionPane.OK_OPTION)
return;
// Remove the current image and display the new one choosen
// from the JFileChooser.
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(pic.getImage(), 0, 0, this);
}
}
Why are you using ImageIcon at all? It appears you want to draw an Image in a JPanel, not sure why you are using ImageIcon. Use ImageIO.read() to read the file chosen by the file chooser as an BufferedImage. Set the image to a member variable, then in paintComponent() instead of pic.getImage(), use your member variable. After a file is chosen and you set your member variable to the new file call repaint().
https://docs.oracle.com/javase/8/docs/api/javax/imageio/ImageIO.html#read-java.io.File-
Also, setting preferredSize, min size, and max size may or may not do anything. It all depends on what layout manager the container you put this JPanel in is using.
Not sure if this will work for what you want to do but give it a try.
g.drawImage(new ImageIcon(filename).getImage(), 0, 0, this);
or just pass in an Image there.
Related
So, I am working on a project which requires switching between images. The images need to be in fullscreen mode. There seem to be 2 problems that I am facing.
First is with the image switching. When I switch between images, some images appear ok when I switch. Others dont seem to show up on the screen at all. I just seem to be getting an empty frame.
Second is that the right key seems to work everytime but just for my sanity, I have put system prints. The system outs dont seem to show up on the console but it switches images in the frame(Even though, I get an empty frame sometimes).
Any suggestions/solutions would be highly appreciated.
Note about the code: The draw strings that I have are for testing purposes of the coordinates. I am using an Eyetribe, so just to show where I am looking. The drawstring seems to work perfectly.
Also, I am calling switchImage very quickly, almost 22 times in a second. Could that be an issue? Although it makes me wonder why it works for some images and not for others.
UPDATE: The problem seems to be in g.drawImage. It doesnt seem to be drawing for some images but I can't seem to figure out why thats happening.
Currently this is my code for fullscreen images.
public void showFrame(){
//jL -> JLabel
//jF -> JFrame
//jP -> Panel
jF.setTitle("Test");
jF.setUndecorated(true);
jF.setResizable(false);
jF.setVisible(true);
Toolkit tk = Toolkit.getDefaultToolkit();
int xsize = (int)tk.getScreenSize().getWidth();
int ysize = (int)tk.getScreenSize().getHeight();
jF.setSize(xsize, ysize);
jF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
if(testImagesList != null){
img = new ImageIcon(testImagesList.get(0));
}
Image imag = img.getImage();
bi = new BufferedImage(1280, 800, BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
g.drawImage(imag, 0, 0, 1280, 800, null);
ImageIcon newIcon = new ImageIcon(bi);
jL.setIcon(newIcon);
jF.add(jL);
jP.add(jL);
jF.add(jP);
jF.validate();
}
To switch between images I am using a key listener. The keyListner calls the switch image code. Both are given below.
public void switchImage(ImageIcon image, JFrame jF, JLabel jL, JPanel jP, GazeData gazeData){
Image imag = image.getImage();
BufferedImage bi = new BufferedImage(1280, 800, BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
g.drawImage(imag, 0, 0, 1280, 800, null);
g.setColor(Color.black);
int x = (int) gazeData.smoothedCoordinates.x;
int y = (int) gazeData.smoothedCoordinates.y;
Font font = new Font("Verdana", Font.BOLD, 16);
g.setFont(font);
String text = "hello(" + gazeData.smoothedCoordinates.x + gazeData.smoothedCoordinates.y + ")";
g.drawString(text, x, y);
g.drawString("Fixed Coordinate at (400, 500)", 400, 500);
ImageIcon newIcon = new ImageIcon(bi);
jL.setIcon(newIcon);
jP.add(jL);
jF.validate();
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
// j.switchImage(image, jF, jL, jP, gazeData);
System.out.println("RightDetected");
imageIndex++;
ImageIcon newImage = new ImageIcon(imageList.get(imageIndex));
if(newImage != null){
this.currentImage = newImage;
System.out.println("IMAGE SWITCHED!!!! Current Image="+imageList.get(imageIndex));
j.switchImage(currentImage, j.jF, j.jL, j.jP, currentGazeData);
}
}
}
Probably won't fix your problem but you have incorrect Swing coding practices:
jL.setIcon(newIcon);
//jF.add(jL); // remove
jP.add(jL);
jF.add(jP);
jF.validate();
A Swing component can only have a single parent. First you add the label to the frame and the to the panel. So the label will only ever appear on the panel. Get rid of the first statement.
All the Swing components should be added to the frame BEFORE the frame is made valid. So get rid of the validate() statement and move the setVisible(...) to the bottom.
jL.setIcon(newIcon);
//jP.add(jL);
//jF.validate();
When you change a property of a Swing component the component is smart enough to repaint itself. All you need to do is change the icon of the label. There is no need to add the label back to the panel or validate() the frame.
Others dont seem to show up on the screen at all
Maybe the image isn't found?
The drawstring seems to work perfectly.
Instead of creating all the BufferedImages and doing custom painting you can just display a label on top your label.
So the basic code would be:
imageLabel = new JLabel();
imageLabel.setLayout( new FlowLayout() );
textLabel = new JLabel();
imageLabel.add(textLabel);
frame.add(imageLabel);
Note:
The imageLabel can be added directly to the frame. I don't see any reason for your panel.
To change the image you just invoke the setIcon() method on the imageLabel
To change the text your just invoked the setText() method on the textLabel
I need a certain image to be redrawn at different locations constantly as the program runs. So I set up a while loop that should move an image across the screen, but it just redraws the image on top of itself over and over again. What am I doing wrong? Is there a way to delete the old image before drawing it in a new location?
JFrame frame = buildFrame();
final BufferedImage image = ImageIO.read(new File("BeachRoad_double_size.png"));
JPanel pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int num = 0;
boolean fluff = true;
while (fluff == true) {
num = num + 1;
g.drawImage(image, num, 0, null);
if (num == 105) {
fluff = false;
}
}
}
};
frame.add(pane);
You can't code a loop in the paintComponent() method. The code will execute so fast that the image will only be painted in the final position, which in your case should be with an x position of 105.
Instead you need to use a Swing Timer to schedule the animation every 100 milliseconds or so. Then when the timer fires you update the x position and invoke repaint() on the panel. Read the Swing tutorial on Using Swing Timers for more information.
Putting a while loop inside a paintComponent method is not the way to do it. Instead, there should be some setup like the following:
...
final int num = 0;
final JPanel pane;
Timer timer = new Timer(10, new ActionListener() {
public void actionPerformed(ActionEvent e) {
num++;
pane.repaint();
}
});
pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, num, 0, null);
}
});
timer.start();
This will move the image ever 10 milliseconds, as specified in the Timer constructor.
This is a common issue people starting out in animation have, as I did. You can't 'remove an image' from the screen. However, you can repaint the entire screen, then redraw your image at a new location.
In psuedocode:
while (condition)
background(white); //or whatever color your background is
drawImage(x,y);
The code above clears the screen so it's safe for you to redraw your image. This effectively 'deletes' your image.
Edit: I didn't read your code, I just addressed your question. So other answers that fix your code are probably better than mine.
Inside my paintComponent() method, I have a drawRect() that paints the background of a jpanel. But because the jbutton is drawn on the screen before the paintComponent() method gets called, the jbutton is blocked out by the drawRect. Does anyone know how to fix this? My guess is to add the jbutton before repaint gets called, but I don't know how to do that?
Some code:
public Frame(){
add(new JButton());
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(0,0,screenwidth,screenheight); //paints the background with a color
//but blocks out the jbutton.
}
Now, firstly, I will tell you what you're doing wrong here -- JFrame is not a JComponent, and has no paintComponent for you to override. Your code will probably never be called. Aside from that, drawRect merely draws a rectangle -- it does not fill one.
However, I believe there is a proper way to do this.
Since you're using a JFrame, you should take advantage of the container's layered pane via JFrame.getLayeredPane.
A layered pane is a container with depth such that overlapping components can appear one on top of the other. General information about layered panes is in How to Use Layered Panes. This section discusses the particulars of how root panes use layered panes.
Root panes are covered in How to Use Root Panes, a part of the Java Tutorials. A layered pane is a child of the root pane, and a JFrame, as a top-level container, utilizes an underlying JRootPane.
Anyways, since you're interested in creating a background, see the following diagram for how a layered pane generally looks inside a top-level container:
The table below describes the intended use for each layer and lists the JLayeredPane constant that corresponds to each layer:
Layer Name - Value - Description
FRAME_CONTENT_LAYER - new Integer(-30000) - The root pane adds the menu bar and content pane to its layered pane at this depth.
Since we want to specify our background is behind the content, we first add it to the same layer (JLayeredPane.FRAME_CONTENT_LAYER), as follows:
final JComponent background = new JComponent() {
private final Dimension size = new Dimension(screenwidth, screenheight);
private Dimension determineSize() {
Insets insets = super.getInsets();
return size = new Dimension(screenwidth + insets.left + insets.right,
screenheight + insets.bottom + insets.top);
}
public Dimension getPreferredSize() {
return size == null ? determineSize() : size;
}
public Dimension getMinimumSize() {
return size == null ? determineSize() : size;
}
protected void paintComponent(final Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, screenwidth, screenheight);
}
};
final JLayeredPane layeredPane = frame.getLayeredPane();
layeredPane.add(background, JLayeredPane.FRAME_CONTENT_LAYER);
Now, to make sure we draw our background before the content, we use JLayeredPane.moveToBack:
layeredPane.moveToBack(background);
I did this really quick test. As HovercraftFullOfEels has pointed out. JFrame does not have a paintComponent, so I used a JPanel instead.
Which was produced by this code
public class PanelTest extends JPanel {
private JButton button;
public PanelTest() {
setLayout(new GridBagLayout());
button = new JButton("Can you see me ?");
add(button);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle bounds = button.getBounds();
bounds.x -= 10;
bounds.y -= 10;
bounds.width += 20;
bounds.height += 20;
g.setColor(Color.RED);
((Graphics2D)g).fill(bounds);
}
}
I've I try and replicate the issue by using paintComponents on the JFrame, I don't see the rectangle. Even if I overwrite paint on the JFrame, the rectangle is still painted under the button (Not that I would ever recommend doing either).
The problem is, you haven't given us enough code to know what's going wrong
ps - drawRect won't "fill" anything
I encountered this before, although not jframe specifically and not the kind of the scenario that you have. Try this code,
this.getContentPane.repaint();
on your jframe. Im not sure about this, but give it a try.
So, I have a grid layout which stores JScrollPane's in each cell. These are also put into an array for other purposes. The "View" extends "JPanel" so it's just a regular panel with image support. The application starts up with cell's filled with scrollPane's that contain the View which doesn't have a image yet.
At that point I see no scrollbar, but that doesn't matter since there is nothing inside the JPanel. As soon as I open an image and use drawImage in the paintComponenet of the JPanel I don't see scrollbar's showing up. Here's how I create the grid and the Scrollpane
private void createContentPane()
{
GridLayout gridLay = new GridLayout(GRID_ROWS, GRID_COLUMNS);
perspectiveTbl = new JScrollPane[NUM_PERSPECTIVE];
mainPane = new JPanel();
mainPane.setLayout(gridLay);
int idx = 0;
while(idx < perspectiveTbl.length)
{
perspectiveTbl[idx] = new JScrollPane(new View(modelImage));
mainPane.add(perspectiveTbl[idx]);
idx++;
}
this.getContentPane().add(mainPane, BorderLayout.CENTER);
}
I'm not exactly sure why the scrollbar's aren't showing up, should they have been set inside the panel for the image?
Here's an image of the application, as you can see the picture of the shoe does not receive scrollbar's so there is no way to view the rest of the picture:
Picture
You can either user not JPanel with image but usual JLabel with the image
or
call setPreferredSize() for the panels to reflect the image's size.
Thanks for the hint Stanislav, I actually figured it out and got it working an hour ago, but you did give the right path to fix it with the preferredSize attribute. I ended up re-implemented getPreferredSize with the size of the image inside the panel, added revalidate to the paint event so that the bars show up as soon as the image is loaded.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
this.getViewScrollPane().revalidate();
}
public Dimension getPreferredSize()
{
if(image != null)
return new Dimension(image.getWidth(),image.getHeight());
else
return super.getPreferredSize();
}
I'm attempting to create my own custom ToolTip for a program I've ported to Java, I'm trying to match the original programs ToolTips (it was written in Delphi). I've got some code that allows me to draw "inside" a ToolTip but for some reason, if I Override paint(Graphics g, JComponent c) and leave it blank it still draws a ToolTip, and anything I attempt to draw will be drawn "inside" this little boxed ToolTip and I can't draw "outside" of it.
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicToolTipUI;
class MultiLineToolTipUI extends BasicToolTipUI {
#Override
public void paint(Graphics g, JComponent c) {
//int mY = 0;
//int mX = 0;
//int xPoints[] = {mX, mX, mX + 15};
//int yPoints[] = {mY, mY + 25, mY + 25};
//Polygon p = new Polygon(xPoints, yPoints, 3);
//g.setColor(Color.BLACK);
//g.fillPolygon(p);
//g.fillRoundRect(mX, mY, 100, 50, 30, 30);
}
public static void main (String args[])
{
JButton button = new JButton ("Mouse Over ME!") {
public JToolTip createToolTip() {
MultiLineToolTip tip = new MultiLineToolTip();
tip.setComponent(this);
return tip;
}
};
//JButton button = new JButton("Mouse Over ME!");
button.setToolTipText("Hello, World");
JFrame frame = new JFrame("Basic SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( button );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
static class MultiLineToolTip extends JToolTip
{
public MultiLineToolTip()
{
setUI(new MultiLineToolTipUI());
}
}
}
This is what I have, and like I said, even without the lines in paint it's drawing on it's own.
Any advice to get around this? Or a better way to go about what I'm trying to do?
CLARIFICATION: The first answer by Camickr was very helpful, however I should clarify my goal is to get the box that is automatically drawn from somewhere outside of paint to go away, or be invisible.
The size of the tool tip is determined by the text. Add:
System.out.println( c.getSize() );
to the paint(...) method to see the size of the tool tip. You are trying to paint outside its bounds. If you want to override the default size then you need to set the preferred size yourself. I think you want:
public Dimension getPreferredSize(JComponent c)
{
return new Dimension(100, 50);
}
Edit: A tooltip is a component with a border and a background. If you want to remove them you can use:
tip.setOpaque(false);
tip.setBorder(new javax.swing.border.EmptyBorder(0, 0, 0, 0));
This will only work when to tooltip is fully contained withing the frame. When the tooltip is displayed outside the bounds of the frame, then the tooltip is added to a JWindow, in which case you will see the background of the window. I don't know how to disable this behaviour because all Swing components must be painted within the bounds of a top level container.