I set my background with
static JLabel board = new JLabel(new ImageIcon("img/rsz_board.png"));
frame.setContentPane(board);
And I tried to change the background with a button by doing this:
static JLabel board2 = new JLabel(new ImageIcon("img/board.png"));
JButton button2 = new JButton("Test");
button2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
frame.setContentPane(board2);
}
});
When I click the button, nothing will happen. If I click the button and then resize the window by dragging it with my cursor, the background will change but all my buttons will disappear. What am I doing wrong?
I think you should use revalidate() and repaint() after changing the background.
try this:
static JLabel board2 = new JLabel(new ImageIcon("img/board.png"));
JButton button2 = new JButton("Test");
button2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
frame.setContentPane(board2);
frame.revalidate();
frame.repaint();
}
});
If I click the button and then resize the window by dragging it with my cursor, the background will change but all my buttons will disappear
That is because all the buttons have been added to the "board" component not the "board2" component.
I tried to change the background with a button by doing this:
Don't change the label that is being used as the content pane. Instead change the Icon of the label:
//frame.setContentPane(board2);
board.setIcon( new ImageIcon("img/board.png") );
Perhaps a better way to do what you want: create a JPanel that draws the image in the background within its paintComponent method, that has its own layout manager which helps allow you to add components to the JPanel in any fashion you deem appropriate. You would give this class an Image field, and then within the paintComponent method, draw whatever image is being referenced by that field. Or if you want to swap a collection of images, give it an ArrayList<Image> field (below called images), and then draw the current image that is referenced by an index to that list (in my code below called imageIndex).
Comments on your code:
You're using static variables for some Swing components, and that suggests that you should re-think your design. Only declare static that which makes sense being static, and Swing GUI components are almost never in that category.
When you use a JLabel as you're using it, the label always sizes to the image and the text it holds (if any) which is OK for some applications, but dangerous for others. A JPanel will set its preferred size to that of the components it holds depending on the layout. That all changes of course if you explicitly change its getPreferredSize() method as I have done.
For example, compile and run the complete program code below:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class ChangeBackground extends JPanel {
public static final String ROOT_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/";
public static final String[] IMG_PATHS = {
"0/01/Sundomecloseup.JPG/640px-Sundomecloseup.JPG",
"3/31/Hanalei,_Kauai_HI.JPG/640px-Hanalei,_Kauai_HI.JPG",
"a/a3/Castle_of_Vajdahunyad.jpg/640px-Castle_of_Vajdahunyad.jpg",
"d/d6/HeratFridayMosque.jpg/640px-HeratFridayMosque.jpg",
"1/16/Hebridean_ram.jpg/640px-Hebridean_ram.jpg",
"1/11/Ouagadougou_Maison_du_peuple.jpg/640px-Ouagadougou_Maison_du_peuple.jpg",
"9/96/Menger-Schwamm-einfarbig.jpg/640px-Menger-Schwamm-einfarbig.jpg",
"4/4f/Olympias.1.JPG/640px-Olympias.1.JPG",
"1/18/Uscapitolindaylight.jpg/640px-Uscapitolindaylight.jpg",
"9/9a/Below_Golden_Gate_Bridge.jpeg/640px-Below_Golden_Gate_Bridge.jpeg",
"2/29/Eiffel_Tower_(2962488972).jpg/640px-Eiffel_Tower_(2962488972).jpg",
"8/8f/Notre-Dame_Cathedral_Basilica.jpg/640px-Notre-Dame_Cathedral_Basilica.jpg"
};
private static final int PREF_W = 640;
private static final int PREF_H = 480;
private List<Image> images = new ArrayList<>();
private int imageIndex = 0;
public ChangeBackground(List<Image> images) {
this.images = images;
add(new JButton(new NextImageAction("Next Image")));
}
public void nextImage() {
imageIndex++;
imageIndex %= images.size();
repaint();
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class NextImageAction extends AbstractAction {
public NextImageAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent arg0) {
nextImage();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(images.get(imageIndex), 0, 0, this);
}
private static void createAndShowGui(final List<Image> images) {
ChangeBackground mainPanel = new ChangeBackground(images);
JFrame frame = new JFrame("ChangeBackground");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
final List<Image> imgs = new ArrayList<>();
for (String imagePath : IMG_PATHS) {
imagePath = ROOT_PATH + imagePath;
try {
URL imgUrl = new URL(imagePath);
imgs.add(ImageIO.read(imgUrl));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
SwingUtilities.invokeLater(() -> createAndShowGui(imgs));
}
}
Related
Hello first of all when I run the program a button appear , when I press the button the image will go from top to down.
I try the code when the image go from top to down , it work very well
BUT when I put all the codes together there is an error in ( frame.add(new AnimationPane() ); )
Question : How to add AnimationPane() to the frame ???
because this is my problem.
The idea that I want to make two scenes , the first one have a button to make go to the second scene which will have an image (it must be pushed from top until reach down ).
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package maincontentpaneswitching;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching {
private static class ChangeContentPaneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// I want to put the image here
JPanel newFrameContents = new JPanel(); //Uses FlowLayout by default.
newFrameContents.add(new JLabel("You have successfully changed the content pane of the frame!", JLabel.CENTER));
/*We assume that the source is a JButton and that the Window is of type JFrame, hence
the following utility method call is possible without letting any errors appear:*/
JFrame frame = (JFrame) SwingUtilities.getWindowAncestor((JButton) e.getSource());
frame.setSize(600, 300);
frame.setContentPane(newFrameContents); //Change the content pane of the frame.
frame.revalidate(); //Notify the frame that the component hierarchy has changed.
frame.add(new AnimationPane() );
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.repaint(); //Repaint frame with all its contents.
}
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int yPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new URL("https://i.stack.imgur.com/memI0.png"));
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += direction;
if (yPos + boat.getHeight() > getHeight()) {
yPos = getHeight() - boat.getHeight();
direction *= +1;
} else if (yPos < 0) {
yPos = 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.getHeight()*2 , boat.getWidth() *2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() - boat.getWidth();
g.drawImage(boat, x, yPos, this);
}
}
private static class MainRunnable implements Runnable {
#Override
public void run() {
JButton changeContentPaneButton = new JButton("Click to go to the next image!");
changeContentPaneButton.addActionListener(new ChangeContentPaneListener());
JPanel frameContents = new JPanel(); //Uses FlowLayout by default.
frameContents.add(changeContentPaneButton);
JFrame frame = new JFrame("My application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Tells the frame that when the user closes it, it must terminate the application.
frame.setContentPane(frameContents); //Add contents to the frame.
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.setVisible(true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new MainRunnable()); //Swing code must always be used in the Event Dispatch Thread.
}
}
Introduction
As I said in my comment, I couldn't get the image animation to work properly. At least this code would give you a solid foundation to start with.
Here's the GUI I came up with.
Here's the GUI after you left-click on the button.
If you're going to add comments to your code, put the comments on separate lines from the code. Not everyone has a large monitor and can read 200+ character lines of code.
Explanation
Oracle has a rad tutorial, Creating a GUI With Swing. Skip the Netbeans section.
When I create a Swing GUI, I use the model/view/controller (MVC) pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
In Swing, the MVC pattern means:
The view reads information from the model
The view may not update the model
The controller updates the model and repaints/revalidates the view.
There's usually not one controller to "rule them all". Each listener controls its portion of the model and the view.
When I put together an application, I code one tiny tiny piece of it, then run tests. I probably ran two to three dozen tests, and this was mostly coded by you.
Model
I created a BoatImage class to read the boat image. It's a separate class, so I can read the image before I start to construct the GUI.
View
I created a JFrame. I created a main JPanel with a CardLayout.
I use a CardLayout to layout the button JPanel and the image JPanel. This way, the JFrame is not constantly changing size.
I create the JFrame and JPanels as separate methods/classes. This makes it much easier for people, including yourself, to read and understand the view code.
Controller
I coded the ChangeContentPaneListener to change from the button JPanel to the image JPanel. This is where you would put your image animation code.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post this code as one block.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching implements Runnable {
public static void main(String[] args) {
// Swing code must always be used in the Event Dispatch Thread.
SwingUtilities.invokeLater(new MainContentPaneSwitching());
}
private AnimationPane animationPane;
private BoatImage boatImage;
private CardLayout cardLayout;
private JPanel mainPanel;
public MainContentPaneSwitching() {
this.boatImage = new BoatImage();
}
#Override
public void run() {
JFrame frame = new JFrame("My application");
// Tells the frame that when the user closes it, it
// must terminate the application.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.mainPanel = createMainPanel();
frame.add(mainPanel, BorderLayout.CENTER);
// Resize the frame as necessary in order to fit as many contents
// as possible in the screen.
frame.pack();
// Place the frame in the center of the screen. As you can tell, this
// needs its size to calculate the location, so we made sure in the
// previous line of code that it is set.
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel() {
cardLayout = new CardLayout();
JPanel panel = new JPanel(cardLayout);
panel.add(createButtonPanel(), "button");
animationPane = new AnimationPane(boatImage);
panel.add(animationPane, "image");
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton changeContentPaneButton = new JButton(
"Click to go to the next image!");
changeContentPaneButton.addActionListener(
new ChangeContentPaneListener(this, boatImage));
panel.add(changeContentPaneButton);
return panel;
}
public JPanel getAnimationPane() {
return animationPane;
}
public void repaint() {
animationPane.repaint();
}
public class AnimationPane extends JPanel {
private static final long serialVersionUID = 1L;
private BoatImage boat;
public AnimationPane(BoatImage boat) {
this.boat = boat;
BufferedImage image = boat.getBoat();
this.setPreferredSize(new Dimension(image.getWidth(),
image.getHeight()));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = boat.getBoat();
int x = getWidth() - image.getWidth();
g.drawImage(image, x, boat.getyPos(), this);
}
}
private class ChangeContentPaneListener implements ActionListener {
private int direction, yPos;
private final MainContentPaneSwitching view;
private final BoatImage model;
public ChangeContentPaneListener(MainContentPaneSwitching view,
BoatImage model) {
this.view = view;
this.model = model;
this.direction = 1;
this.yPos = 0;
}
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(mainPanel, "image");
}
}
public class BoatImage {
private int yPos;
private BufferedImage boat;
public BoatImage() {
try {
URL url = new URL("https://i.stack.imgur.com/memI0.png");
boat = ImageIO.read(url); // boat.jpg
} catch (MalformedURLException e) {
e.printStackTrace();
boat = null;
} catch (IOException e) {
e.printStackTrace();
boat = null;
}
this.yPos = 0;
}
public BufferedImage getBoat() {
return boat;
}
public void setyPos(int yPos) {
this.yPos = yPos;
}
public int getyPos() {
return yPos;
}
}
}
I am trying to get the RGB value of a pixel of an image. (The pixel that the mouse is over). Here is the code which adds the image and the code which attends to the mouse ::
/// I got this code somewhere from StackOverFlow;
///Couldn't find the link;
label = new JLabel();
try {
// THIS PICTURE IS NOT MINE
targetImg = ImageIO.read(new java.net.URL("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFAhy8GM1F3_TyPjG7ezbeFRUy00PN4hpJCA&usqp=CAU"));
label.setIcon(new ImageIcon(rescale(targetImg)));
panel1.add(label);
fields = new JPanel();
fields.setBorder(new EmptyBorder(5, 5, 5, 5));
red = new JTextField(3);
green = new JTextField(3);
blue = new JTextField(3);
red.setEditable(false);
green.setEditable(false);
blue.setEditable(false);
fields.add(red);
fields.add(green);
fields.add(blue);
panel1.add(fields);
label.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
try{
int packedInt = targetImg.getRGB(e.getX(), e.getY());
Color color = new Color(packedInt, true);
fields.setBackground(color);
red.setText(Integer.toString(color.getRed()));
green.setText(Integer.toString(color.getGreen()));
blue.setText(Integer.toString(color.getBlue()));
}
catch (ArrayIndexOutOfBoundsException exception) { }
}
});
} catch (IOException ex) {
ex.printStackTrace();
}
This runs and compiles fine; However, sometimes the color returned by the RGB is not the color my mouse is on; Here is a pic ::
https://i.stack.imgur.com/R5qIx.png
Note: The white cross is an approximate location of where it is; A screenshot apparently does not take the mouse too.
The code is also laggy. Sometimes it hangs on one pixel and I have to wait until it runs again.
Why does this happen, and what can I do to prevent this? How can I solve this problem?
Since you only provided a code snippet, I created the following GUI.
Since the picture was so small, I put it under the RGB display.
The main problem was matching coordinate systems so that the mouse pointer would point to the pixel in the image. When you put the image inside a JLabel, you have to translate the mouse coordinate to an image coordinate.
I sidestepped the coordinate system problem by creating a drawing JPanel. By drawing the image on a drawing JPanel and adding the MouseMotionListener to the drawing JPanel, the mouse coordinates match the image coordinates.
I started the Swing application with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a JFrame, an RGB JPanel, and a drawing JPanel. The JFrame methods must be executed in a specific order. This is the order I use for all my Swing applications.
The RGB JPanel uses a FlowLayout to separate the red, green, and blue JTextFields.
The drawing JPanel draws the image. The drawing JPanel is the same size as the image. Again, by drawing the image on a drawing JPanel and adding the MouseMotionListener to the drawing JPanel, the mouse coordinates match the image coordinates.
I made the ColorListener a separate class. I basically copied your code to the mouseMoved method.
Here's the complete runnable code. I made the classes inner classes so I could paste this code as one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class RGBProcessor implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RGBProcessor());
}
private BufferedImage image;
private Color panelColor;
private DrawingPanel drawingPanel;
private JPanel rgbPanel;
private JTextField redField;
private JTextField greenField;
private JTextField blueField;
public RGBProcessor() {
this.panelColor = Color.RED;
this.image = getImage();
this.drawingPanel = new DrawingPanel(this, image);
this.rgbPanel = createRGBPanel();
}
#Override
public void run() {
JFrame frame = new JFrame("RGB Processor");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(rgbPanel, BorderLayout.BEFORE_FIRST_LINE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createRGBPanel() {
JPanel panel = new JPanel();
panel.setBackground(panelColor);
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
redField = new JTextField(4);
redField.setEditable(false);
panel.add(redField);
greenField = new JTextField(4);
greenField.setEditable(false);
panel.add(greenField);
blueField = new JTextField(4);
blueField.setEditable(false);
panel.add(blueField);
return panel;
}
private BufferedImage getImage() {
try {
return ImageIO.read(new URL("https://encrypted-tbn0.gstatic.com/"
+ "images?q=tbn:ANd9GcSFAhy8GM1F3_TyPjG7ezbe"
+ "FRUy00PN4hpJCA&usqp=CAU"));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public void setPanelColor(Color color) {
this.panelColor = color;
this.rgbPanel.setBackground(color);
}
public JTextField getRedField() {
return redField;
}
public JTextField getGreenField() {
return greenField;
}
public JTextField getBlueField() {
return blueField;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private BufferedImage image;
public DrawingPanel(RGBProcessor frame, BufferedImage image) {
this.image = image;
this.setPreferredSize(new Dimension(image.getWidth(),
image.getHeight()));
this.addMouseMotionListener(new ColorListener(frame, image));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
public class ColorListener extends MouseAdapter {
private RGBProcessor frame;
private BufferedImage image;
public ColorListener(RGBProcessor frame, BufferedImage image) {
this.frame = frame;
this.image = image;
}
#Override
public void mouseMoved(MouseEvent event) {
int packedInt = image.getRGB(event.getX(), event.getY());
Color color = new Color(packedInt, true);
frame.setPanelColor(color);
frame.getRedField().setText(Integer.toString(color.getRed()));
frame.getGreenField().setText(Integer.toString(color.getGreen()));
frame.getBlueField().setText(Integer.toString(color.getBlue()));
}
}
}
A gif that I tried to put into a JPanel isn't showing up after clicking the button that triggers it until I resize the window. When it does show up, it does not fit the JPanel and is not animated. I looked at several posts that dealt with this but I don't understand how to use them in my case.
/*
* Author: Raymo111
* Date: 13/04/2018
* Description: Wishes happy birthday to a special someone
*/
//Imports java GUI classes
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
// Main class with JFrame and ActionListener enabled
public class Happy_Birthday_GUI extends JFrame implements ActionListener {
// Class variables
private static JButton startButton = new JButton("CLICK TO START");
private static JPanel startPanel = new JPanel(), gifPanel = new JPanel();
private static Color blue = new Color(126, 192, 238), pink = new Color(255, 192, 203);
private static GridLayout grid1 = new GridLayout(1, 1);
// Constructor
public Happy_Birthday_GUI() {
// Initial screen
startButton.addActionListener(this);
startButton.setFont(new Font("Comic Sans MS", Font.PLAIN, 50));
startPanel.setLayout(grid1);
startPanel.add(startButton);
startPanel.setBorder(BorderFactory.createLineBorder(blue, 100));
startButton.setBackground(pink);
getContentPane().add(startPanel);
// Sets title, size, layout (grid 1x1), and location of GUI window (center)
setTitle("Happy Birthday from Dolphin");
setSize(840, 840);
setLayout(grid1);
setLocationRelativeTo(null);
setVisible(true);
}
// Main method
public static void main(String[] args) {
new Happy_Birthday_GUI();
}
// Action Performed method
public void actionPerformed(ActionEvent event) {
// Proceed to gif and song
if (startButton == event.getSource()) {
getContentPane().removeAll();
BufferedImage dolphin;
gifPanel.setLayout(grid1);
gifPanel.setBorder(BorderFactory.createLineBorder(pink, 100));
try {
dolphin = ImageIO.read(new File("C:\\Users\\raymo\\Pictures\\dolphin.gif"));
JLabel gifLabel = new JLabel(new ImageIcon(dolphin));
gifPanel.add(gifLabel);
} catch (IOException e) {
e.printStackTrace();
}
getContentPane().add(gifPanel);
}
}
}
Here is dolphin.gif. It's cute.
How do I get it to show up immediately after clicking the start button as an animated gif that fits the JPanel? Thanks in advance.
BufferedImage doesn't support painting animated Gifs, instead, you'll need to make use of Image (or preferably, ImageIcon).
This could then be applied directly to a JLabel, which will perform the animation operation itself.
animated gif that fits he JPanel?
Okay, that's a much more complex problem. One approach would be to convert the Gif to the required size, but needless to say, that's very, very complex.
A simpler solution might be to use a AffineTransform and scale the image to meet the requirements of the component itself. This would require a custom component, capable of calculating the scale and painting each frame of the image.
Luckily for you, JPanel is an ImageObserver, this means it's capable of painting the gif animation
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
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);
}
});
}
public class TestPane extends JPanel {
private ImageIcon image;
public TestPane() {
image = new ImageIcon("/Users/swhitehead/Downloads/NbENe.gif");
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
#Override
protected void paintComponent(Graphics g) {
int imageWidth = image.getIconWidth();
int imageHeight = image.getIconHeight();
if (imageWidth == 0 || imageHeight == 0) {
return;
}
double widthScale = (double)getWidth() / (double)imageWidth;
double heightScale = (double)getHeight() / (double)imageHeight;
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(image.getImage(), AffineTransform.getScaleInstance(widthScale, heightScale), this);
g2d.dispose();
}
}
}
I tried to put into a JPanel isn't showing up after clicking the button
When you add (or remove) components from a visible GUI the basic code is:
panel.add(...);
panel.revalidate();
panel.repaint();
The revalidate() is need to invoke the layout manager so the component is given a size.
is not animated.
Use a JLabel with an ImageIcon to display images. A JLabel will animated the gif.
When it does show up, it does not fit the JPanel and
You can try the Stretch Icon which is designed to fill the space available to the label.
I ended up doing:
gifPanel.add(new TestPane());
getContentPane().add(gifPanel);
revalidate();
repaint();
using camickr's revalidate and repaint, and MadProgrammer's TestPane class,
which worked very well to get the gif to animate, resize correctly and display immediately.
I want to paint an icon when user's input is invalid. I've found an example by Oracle and modified it for my purposes. The painting of Icon works correctly but when I change the value to correct the icon goes not completly invisible: the part which is drawn over the JPanel is still displayed.
Here is my code:
import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.text.NumberFormat;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.plaf.LayerUI;
public class FieldValidator extends JPanel {
private static final int ICON_SIZE = 12;
private static final Icon ICON = createResizedIcon((ImageIcon) UIManager.getIcon("OptionPane.errorIcon"));
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createUI();
}
});
}
public static void createUI() {
final JFrame f = new JFrame ("FieldValidator");
final JComponent content = createContent();
f.add (content);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo (null);
f.setVisible (true);
}
private static JComponent createContent() {
final LayerUI<JPanel> panelUI = new ValidationLayerUI();
// Number field.
final JLabel numberLabel = new JLabel("Number:");
final NumberFormat numberFormat = NumberFormat.getInstance();
final JFormattedTextField numberField = new JFormattedTextField(numberFormat) {
/**
* {#inheritDoc}
*/
#Override
public void replaceSelection(String content) {
super.replaceSelection(content);
getParent().repaint();
}
};
numberField.setColumns(16);
numberField.setFocusLostBehavior(JFormattedTextField.PERSIST);
numberField.setValue(42);
final int i = (ICON_SIZE / 2) + (ICON_SIZE % 2);
final JPanel numberPanel = new JPanel();
numberPanel.add(numberLabel);
final JPanel panel = new JPanel(new GridBagLayout());
final GridBagConstraints constr = new GridBagConstraints();
constr.insets = new Insets(i, i, i, i);
constr.weightx = 1;
constr.weighty = 1;
constr.fill = GridBagConstraints.BOTH;
panel.add(numberField, constr);
numberPanel.add(new JLayer<JPanel>(panel, panelUI));
return numberPanel;
}
//Icon resized to 12x12
private static Icon createResizedIcon(ImageIcon anIcon) {
final BufferedImage result = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g = result.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.drawImage(anIcon.getImage(), 0, 0, ICON_SIZE, ICON_SIZE, null);
g.dispose();
return new ImageIcon(result);
}
static class ValidationLayerUI extends LayerUI<JPanel> {
#Override
public void paint (Graphics g, JComponent c) {
super.paint (g, c);
final JLayer jlayer = (JLayer) c;
final JPanel panel = (JPanel) jlayer.getView();
final JFormattedTextField ftf = (JFormattedTextField) panel.getComponent(0);
if (!ftf.isEditValid()) {
ICON.paintIcon(panel, g, 0, panel.getHeight() - ICON.getIconHeight());
}
}
}
}
Here is the screens:
Initial all is correct
When I paint invalid Icon all is still correct
But when the value goes correct only the text field will be repainted
How can I force the JPanel to repaint???
P.S. I've already found an approach with JLayeredPane which works correct, but I want to know what is wrong in my code?
How about using the DocumentListener:
numberField.getDocument().addDocumentListener(new DocumentListener() {
#Override public void insertUpdate(DocumentEvent e) {
//Container c = numberField.getParent();
Container c = SwingUtilities.getUnwrappedParent(numberField);
if (c != null) {
c.repaint();
}
}
#Override public void removeUpdate(DocumentEvent e) {
insertUpdate(e);
}
#Override public void changedUpdate(DocumentEvent e) {}
});
Edit
Quote from this link: Painting in AWT and Swing
The RepaintManager The purpose of Swing's RepaintManager class is to
maximize the efficiency of repaint processing on a Swing containment
hierarchy, and also to implement Swing's 'revalidation' mechanism (the
latter will be a subject for a separate article). It implements the
repaint mechanism by intercepting all repaint requests on Swing
components (so they are no longer processed by the AWT) and
maintaining its own state on what needs to be updated (known as "dirty
regions"). Finally, it uses invokeLater() to process the pending
requests on the event dispatching thread, as described in the section
on "Repaint Processing" (option B).
In this case the parent JPanel is not dirty region when isEditValid() status changed. so remaining previous Icon paint.
I want to set the location of a JPopupMenu depending of the y location of the button that opens the menu. My code works fine on my first monitor, but fails on my second monitor, wich has a different height.
The problem is getLocationOnScreen() delivers the location relative to the main screen, not the actual screen on which the component is shown.
My code:
// screenSize represents the size of the screen where the button is
// currently showing
final Rectangle screenSize = dateButton.getGraphicsConfiguration().getBounds();
final int yScreen = screenSize.height;
int preferredY;
// getLocationOnScreen does always give the relative position to the main screen
if (getLocationOnScreen().y + dateButton.getHeight() + datePopup.getPreferredSize().height > yScreen) {
preferredY = -datePopup.getPreferredSize().height;
} else {
preferredY = getPreferredSize().height;
}
datePopup.show(DateSpinner.this, 0, preferredY);
How can I get the location of a component on its actual monitor?
I got a solution for this using the bounds of the second screen, it's quite simple:
public static Point getLocationOnCurrentScreen(final Component c) {
final Point relativeLocation = c.getLocationOnScreen();
final Rectangle currentScreenBounds = c.getGraphicsConfiguration().getBounds();
relativeLocation.x -= currentScreenBounds.x;
relativeLocation.y -= currentScreenBounds.y;
return relativeLocation;
}
Thanks for your answers!
Usually when you call "getLocationOnScreen()" it gets the location of the component "this" (from the code I don't quite understand who "this" is).
Maybe you can try to get location of the button by using "button.getLocationOnScreen()".
Here is a small snippet that shows how to position elements relatively to another one. It displays a popup menu below the button, and a JDialog to its left. I tested it on a multi-screen environment where secondary screen is on the right of the main one.
Also, use getSize(), getWidth() and getHeight() instead of getPreferredSize(). getSize(), getWidth and getHeight return the actual dimensions of the component, while getPreferredSize() is only an indicator to the LayoutManager to what the component wishes to have.
If you use the method JPopupMenu.show() make sure to use coordinates and sizes relative to the invoker component.
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
public class Test2 {
public static void main(String[] args) {
final JFrame frame = new JFrame();
final JButton button = new JButton("Hello");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("Some test"));
System.err.println(button.getLocationOnScreen());
popupMenu.show(button, 0, button.getHeight());
JDialog dialog = new JDialog(frame);
dialog.setSize(100, 30);
Point locationOnScreen = button.getLocationOnScreen();
locationOnScreen.x += button.getWidth();
dialog.setLocation(locationOnScreen);
dialog.setVisible(true);
}
});
frame.addComponentListener(new ComponentListener() {
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public void componentResized(ComponentEvent e) {
info(button);
}
private void info(final JButton button) {
if (button.isShowing()) {
System.err.println(button.getLocationOnScreen());
System.err.println(button.getGraphicsConfiguration().getBounds());
}
}
#Override
public void componentMoved(ComponentEvent e) {
info(button);
}
#Override
public void componentHidden(ComponentEvent e) {
}
});
button.setPreferredSize(new Dimension(200, 60));
frame.add(button);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}