I'm looking to display an image on a JPanel, but when I run the code, the panel is blank. The method I'm attempting is a slightly modified version of this tutorial.
I've confirmed that all methods in the code are actually called using println. My frame and panel are visible, and I'm fairly certain I've added both the icon to the label, and the label to the panel. What am I missing?
The relevant parts of my code:
public class SYSTEM
public static void start() {
GameFrame GF = new GameFrame();
GamePanel GP = new GamePanel();
GF.add(GP);
GP.loadSprite("Player", new Dimension(0,0));
}
}
public class GameFrame extends JFrame {
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(SYSTEM.windowSize);
setVisible(true);
}
}
public class GamePanel extends JPanel {
ArrayList<Sprite> sprites = new ArrayList<Sprite>();
GamePanel() {
setVisible(true);
setSize(SYSTEM.windowSize);
setLayout(null);
}
public void loadSprite(String spriteName, Dimension pos) {
sprites.add(new Player());
add(sprites.get(0).getIcon());
}
public class Sprite {
protected JLabel icon;
protected BufferedImage image;
protected String filePath;
protected Dimension pos;
public JLabel getIcon() {
return icon;
}
}
public class Player extends Sprite {
public Player() {
filePath = "FILEPATH_OMITTED";
pos = new Dimension(0,0);
try {
image = ImageIO.read(new File(filePath));
} catch (IOException e) {
System.out.println(e);
}
icon = new JLabel(new ImageIcon(image));
}
}
Don't use components for games, you've thrown out the layout manager which is responsible for determining the size and location of the components, but you've failed to compensate for it. I'd recommend that you use a custom painting approach instead, see Painting in AWT and Swing and Performing Custom Painting for starters.
Just so I'm clear, you problem is directly related to setLayout(null); which is bad regardless of what you're doing
Related
The Buttons in my JPanel don't show up when it's loaded, only when I resize the window or move my mouse over it. In other discussions the use of "validate()" or "repaint()" was suggested, but that doesn't work for me.
I'm using a basic model view controller design and I am pretty sure that I'm doing everything else correctly.
Just in case you wonder, of course more panels will be added to the frame, that's the purpose of the update() and changeCards() methods.
Here's my frame:
public class View extends JFrame {
private MainMenuPanel mainMenu;
private final String MAIN_MENU_CONSTRAINTS = "MAIN_MENU";
public View() {
super();
init();
mainMenu = new MainMenuPanel();
add(mainMenu;MAIN_MENU_CONSTRAINTS);
validate();
repaint(0,0,getWidth(),getHeight());
setVisible(true);
}
private void init() {
setVisible(false);
setTitle("Test");
// set card-layout
setRootPaneCheckingEnabled(false);
CardLayout cl = new CardLayout();
this.setLayout(cl);
// expand frame to whole display size
setExtendedState(MAXIMIZED_BOTH);
// set unclosable
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void update (Mode mode) {
switch (mode) {
case MAIN_MENU:
changeCard(MAIN_MENU_CONSTRAINTS);
break;
}
}
public void changeCard(String card) {
// update cards
CardLayout cl = (CardLayout) getLayout();
cl.show(this, card);
}
}
And here's the Panel:
public class MainMenuPanel extends Panel implements ActionListener{
private JButton startButton;
private JButton quitButton;
private final String START_ACTION_COMMAND = "START";
private final String QUIT_ACTION_COMMAND = "QUIT";
private MainMenuPanelListenerImpl listener;
public MainMenuPanel() {
super();
init();
initComponents();
configureComponents();
configureListeners();
addComponents();
revalidate();
}
private void init() {
setLayout(null);
}
private void initComponents() {
startButton = new JButton();
quitButton = new JButton();
}
private void configureComponents() {
startButton.setText("Start");
quitButton.setText("End");
startButton.setBounds((int)(0.5*getWidth()-200), (int)(0.5*getHeight()-75), 400, 75);
quitButton.setBounds((int)(0.5*getWidth()-200), (int)(0.5*getHeight()+25),400,75);
}
private void configureListeners() {
startButton.addActionListener(this);
startButton.setActionCommand(START_ACTION_COMMAND);
quitButton.addActionListener(this);
quitButton.setActionCommand(QUIT_ACTION_COMMAND);
}
private void addComponents() {
add(startButton);
add(quitButton);
startButton.validate();
quitButton.validate();
}
#Override
public void actionPerformed(ActionEvent e) {
switch (e.getActionCommand()) {
case START_ACTION_COMMAND:
listener.start();
break;
case QUIT_ACTION_COMMAND:
System.exit(0);
break;
}
}
public void setListener(MainMenuPanelListenerImpl listener) {
this.listener = listener;
}
}
After you paint the elements, you put the setvisible(true) because if you put it before, the Jframe will paint no elements
Well first of all you mix the old AWT-Components like Panel with newer SWING-Components like JFrame. Those don´t really work well together so I would try to fix that first. I would highly recommend using SWING or if you want to learn the newest Java GUI Library then JavaFX.
Don´t use the method repaint in the constructor of your JFrame, actually you shouldn´t use repaint in SWING at all. Nor do you need validate in the constructor. If you want to position your JFrame somewhere you should use something like this this.setLocation(0,0)
And to the main question: The panel probably only shows it´s components after resizing because you add it to the JFrame the wrong way. In SWING there is something called a content pane where you should add all of your stuff onto (except JMenuBar but that is a different story).
Simply set the layout of the content pane to the card layout that you want to use and then add your panel onto the content pane.
Here a link regarding the panel levels: https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
I am working with GUIs in Java using JFrame and JPanel, as well as ActionListener to edit an image when I click a button. Currently, I am having a lot of trouble getting my JPanel class called ButtonPanel to interact with my BufferedImage img. I am trying to display the height of the image but nothing is working and I have tried various solutions. My code for the ButtonPanel class is:
class ButtonPanel extends JPanel
{
//JButton makeBlue;
BufferedImage img;
ButtonPanel(BufferedImage x)
{
final JButton makeBlue = new JButton ("Make Blue");
img = x;
//int width, height;
setBackground(Color.RED);
int height = 0;
add(makeBlue);
ActionListener action =
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (e.getSource()== makeBlue)
{
//img = x;
height = img.getHeight();
// System.out.println(rgbvalue);
//System.out.println(width + " " + height);
setBackground(Color.GREEN);
repaint();
}
}
};
makeBlue.addActionListener(action);
}
}
Whenever I try to use a method from the BufferedImage API in order to edit the image, such as in the above code, I get this error:
ImageEditorDeluxe.java:184: error: local variable height is accessed from within inner class; needs to be declared final
height = img.getHeight();
I have played around with where I initialize height but nothing has worked. Any help would be appreciated.
I have another class called ProgramWindow in which I add all the different JPanels of the image editor to one main JFrame and I think this is where my problem might be, as the BufferedImage is null. Here is the code for ProgramWindow:
class ProgramWindow extends JFrame
{
ProgramWindow()
{
ImagePanel ip = new ImagePanel();
ChooseFile cf = new ChooseFile();
ButtonPanel bp = new ButtonPanel(ip.getImg());
add(ip, BorderLayout.CENTER);
add(cf, BorderLayout.SOUTH);
add(bp, BorderLayout.WEST);
}
}
I have concluded that the ButtonPanel in ProgramWindow is being passed a null parameter but I do not know why this is. I have a method called getImg in the ImagePanel class which I am calling as the parameter for ButtonPanel. Here is the code for ImagePanel:
class ImagePanel extends JPanel
{
BufferedImage img;
ImagePanel()
{
setBackground(Color.BLUE); //to test
final JButton button = new JButton ("Display picture");
add(button);
ActionListener action =
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==button)
{
try
{
img = ImageIO.read(ChooseFile.getFile());
}
catch(IOException f)
{
f.printStackTrace();
}
repaint();
}
}
};
button.addActionListener(action);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (img != null)
g.drawImage(img, 0, 0, this);
}
public void setImage(BufferedImage i)
{
img = i;
repaint();
}
public BufferedImage getImg()
{
return img;
}
}
You're declaring height in the constructor and so it is local to the constructor. Perhaps it would be better to make it an instance field of the class instead.
public class ButtonPanel extends JPanel {
private BufferedImage img;
private int height;
Having said that, why even have a height variable field, since you can obtain it any time you'd like by calling img.getHeight()?
Edit
Your second problem, one we can't help you on yet, is that your img variable holds a null reference. Given your new code, this suggests that ip.getImg() is returning null, but don't take my word on this, test it. Put this line in your code:
System.out.println("is ip.getImg() returning null? " + (ip.getImg()));
Edit 2
Of course you're getting null. You're extracting img from ImagePanel before the user has had a chance to interact with it. Since it only gets an image when the user presses the button, and since you're extracting the image on class creation, before the user has had a chance to do squat, the only possibility is that when you extract the image on class creation, you're going to get null. Solution: don't do that.
Code in Question
There isn't an actionListener for the image thumbnails, yet when clicked they update the image.
From this webpage.
Edit: I am currently importing images using JFileChooser and then creating a thumbnail and displaying the full image in a similar way to this, although not using ImageIcons. But would like to use this method so when I add an image it adds to the list and allows me to click the thumbnail to show that image.
However mine using actionListeners to change when something is pressed but this doesn't and can't understand the code where it does.
Thanks
Edit2:
Regarding the repaint option:
I have a class which extends component which then calls a repaint function.
public class Image extends Component {
private BufferedImage img;
//Print Image
public void paint(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
I then have a class with all my Swing components which call methods from other classes.
Image importedImage = new Image(loadimageone.openFile());
Image scaledImage = new Image();
// Save image in Buffered Image array
images.add(importedImage.getImg());
// Display image
imagePanel.removeAll();
imagePanel.add(importedImage);
imagePanel.revalidate();
imagePanel.repaint();
previewPanel.add(scaledImage);
previewPanel.revalidate();
previewPanel.repaint();
If I remove the revalidate or repaint it wont' update the image on the screen.
Edit 3:
This is the code on how I implemented the dynamic buttons:
//Create thumbnail
private void createThumbnail(ImpImage image){
Algorithms a = new Algorithms();
ImpImage thumb = new ImpImage();
//Create Thumbnail
thumb.setImg(a.shrinkImage(image.getImg(), 75, 75));
//Create ImageIcon
ImageIcon icon = new ImageIcon(thumb.getImg());
//Create JButton
JButton iconButton = new JButton(icon);
//Create ActionListener
iconButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
bottomBarLabel.setText("Clicked");
imagePanel.removeAll();
imagePanel.add(images.get(position)); //Needs Fixing
imagePanel.revalidate();
}
});
//Add to previewPanel
previewPanel.add(iconButton);
previewPanel.revalidate();
previewPanel.repaint();
}
It looks like it uses ThumbnailAction instead which extends AbstractAction (at the very bottom of the code). Swing components can use Actions instead of ActionListeners. The advantage of Actions is that buttons can share an Action and they will automatically use the same key-bindings etc.
http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html
EDIT: I have added some code demonstrating that you do not need to explicitly repaint(). Give it a try.
public static void main(String args[]) {
JFrame frame = new JFrame();
frame.setSize(200, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(2, 1));
final JLabel iconLabel = new JLabel();
JButton button = new JButton("Put Image");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
try {
iconLabel.setIcon(new ImageIcon(ImageIO.read(fc.getSelectedFile())));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
panel.add(iconLabel);
panel.add(button);
frame.add(panel);
frame.setVisible(true);
}
EDIT 2 (There is no Edit 2)
EDIT 3: Try this
public class MyActionListener implements ActionListener {
private JPanel imagePanel;
private Image image;
public MyActionListener(JPanel imagePanel, Image image) {
this.imagePanel = imagePanel;
this.image = image;
}
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Clicked");
imagePanel.removeAll();
imagePanel.add(image); //Needs Fixing
imagePanel.revalidate();
}
}
Not sure if this has been discussed before. But I am having a odd issue with transparent JTextFields added on a transparent JPanel. For some reason (I could not dig enough to find why) there are additional paintings that get carried out. Perhaps there are dirty regions that needs to be dealt with? not sure.
Let me present this simple example:
public class TextFieldGame extends JPanel {
public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame someFrame = new JFrame("Is this odd?");
someFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
someFrame.setSize(200,600);
someFrame.add(new TextFieldGame());
someFrame.setVisible(true);
}
});
}
public TextFieldGame() {
setupContentPane();
}
private void setupContentPane() {
setLayout(new BorderLayout());
final CanvasPanel canvasPanel = new CanvasPanel();
add(canvasPanel, BorderLayout.CENTER);
add(new ControlPanel(canvasPanel), BorderLayout.SOUTH);
}
public static class ControlPanel extends JPanel {
private final CanvasPanel canvasPanel;
ControlPanel(CanvasPanel canvasPanel) {
this.canvasPanel = canvasPanel;
setupContentPane();
}
private void setupContentPane() {
setLayout(new FlowLayout(FlowLayout.RIGHT));
final JButton load = new JButton("load");
add(load);
load.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvasPanel.addChildComponent(getComponent());
canvasPanel.revalidate();
canvasPanel.repaint();
}
});
}
private JComponent getComponent() {
final JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
container.setOpaque(false);
for (int i = 0; i < 10; i++) {
final JTextField textField = new JTextField("why you no work?") {
#Override
public Dimension getMaximumSize() {
return new Dimension(Short.MAX_VALUE, getPreferredSize().height);
}
};
textField.setOpaque(false);
container.add(textField);
}
return container;
}
}
public static class CanvasPanel extends JPanel {
private int paintCount = 0;
CanvasPanel() {
setupContentPane();
}
public void setupContentPane() {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
setBackground(Color.white);
}
public void addChildComponent(JComponent component) {
component.setAlignmentY(TOP_ALIGNMENT);
add(component);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("paint count: " + ++paintCount);
}
}
}
I have added the System out statement to show the count of paint task.
When first loaded, there will be 2/3 paints - fair enough. Then if you are to press the "Load" button, 10 transparent JTextFields will be added to a transparent JPanel, and this transparent JPanel will be added to the CanvasPanel. (Canvas panel is in turn a subchild of the JFrame). You will notice upon doing this, 11 additional paint jobs will be done.
But in theory (well in my understanding) after "Load" is pressed, only one paint job should be carried out. That is because, I have added only one child to CanvasPanel (the child itself might have 10 textfields, but they should all be painted in one shot).
Just to test my understanding, if you are to use 10 JLabels instead of the 10 JTextFields, only one paint job is carried out after "Load" is pressed. Which is what it should be.
Also, if you are to keep JTextField opaque, only one paint job is carried out. (Just tested that if instead of JTextField, a JTextArea is used, one paint is done)
What is going on? Note that JLabel is transparent by default, so I am not sure why transparency of JTextField component causing these additional paints.
Please help/
whats wrong with my code? I just want to have a Picture in my Window...
//class ImagePanel:
public class ImagePanel extends JPanel {
private static final long serialVersionUID = -7664761101121497912L;
public Image i;
public ImagePanel(Image i) {
this.i = i;
}
#Override
public void paintComponents(Graphics g) {
super.paintComponent(g);
g.drawImage(this.i, 0, 0, null);
}
}
//class Main
public class Main extends JFrame {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(1024, 768);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ImagePanel panel = null;
try {
panel = new ImagePanel(ImageIO.read(new File("D:/test.JPG")));
} catch (IOException e) {
e.printStackTrace();
}
frame.getContentPane().add(panel);
frame.setVisible(true);
}
}
There is just a Window without a picture :(
Whats the problem? And is there an easy way to set size of the window == picture size?
Thank you!
You must override paintComponent(Graphics g) instead of paintComponents(Graphics g).
The best solution is to use a JLabel. Don't reinvent wheel. There is no need for you to do custom painting.
But if you do custom painting then you need to override the getPreferredSize() method to be the size of the image so the layout manager can do their job.
please read this tutorials about Icon in Swing and your Image would by placed to the JLabel but with same way/funcioanlities as to the JPanel
I not at all familiar with Java panes, but don't you need to set the size of ImagePanel before you add it to the content pane.