I am programming in java and I'm getting into GUIs and Graphics. In my program I paint an image onto a JPanel and add the JPanel to the main window. The Problem I'm having is when I run the program the image doesn't show until I manually resize the window. Here is the relevant code:
Where the image is drawn:
public class painting extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.drawImage(Toolkit.getDefaultToolkit().getImage("image.png"), 0, 0, null);
}
}
Where JPanel is added to JFrame (c is GridBagConstraints):
public class GUI extends JFrame{
public GUI(){
painting Pnt = new painting();
c.gridx = 1; c.gridy = 0;
c.ipadx = 540; c.ipady = 395;
add(Pnt, c);
}
}
Where The Window is set up:
public class MainC{
public static void main (String args[]){
GUI gui = new GUI();
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.pack();
gui.setVisible(true);
gui.setTitle("Title");
}
}
Thanks,
Bennett
EDIT: I noiced that it sometimes displays the image correctly but then if I close the program and try again and it doesn't work until I resize it.
EDIT2: Here are the files GUI class, MainC class
Toolkit.getImage() works asynchronously. Either use ImageIO.read() or add a MediaTracker.
In GUI constructor you should call super() before anything else.
Also move:
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.pack();
gui.setVisible(true);
gui.setTitle("Title");
in GUI constructor.
Your problem must be somewhere else in your code. I copied and pasted your three classes into my IDE. The only thing I changed was in GUI.java I added setLayout(new GridBagLayout()); and GridBagConstraints c = new GridBagConstraints(); and I changed the location of the image to one of my own. The window works as expected with the image displayed right away.
I assume you have additional code not displayed here, such as the initialization of c that I added. Check your other code to make sure you are not redrawing over your image initially. Also, if you post the other code I could help you identify the problem
does
Toolkit.getDefaultToolkit().getImage("image.png")
brings you the picture?
I tried it with:
public class Gui extends JFrame{
public Gui() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new BorderLayout());
this.setBounds(100, 100, 300, 300);
JPanel pnl = new JPanel(){
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.WHITE);
try {
g.drawImage(new Robot().createScreenCapture(new Rectangle(90, 90)), 0, 0, null);
} catch (AWTException e) {
e.printStackTrace();
}
}
};
getContentPane().add(pnl, BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Gui().setVisible(true);
}
});
}
and it works.
Related
I have a program where I have a JFrame with a JButton in it. When the user clicks the JButton, all Components of the JFrame are removed, and a JPanel with red background is added to it.
When I click the JButton, that red JPanel does not become visible unless I resize the JFrame (I am using Windows 7). Is there a way to achieve what I want without having to manually resize the JFrame?
Here is a part of the code I am using:
public class Demo implements ActionListener{
public static void main(String args[]){
...............
button.addActionListener(this); //'button' is an object of Jbutton class.
frame.setVisible(true); //'frame' is an object of JFrame class.
............
}
public void actionPerformed(ActionEvent ae){
frame.removeAllComponents();
frame.add(panel1); //panel1 is an object of Jpanel class with red background.
/* Here is where my problem lies.
panel1 is not visible to me unless I manually resize the JFrame. */
}
}
For removing (and then, for example, add new JComponents) JComponents from JPanel or from top-level containers you have to call, only once and on the end of the action:
revalidate();
repaint();
And if you only resize or change JComponents:
validate();
repaint();
For me, this was a bit of an oddity. As it turned out, invoking remove(Component comp), adding the new JPanel, and then invoking pack() worked for me.
public class Demo{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
final JButton button = new JButton("Press Me");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
frame.remove(panel);
final JPanel redPanel = new JPanel(){
#Override
public Dimension getPreferredSize(){
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g){
Graphics g2 = g.create();
g2.setColor(Color.RED);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.dispose();
}
};
frame.add(redPanel);
frame.pack();
}
});
panel.add(button);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
BEFORE PRESSING THE BUTTON
AFTER PRESSING THE BUTTON
ODDITIES
Invoking removeAll() actually caused the GUI to freeze. It seems that this event has occurred before. This occurred even after I attempted to remove the action listener prior to removal of all components.
I did not need to invoke any of the validate methods, or even repaint the GUI.
you have to force a repaint() in the frame so the frame have to repaint itself.
After making an instance in a previous frame, I'm trying to the background image on the next frame but as a result, I just saw the debugged result and found out that the paint method was not called. From what I know, the paint method is inherited by the JFrame class and with this logic, I've made it overrided. As I guess, the reason happen the logical error is from what I used the event handler and made the instance in the EventHandlerClass.
if(e.getActionCommand().equals(ButtonTo))
if(idString.equals("USER"))
{
{
if("1234".equals(pwSt))
{
System.out.println("Wellcome");
if(gs==null)
{
gs=new GameStart();
}
}
else
{
System.out.println("Confirm your password");
}
}
}
This is a code that If an action is performed it will make an instance(gs). After doing this, I noticed that the instance has been used as to make a new console frame.
class GameStart extends JFrame {
private Image screenImage;
private Graphics screenGraphic;
private Image introBackgroundImage;
private ImageIcon img;
GameStart()
{
JFrame jf=new JFrame("Game Set");
jf.setBounds(300, 300, 400, 200);
jf.setLayout(new BorderLayout());
JButton bt1=new JButton("Start");
JButton bt2=new JButton("Exit");
JPanel panel1=new JPanel();
panel1.add(bt1);panel1.add(bt2);
setContentPane(panel1);
jf.add(panel1, BorderLayout.SOUTH);
bt1.addActionListener(new Choice());
bt2.addActionListener(new Choice());
jf.setVisible(true);
img=new ImageIcon("./Images/backGroundImage.jpg");
System.out.println("1");
}
public void paint(Graphics g) {
screenImage=createImage(200, 200);
screenGraphic=screenImage.getGraphics();
screenDraw(screenGraphic);
g.drawImage(screenImage, 0, 0, null);
System.out.println("2");
}
public void screenDraw(Graphics g)
{
this.repaint();
System.out.println("3");
}
Now, With making a frame and some buttons, I expect to show all the numbers(1, 2, 3) that indicate the result but Just did number 1.
There are some errors in your code that I can see at first glance:
You're extending JFrame, but you're not adding any extra functionality to it, see: Extends JFrame vs. creating it inside the program. Instead, build your GUI towards the use of JPanels and override their paintComponent(...) method and not the paint(...) one.
You're breaking the paint-chain: After doing the above point, in paintComponent(), call super.paintComponent(...)
Maybe there are others but I'm currently busy and can't test your code, but the ones above should help with your issue.
I hope this isn’t a stupid first question; I can’t seem to find an answer anyway.
I have this JFrame constructor where a JPanel is added to the JFrame. The JPanel paints a Rectangle in the JFrame, and that’s fine. However, if I add an ImageIcon object as in the code below (for later use), the rectangle isn’t painted. It does appear if I resize the window though.
One solution is to put the setVisible(true) as the last line, or to instantiate the ImageIcon above the constructor, but I really want to understand this. It doesn’t make sense to me that an object not even used can cause this behaviour. Thanks.
public class AJFrame extends JFrame {
ImageIcon ii;
public AJFrame() {
setSize(400,400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
ImageIcon ii = new ImageIcon("Untitled.png");
JPanel jp = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
g.fillRect(0, 0, 50, 50);
}
};
add(jp);
}
public static void main(String[] args) {
AJFrame jf = new AJFrame();
}
}
All actions within a frame should be done in the EDT (Event Dispatching Thread) of Swing. Therefore the right way to start your frame is
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new AJFrame().setVisible(true);
}
});
So maybe it all comes down to the wrong start of your frame.
The main routine of a Java program is not started within the EDT. All Swing actions that are not within the EDT could produce strange refresh/visibility issues.
Here is the complete sourcecode:
public class AJFrame extends JFrame {
ImageIcon ii;
public AJFrame() {
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
//setVisible(true);
//ImageIcon ii = new ImageIcon("Untitled.png");
JPanel jp = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
g.fillRect(0, 0, 50, 50);
}
};
add(jp);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new AJFrame().setVisible(true);
}
});
}
}
I'm basically trying to draw a JComponent inside another by calling the second component's paint passing it the first component's Graphics.
I'm trying to create a GUI editor, (reinventing the wheel, I know, it's just a proof of concept)
So I have a class that extends JPanel where I want to draw components from a VectorControls.
So far I got this method in my extended JPanel:
#SuppressWarnings("serial")
public class Sketch extends JPanel {
private Vector<JComponent> controls = new Vector<JComponent>();
public Sketch() {
super();
this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
}
public void addControl(JComponent c) {
Dimension d = new Dimension(100,50);
c.setPreferredSize(d);
c.setMinimumSize(d);
c.setMaximumSize(d);
controls.add(c);
this.repaint();
this.revalidate();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i=controls.size()-1; i>=0; i--) {
JComponent c = controls.get(i);
c.paint(g);
}
}
}
I'm building/attaching the Sketch panel like this:
public GUIEditor() {
mainFrame = new JFrame("GUI EDITOR");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Sketch mainPanel = new Sketch();
mainPanel.setPreferredSize(new Dimension(640,480));
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
mainFrame.setLayout(gbl);
JPanel toolsPanel = new JPanel();
toolsPanel.setPreferredSize(new Dimension(160,480));
toolsPanel.setLayout(new GridLayout(0,1));
for(Control c : toolBoxItems ) {
AbstractAction action = new ToolBoxButtonAction(mainPanel, c.type);
JButton b = new JButton(action);
b.setText(c.title);
toolsPanel.add(b);
}
gbc.gridx = 0;
gbc.gridy = 0;
gbl.setConstraints(mainPanel, gbc);
mainFrame.add(mainPanel);
gbc.gridx = 1;
gbc.gridy = 0;
gbl.setConstraints(toolsPanel, gbc);
mainFrame.add(toolsPanel);
mainFrame.pack();
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
}
Inside ToolBoxButtonAction, basically I'm doing this:
public void actionPerformed(ActionEvent e) {
try {
sketch.addControl(control.newInstance());
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
}
but I'm writing this because it doesn't work.
Any ideas on how to achieve this?
I'm basically trying to draw a JComponent inside another by calling the second component's paint passing it the first component's Graphics.
Components can only be painted when the component has non-zero size. Normally the size of a component is determined by the layout manager.
Your basic code looks reasonable, but unless you have code to size and locate the components you won't see anything. If you just set the size then all components will paint on top of one another.
Or the problem may be that your parent panel doesn't have a size so it is not even painted. The default FlowLayout uses the preferred size of the child components to determine the panels size. Since you don't add components directly to the panel there are no child components so the preferred size will be 0. When you reinvent the wheel you need to reinvent everything.
Without a SSCCE the context of how you use this code is unknown to all we can do is guess.
Edit:
Create a SSCCE when you have a problem and get it working with hard coded values before trying to get it to work dynamically. Something like:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class Sketch extends JComponent
{
private Vector<JComponent> controls = new Vector<JComponent>();
public void addControl(JComponent c)
{
c.setSize(100, 50);
int location = controls.size() * 50;
c.setLocation(location, location);
controls.add(c);
repaint();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i=controls.size()-1; i>=0; i--)
{
JComponent c = controls.get(i);
Point location = c.getLocation();
g.translate(location.x, location.y);
c.paint(g);
g.translate(-location.x, -location.y);
}
}
private static void createAndShowUI()
{
Sketch sketch = new Sketch();
sketch.addControl( new JButton("button") );
sketch.addControl( new JTextField(10) );
sketch.addControl( new JCheckBox("Checkbox") );
JFrame frame = new JFrame("Sketch");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( sketch );
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Some time ago, I've written a framework for such tasks. Maybe you find it useful (the library is Open Source):
Tutorial:
http://softsmithy.sourceforge.net/lib/current/docs/tutorial/swing/customizer/index.html
Javadoc:
http://softsmithy.sourceforge.net/lib/current/docs/api/softsmithy-lib-swing-customizer/index.html
Info about the latest release:
http://puces-blog.blogspot.ch/2012/11/news-from-software-smithy-version-03.html
JButton and JLabel disappears when adding custom background. I don't see any problems in my program, but maybe you guys find an solution! I think it's only a little thing I forgot, but I can't figure it out.
Here's the code:
GameWindow.java:
setContentPane(new StartImagePanel(RollrackLogo));
out.println("adding JLWelcome");
JLWelcome.setText("Welcome to Rollrack, " + namewindow.name);
add(JLWelcome);
JLWelcome.setVisible(true);
out.println("JLWelcome added");
out.println("adding JBRandom");
JBRandom.setText("Random");
add(JBRandom);
JBRandom.setVisible(true);
out.println("added JBRandom");
The background appears perfect, but not the JButton and JLabel!
Code to the StartImagePanel.java:
public class StartImagePanel extends JComponent{
private Image image;
public StartImagePanel(Image image) {
this.image = image;
}
#Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
Your button and label are added to your GameWindow frame while they should be added to its contentPane, setContentPane(new StartImagePanel(RollrackLogo)); instead. That's why they are not showing, they are added to the frame.
Make a variable of the StartImagePanel and add the button and label to it and they should show up.
StartImagePanel contentPanel = new StartImagePanel(RollrackLogo);
setContentPane(contentPanel);
...
out.println("adding JLWelcome");
JLWelcome.setText("Welcome to Rollrack, " + namewindow.name);
contentPanel.add(JLWelcome);
JLWelcome.setVisible(true);
out.println("JLWelcome added");
out.println("adding JBRandom");
JBRandom.setText("Random");
contentPanel.add(JBRandom);
JBRandom.setVisible(true);
out.println("added JBRandom");
Answer dispute
The claims in the first paragraph are plain wrong. Here is source that proves it.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class AddToCustomContentPane {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
JPanel gui = new JPanel(new FlowLayout());
gui.setBorder(new EmptyBorder(2, 3, 2, 3));
gui.setBackground(Color.RED);
JFrame f = new JFrame("Demo");
f.setContentPane(gui);
// Acid test. Can we add buttons direct to the frame?
f.add(new JButton("Button 1"));
f.add(new JButton("Button 2"));
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
Edit after the custom panel code was given
Here's a snippet that works to show both button and label on a black image background, I removed that was not needed (listeners).
public static void main(String[] v) {
class StartImagePanel extends JPanel {
private Image image;
public StartImagePanel(Image image) {
this.image = image;
}
#Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
class GameWindow extends JFrame{
public GameWindow() {
BufferedImage RollrackLogo;
RollrackLogo = new BufferedImage(400,200,BufferedImage.TYPE_INT_RGB);
final JButton JBRandom = new JButton();
final JLabel JLWelcome = new JLabel();
setDefaultCloseOperation(EXIT_ON_CLOSE);
StartImagePanel panel = new StartImagePanel(RollrackLogo);
setContentPane(panel);
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
JLWelcome.setText("Welcome to Rollrack");
panel.add(JLWelcome);
JLWelcome.setVisible(true);
JBRandom.setText("Random");
panel.add(JBRandom);
JBRandom.setVisible(true);
}
}
GameWindow window = new GameWindow();
window.pack();
window.setVisible(true);
}
I rather use an instance of a JFrame, instead of extending it, as #Andrew Thompson suggested in another question.
However, if you're extending it, it might be a good practice to call super() in the constructor.
Additionally, we may need to know what is going on in your StartImagePanel.
It seems, to me, to be the problem.
Ensure both your GameWindow and StartImagePanel extend properly their superclasses (call super();).
Ensure your StartImagePanel has a proper Layout.
Add your components before you set your frame visible. This also means you won't need JLWelcome.setVisible(true);.
Ensure that your code is executed in the EDT (Event-Dispatch Thread).
Example:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameWindow extends JFrame{
BufferedImage rollrackLogo;
JButton jbRandom;
JLabel jlWelcome;
public GameWindow() {
super();
jbRandom = new JButton("Random");
jlWelcome = new JLabel("Welcome to Rollrack, " +
namewindow.name);
rollrackLogo = new BufferedImage(400, 200,
BufferedImage.TYPE_INT_RGB);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setContentPane(new StartImagePanel(rollrackLogo));
// Add your components.
add(jlWelcome);
add(jbRandom);
addKeyListener(new KeyListener() {
#SuppressWarnings("static-access")
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == e.VK_ESCAPE){
System.exit(7);
}
}
#Override
public void keyReleased(KeyEvent arg0) {}
#Override
public void keyTyped(KeyEvent arg0) {}
});
// Pack, or otherwise set fullscreen.
pack();
// Now, set frame visible.
setVisible(true);
}
}
Edit: Now that you've posted the code for your StartImagePanel, I see that you're extending JComponent. Follow my previous advice, (call super), set a Layout, and extend JPanel instead.