I have a program that consists of four JButtons in a JFrame. I want to add images to the JButtons. The problem is that I can't seem to add them, despite trying multiple methods. When compiled, the output is input == null. The images are stored in the same folder as my .java files, so I can't figure out why they aren't showing up.
Main class:
import java.awt.GridLayout;
import java.awt.Image;
import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AutoProgram extends JFrame {
private static String[] files = {"workA","programmingA","leisureA","writingA"};
private static JButton[] bIcons = new JButton[4];
private static Image[] bImg = new Image[4];
public AutoProgram() {
super("Automation Project V.1");
JPanel autoIcons = new JPanel();
autoIcons.setLayout(new GridLayout(2,2));
// Initialize the four buttons (w/ images)
for(int i = 0; i < files.length; i++) {
bIcons[i] = new JButton();
try {
bImg[i] = ImageIO.read(getClass().getResource(files[i].toLowerCase() + ".png"));
bIcons[i].setIcon(new ImageIcon(bImg[i]));
} catch (Exception ex) {
System.out.println(ex);
}
autoIcons.add(bIcons[i]);
}
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));;
mainPanel.add(autoIcons);
add(mainPanel);
pack();
}}
Window class:
public class Window {
public static void main(String[] args) {
AutoProgram frame = new AutoProgram();
frame.setSize(315,315);
frame.setLocationRelativeTo(null);
frame.setFocusable(true);
frame.setResizable(true);
frame.setVisible(true);
}
}
Any help would be greatly appreciated. Thanks!
Before going into the answer to your question, please read the following recommendations:
private static JButton[] bIcons = new JButton[4]; Creating static fields could break your program, so be careful when to use them. Not really needed in this case, please read What does the 'static' keyword do in a class?
JFrame is a rigid container which cannot be placed inside others, and you're not changing it's functionallity anywhere in your program, so there's no need to call extends JFrame, it's better to create a JFrame instance then. See: Extends JFrame vs. creating it inside the program for more information about this.
You're correctly calling pack() but later in the code you're calling frame.setSize(315,315); which "destroys" the changes made by pack(), use one or the other, not both, I recommend you to leave pack() call.
You're not placing your program in the Event Dispatch Thread (EDT), you can fix it by changing your main(...) method as follows:
public static void main (String args[]) {
//Java 7 and below
SwingUtilities.invokeLater(new Runnable() {
//Your code here
});
//Java 8 and higher
SwingUtilities.invokeLater(() -> {
//Your code here
});
}
Now, let's go to the solution:
Your code works fine, I think that your errors might come from the following posibilities:
Calling files[i].toLowerCase() (.toLowerCase() method might be breaking your program, Java is case sensitive).
Your images are not PNG but JPG or JPEG (look at the extension carefully)
Your images are damaged
Related
I waned to put a background in a JFrame, so I searched on YouTube and found a Video, but it didn't work I tried another one and this didn't work either so what is wrong with my code
because all the time i start it is just not there but eclipse dont mark anything wrong.
package Pack;
import javax.swing.*;
public class Gui {
public Gui(){
JLabel background;
Var.jf = new JFrame();
Var.jf.setSize(Var.screenwidth, Var.screenheight);
Var.jf.setTitle("test");
Var.jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Var.jf.setResizable(false);
Var.jf.setVisible(true);
Var.jf.setLocationRelativeTo(null);
Var.jf.setLayout(null);
ImageIcon img = new ImageIcon("Bilder/Testbild");
background = new JLabel("",img,JLabel.CENTER);
background.setBounds(0,0,1000,1000);
Var.jf.add(background);
Var.Buttonxstart = new JButton("Start");
Var.Buttonxstart.setBounds(300,220,400,120);
Var.jf.add(Var.Buttonxstart);
Var.Buttonxclose = new JButton("Exit");
Var.Buttonxclose.setBounds(300,440,400,120);
Var.jf.add(Var.Buttonxclose);
}
}
.
package Pack;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Var {
static JFrame jf;
static int screenheight = 1000;
static int screenwidth = 1000;
static JButton Buttonxstart;
static JButton Buttonxclose;
}
Some points:
code should check if the image is really being loaded - ImageIcon does not throw any Exception if the file is not found. Test if the height or width is -1 or not, example:
if (img.getIconHeigth() == -1) {
throw new FileNotFoundException("image: Bilder/Testbild");
}
or better IMO, use ImageIO.read().
the whole GUI code should run in the EDT (Event Dispatch Thread), see Swing's Threading Policy. . Unable to see if it is, if not, use some code like SwingUtilities.invokeLater(() -> new Gui());
3. it is not recommended to use the null layout manager.Check the Laying Out Components Within a Container tutorial.
Note: I tested the posted code - it's working, if calling new Gui() on the EDT and having an image Testbild in folder Bilder. But for background image I would prefer to override the paintComponent method of a JPanel, to paint the image.
my question is: how do I get the object of my CustomPanel, so that I am able to access its fields (because in my real programm I have some more fields in there) and also am able to delete it from my ArrayList?
I don't know how I have to implement an ActionListener in the Class Window, to somehow get the Object in my Arraylist, which containes the button that got pressed.
Also I am wondering if I am somehow able to implement an ActionListener in the Class CustomPanel which can influence the behaviour of the Object which is an instance of my Class Window.
I have kind of the following code:
public class Window extends JFrame{
ArrayList<CustomPanel> aLCustomPanel = new ArrayList();
JPanel jp = new JPanel();
public Window() {
for(int i=0;i<5;i++){
aLCustomPanel.add(new CustomPanel());
//here I could put the code from the 1 edit - see below
jp.add(aLCustomPanel.get(i));
}
this.add(jp);
}
public static void main(String args[]){
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Window().setVisible(true);
}
});
}
}
class CustomPanel extends JPanel {
private JButton button;
public CustomPanel(){
button = new JButton("button");
this.add(button);
}
public JButton getButton(){
return this.button;
}
}
my Code is much longer and weirder, so I tried to extract the (for this question) importing things.
Thanks for any help in advance!
edit:
for example: I would like to delete the object from the ArrayList, of which the button got pressed.
//imagine this comment in above code
aLCustomPanel.get(aLCustomPanel.size()-1).getButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
button_IwantToDeleteYou(e); //here I want to remove the panel, containing the button that got pressed from the above ArrayList, which is located in Class Window
}
});
edit2:
added a missing bracket and fixed some mistakes, code should be ok now.
Your code contained a few "gaps", i.e. missing code, which I filled in, as follows:
Added calls to [JFrame] methods setDefaultCloseOperation() and pack() and setLocationByPlatform(). I suggest you refer to the javadoc for those methods in order to understand what they do.
I set a layout manager for jp class member variable in your Window class.
Yes, you need to register an ActionListener with the JButton in class CustomPanel and that listener should reside in your Window class - the one that extends JFrame.
Here is my rewrite of your code. Note that I changed the name of class Window to CusPanel so as to distinguish between your class and java.awt.Window class. Not that it makes a difference, I just prefer not to use names of classes from the JDK.
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class CusPanel extends JFrame implements ActionListener {
private static final int COUNT = 5;
private ArrayList<CustomPanel> aLCustomPanel = new ArrayList<>();
private JPanel jp = new JPanel(new GridLayout(0, COUNT));
public CusPanel() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
for (int i = 0; i < COUNT; i++) {
aLCustomPanel.add(new CustomPanel(this));
// here I could put the code from the 1 edit - see below
jp.add(aLCustomPanel.get(i));
}
this.add(jp);
pack();
setLocationByPlatform(true);
}
public void actionPerformed(ActionEvent actionEvent) {
Object source = actionEvent.getSource();
if (source instanceof JButton) {
JButton button = (JButton) source;
Container parent = button.getParent();
jp.remove(parent);
jp.invalidate();
jp.repaint();
pack();
// aLCustomPanel.remove(parent); <- optional
}
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new CusPanel().setVisible(true);
}
});
}
}
class CustomPanel extends JPanel {
private JButton button;
public CustomPanel(ActionListener parent) {
button = new JButton("button");
button.addActionListener(parent);
this.add(button);
}
public JButton getButton() {
return this.button;
}
}
Note that after removing a CustomPanel, the GUI components need to be laid out again and the JFrame should also be resized accordingly. Hence in the actionPerformed() method, I call invalidate(), then repaint() and then pack(). I also think that if you remove a CustomPanel from the GUI, you should also remove it from the ArrayList, but hey, I still don't understand why you want to do this although I obviously don't know the whole story behind you wanting to do this in the first place.
Of-course, since each button (and each CustomPanel) looks exactly the same, you can't really know which button was removed. Again, I assume you see the big picture whereas I don't.
As for testing-reasons I tried to open a JDialog window with the panel and its contents of the main application frame. As I already had anything in there I wanted to see if I could simply set the JDialogs contentPane to the one I passed over. So in simplyfied form this came together:
testsforSO.java :
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class testsforSO extends JFrame {
private static final long serialVersionUID = -3890178393751567629L;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
testsforSO frame = new testsforSO();
frame.setSize(300, 300);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public testsforSO() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("I am the Frame");
getContentPane().setLayout(new BorderLayout(0, 0));
JPanel panel = new JPanel();
panel.setLayout(null);
JButton btnRestart = new JButton("Restart");
btnRestart.setBounds(10, 10, 50, 50);
btnRestart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
testsforSO.main(null);
dispose();
}
});
panel.add(btnRestart);
getContentPane().add(panel);
repaint();
// -----------DELETE These comments-------------
// JDialog myDialg = new JDialog(this);
// myDialg.setContentPane(panel);
// myDialg.setVisible(true);
// myDialg.setSize(300,300);
// myDialg.setLocation(new Point(250, 250));
// myDialg.setTitle("I am Dialog from within the script");
myDialog.main(panel);
}
}
and myDialog.java :
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.JDialog;
public class myDialog extends JDialog {
private static final long serialVersionUID = 7079322237622743228L;
public static void main(Container myContainer) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
myDialog frame = new myDialog(myContainer);
frame.setVisible(true);
frame.setContentPane(myContainer);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public myDialog(Container myContainer) {
setContentPane(myContainer);
getContentPane().setLayout(null);
setBounds(200,200,200,200);
}
}
When starting the main frame I assumed it would contain the restarting button as well as the dialog does. But interestingly it was only the dialog with the button.
However when hitting it, the main frame properly restarted, a second dialog was set up and it contained the button again. This time however the main frame had the button as well, just without any function. Clicking on it does absolutely nothing.
Then I tried further and checked if that behaviour would change if I added the dialog directly into the main applications code (see the commented lines) and, starting the application once again only the dialog in its own class showed the button. Even hitting this one now restarted properly but the button won't show up on any other window except the lonely declared dialog.
What am I missing here and how could I refactor my code to work properly if even at all in this manner?
Understand that Swing components can only be present in one container, and while you may see the visual residue of a component in a container, the actual component is only present in the last container added to.
Myself, if I wanted dialog and jframe to have the same content pane components, I'd create a factory method to create the contentPane, and then use it to create two unique but identical contentPanes.
Also, I'd be remiss if I didn't mention something about your use of null layouts. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
For instance, when I ran your code, this is the dialog that I saw:
You hard coded the button's size to be too small to show its text on my platform. If you had used layout managers, and called pack() on the top-level window, the button would show appropriately.
First of all, this is a more specific question than it seems to be. To start off: I am currently doing a small application with a rather small GUI, so I decided to make a GUI class, and initialize my whole GUI in this constructor.
This would look like this:
public class GUI extends JFrame{
public GUI{
//Initialize GUI here, including its Frames, Panels, Buttons etc.
}
}
How can I now access the GUIs frame etc. from an external class? If I would create an object of the GUI class, I would simply duplicate my GUI window. I did not come across any other ideas than making the frame, panel and so on static.
I'm somewhat lost right now. Also I'm pretty sure that I am not thinking the right way into this case, but I need someone to point me to the right direction. If someone could help me out, I would be very thankful.
First of all, using static is the worst solution possible, even if your GUI class is a singleton (buf if it is, at least it will work fine).
Why don't you simply create getters and/or setters ? And finally, it is usually not normal that external classes need to access the components of another graphic class. You should wonder if your design is the most fitted for your needs.
Here's a simple GUI to change the background color of a JPanel with a JButton. Generally, this is how you construct a Swing GUI.
package com.ggl.testing;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ChangeDemo implements Runnable {
private boolean isYellow;
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new ChangeDemo());
}
#Override
public void run() {
frame = new JFrame("Change Background Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
JPanel namePanel = new JPanel();
JLabel nameLabel = new JLabel(
"Click the button to change the background color");
nameLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
namePanel.add(nameLabel);
mainPanel.add(namePanel);
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
buttonPanel.setBackground(Color.YELLOW);
isYellow = true;
JButton changeButton = new JButton("Change Color");
changeButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
isYellow = !isYellow;
if (isYellow) buttonPanel.setBackground(Color.YELLOW);
else buttonPanel.setBackground(Color.RED);
}
});
buttonPanel.add(changeButton);
mainPanel.add(buttonPanel);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
You don't access the Swing components of the GUI from other classes. You create other classes to hold the values of the GUI.
Generally, you use the model / view / controller pattern to construct a Swing GUI. That way, you can focus on one part of the GUI at a time.
Take a look at my article, Java Swing File Browser, to see how the MVC pattern works with a typical Swing GUI.
You don't need to make it static or to create a new JFrame object every time.
Have a look at this simple code :
class UseJFrame {
public static void main(String...args) {
Scanner sc = new Scanner(System.in);
JFrame frame = new GUI();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
System.out.println("Press E to exit");
String ip;
while(true) {
System.out.println("Show GUI (Y/N/E)? : ");
ip = sc.nextLine();
if(ip.equalsIgnoreCase("y") {
frame.setVisible(true);
} else if(ip.equalsIgnoreCase("n") {
frame.setVisible(false);
} else { // E or any other input
frame.dispose();
}
}
}
}
Note : Don't make GUI visible through constructor or it will show window at the very starting of creation of JFrame object.
If you want to use the same JFrame object at other places too then pool architecture would be better approach.
I have a JInternalFrame painted with a BufferedImage and contained in the JDesktopPane of a JFrame. I also have a JTextArea where I want to write some java code (function) that takes the current JInternalFrame's painted BufferedImage as an input and after doing some manipulation on this input it returns another manipulated BufferedImage that paints the JInternalFrame with new manipulated Image again.
Manipulation java code of JTextArea:-
public BufferedImage customOperation(BufferedImage CurrentInputImg)
{
Color colOld;
Color colNew;
BufferedImage manipulated=new BufferedImage(CurrentInputImg.getWidth(),CurrentInputImg.getHeight(),BufferedImage.TYPE_INT_ARGB);
// make all Red pixels of current image black
for(int i=0;i< CurrentInputImg.getWidth();i++) {
for(int j=0;j< CurrentInputImg.getHeight(),j++) {
colOld=new Color(CurrentInputImg.getRGB(i,j));
colNew=new Color(0,colOld.getGreen(),colOld.getBlue(),colOld.getAlpha());
manipulated.setRGB(i,j,colNew.getRGB());
}
}
return manipulated;
}
How can I run/compile this JTextArea java code at runtime and get a new
manipulated image for painting on JInternalFrame?
Here is my Main class:
(This class is not actual one but I have created it for you for basic interfacing containing JTextArea,JInternalFrame,Apply Button)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.JInternalFrame;
import javax.swing.JDesktopPane;
import java.awt.image.*;
import javax.imageio.*;
import java.io.*;
import java.io.File;
import java.util.*;
class MyCustomOperationSystem extends JFrame
{
public JInternalFrame ImageFrame;
public BufferedImage CurrenFrameImage;
public MyCustomOperationSystem() {
setTitle("My Custom Image Operations");
setSize((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth(), (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight());
JDesktopPane desktop=new JDesktopPane();
desktop.setPreferredSize(new Dimension((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth(),(int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()));
try {
CurrenFrameImage=ImageIO.read(new File("c:/Lokesh.png"));
}catch(Exception exp) {
System.out.println("Error in Loading Image");
}
ImageFrame=new JInternalFrame("Image Frame",true,true,false,true);
ImageFrame.setMinimumSize(new Dimension(CurrenFrameImage.getWidth()+10,CurrenFrameImage.getHeight()+10));
ImageFrame.getContentPane().add(CreateImagePanel());
ImageFrame.setLayer(1);
ImageFrame.setLocation(100,100);
ImageFrame.setVisible(true);
desktop.setOpaque(true);
desktop.setBackground(Color.darkGray);
desktop.add(ImageFrame);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add("Center",desktop);
this.getContentPane().add("South",ControlPanel());
pack();
setVisible(true);
}
public JPanel CreateImagePanel() {
JPanel tempPanel=new JPanel() {
public void paintComponent(Graphics g) {
g.drawImage(CurrenFrameImage,0,0,this);
}
};
tempPanel.setPreferredSize(new Dimension(CurrenFrameImage.getWidth(),CurrenFrameImage.getHeight()));
return tempPanel;
}
public JPanel ControlPanel() {
JPanel controlPan=new JPanel(new FlowLayout(FlowLayout.LEFT));
JButton customOP=new JButton("Custom Operation");
customOP.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evnt) {
JFrame CodeFrame=new JFrame("Write your Code Here");
JTextArea codeArea=new JTextArea("Your Java Code Here",100,70);
JScrollPane codeScrollPan=new JScrollPane(codeArea,ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
CodeFrame.add(codeScrollPan);
CodeFrame.setVisible(true);
}
});
JButton Apply=new JButton("Apply Code");
Apply.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
// What should I do!!! Here!!!!!!!!!!!!!!!
}
});
controlPan.add(customOP);
controlPan.add(Apply);
return controlPan;
}
public static void main(String s[]) {
new MyCustomOperationSystem();
}
}
Note: in the above class JInternalFrame (ImageFrame) is not visible even though I have declared it visible. So, ImageFrame is not visible while compiling and running the above class. You have to identify this problem before running it.
Take a look at the Java 6 Compiler API
If you want to actually compile code from within java, it's doable but not trivial.
It's much better to use a scripting framework like Groovy--that would work great. Groovy is very java-compatible, it will almost always run java code directly (there are a few strange exceptions)
BeanShell is another scripting framework.
These both do just what you want without the effort of trying to figure out how to compile a class then load it into your existing runtime. (Actually, the problem I've seen isn't the initial compile, it's flushing your class so you can replace it with a new one after an edit).