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);
}
});
}
}
Related
I am trying to make a Swing GUI that includes some 3D stuff using Java3D's Canvas3D object. The problem is that it takes a while for a Canvas3D object to initialize, and I want the Swing GUI to come up right away. My solution to this problem is to initialize the Canvas3D in a separate thread, and then add it to the JFrame once it is initialized. However, when that separate thread adds the Canvas3D to the JFrame, the window loses focus for a moment, which is undesirable. How can I prevent that from happening? I have included a simple example to illustrate what I am trying to do:
public class Main extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main()::setup);
}
private void setup() {
setSize(600, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
Thread thread = new Thread() {
#Override
public void run() {
Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
SwingUtilities.invokeLater(() -> {
Main.this.add(canvas); //the window loses focus for a moment here
Main.this.revalidate();
});
}
};
thread.start();
}
}
I am using Java3D 1.7.1.
I have modified my code as per R VISHAL's comment, but the problem still persists.
public class Main extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main()::setup);
}
private void setup() {
setSize(600, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
SwingWorker<Canvas3D, Object> worker = new SwingWorker<Canvas3D, Object>() {
#Override
public Canvas3D doInBackground() {
return new Canvas3D(SimpleUniverse.getPreferredConfiguration());
}
#Override
public void done() {
try {
Main.this.add(get());
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException();
}
Main.this.requestFocusInWindow();
Main.this.revalidate();
}
};
worker.execute();
}
}
Ok so this might not be the answer but it was to large to put it as an comment
Use a JPanel having a CardLayout as your frame's contentpane, have one screen inside this panel set as the background[or whatever initial screen you want to display before the canvas is displayed], and then once the canvas is initialized add it to the content pane as the second screen and then call the CardLayout's show() method do display the canvas
public class Add
{
public static void main(String args[])
{
JFrame frame=new JFrame("Test");
JPanel mainPanel=new JPanel(new CardLayout());
mainPanel.addMouseListener(new MouseAdapter()
{
int count=0;
#Override
public void mouseClicked(MouseEvent m)
{
System.out.println("Focus "+(++count));
}
});
JPanel background=new JPanel();
background.setBackground(Color.WHITE);
mainPanel.add("Screen1",background); //Initial Screen To Show Something To The User While Canvas Is Being Initialized
frame.setContentPane(mainPanel);
SwingWorker worker=new SwingWorker<Canvas3D,Object>()
{
#Override
public Canvas3D doInBackground(){return new Canvas3D();}
#Override
public void done()
{
try
{
mainPanel.add("Screen2",get()); //Add Canvas To MainPanel
CardLayout layout=(CardLayout)mainPanel.getLayout();
layout.show(mainPanel,"Screen2"); //Remember This While Using CardLayout
}
catch(InterruptedException | ExecutionException ex){}
}
};
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
worker.execute();
}
private static final class Canvas3D extends JPanel
{
private Canvas3D()
{
super(new BorderLayout());
try{Thread.sleep(5000);}//Mimicing Long Operations
catch(Exception ex){}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0,0,500,500);
g.setFont(new Font("",Font.BOLD,15));
g.setColor(Color.RED);
g.drawString("CANVAS 3D",100,100);
}
}
}
Of course you would use your actual canvas3d instead of this custom one and there is no need to revalidate or requestFocus()
If you are still worried about focus you could always create an javax.swing.Timer class to request focus to your mainPanel or frame[Or Both See What Works For You] every second/or millisecond
Timer timer=new Timer(1000,new ActionListener() //milliseconds make it 1 if your are dead serious about focus
{
#Override
public void actionPerformed(ActionEvent e)
{
mainPanel.requestFocusInWindow();
frame.requestFocusInWindow(); //May not be required since we are already requesting focus in mainPanel
}
});
timer.start();
If you want to get even more paranoid about focus you could always add an focus listener
mainPanel.addFocusListener(new FocusListener()
{
#Override
public void focusLost(FocusEvent e)
{
mainPanel.requestFocusInWindow();
frame.requestFocusInWindow();
}
#Override
public void focusGained(FocusEvent e){}
});
If any of these suggestions did/didn't work comment below :)
Can i paint with graphics g on a Jpanel and if i can how does it work?
JPanel Game = new JPanel();
Game.setLayout(null);
Game.setLocation(0,0);
Game.setSize(500,700);
Game.setBackground(Color.WHITE);
Game.setBorder(BorderFactory.createLineBorder(Color.black));
how can i add paintcomponent on this Jpanel
Yes, for custom painting you have to subclass your JPanel then you have to override paintComponent calling in first line super.paintComponent(..).
Example :
JPanel game = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//custom painting here
}
};
//game.setLayout(null); don't use null layout
game.setLocation(0,0);
game.setSize(500,700);
game.setBackground(Color.WHITE);
game.setBorder(BorderFactory.createLineBorder(Color.black));
Read more in Painting in AWT and Swing | Performing custom painting in Swing
Note: In java variable's name start with lower-case following a camel style by convention. So Game should be game.
If you want to implement your own custom painting on a component you will have to make your own class which extends that component. You will start with something like
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Add your custom painting here
}
}
You can then use that panel with something like
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
System.out.println("Created GUI on EDT? " + SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
Why JFrame is empty after my graphics
package javaGame;
import java.awt.Graphics;
import javax.swing.JFrame;
public class javaGame extends JFrame {
public javaGame (){
setVisible(true);
setSize(250,250);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Java Game");
}
public void paint (Graphics g){
g.drawString("Hello world!", 75, 75);
}
public static void main (String[] args){
new javaGame ();
}
}
You are used to applets, where overriding paint will actually work. In a JFrame, the recommended technique is a little more complicated. Add this code to your constructor:
getContentPane().add(new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Hello world!", 75, 75);
}
});
This creates an anonymous subclass of JPanel which overrides paintComponent to do some painting, then adds the JPanel to the frame. The call to super.paintComponent is important.
There are several other things that you should know are different from applets:
You need to call setVisible(true) to make the frame appear. This is mandatory for your program to work.
You should add setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) if you want the program to be closed if the window is closed. Highly recommended.
When you opening a new frame you should make it visible after validate or pack is applied to it, also call setVisible(true) at the end of the frame creation. The code for modification:
public void paint (Graphics g){
super.paint(g);
g.drawString("Hello world!", 75, 75);
}
1) Follow java code conventions javaGame should be JavaGame
2) Swing programs should override paintComponent() instead of overriding paint().
3) You should make custom painting in a JComponent like JPanel.
Change your code to for example something like this.
public class JavaGame {
private JFrame frame; //use composition instead of concrete inheritance
public JavaGame () {
jframe.setSize(250,250);
jframe.setResizable(false);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setTitle("Java Game");
jframe.add(new JPanel() {
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.drawString("Hello world!", 75, 75);
}
});
jframe.pack(); //sizes the frame
jframe.setVisible(true);
}
public static void main (String[] args) {
SwingUTilities.invokeLater(new Runnable() {
#Override
public void run() {
new javaGame();
}
});
}
}
I created two classes which one extending. I create an object, but a file is not appearing (method work fine, because title form draw() method appears. There is all code:
public class Main_class extends JFrame implements ActionListener{
//**************************//
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable(){
public void run(){
new Main_class().setVisible(true);
}
});
}
//**************************//
JPanel panel;
JMenuBar mbar;
JMenuItem item;
JMenuItem open;
JMenu file;
BufferedImage my_image;
public Main_class(){
setSize(800, 600);
setTitle("TEST");
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel=new JPanel();
mbar=new JMenuBar();
setJMenuBar(mbar);
file=new JMenu("File");
mbar.add(file);
open=new JMenuItem("Open");
open.addActionListener(this);
file.add(open);
}
#Override
public void actionPerformed(ActionEvent e) {
String zrodlo=e.getActionCommand();
image_class k=new image_class();
if(zrodlo.equals("Open")) try {
k.load(my_image);
} catch (IOException ex) {
Logger.getLogger(Main_class.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
///////////////////////////////////////////
public class image_class extends Main_class{
public void load(BufferedImage my_image) throws IOException{
JFileChooser open_chooser=new JFileChooser("//");
FileNameExtensionFilter rast=new FileNameExtensionFilter("Pliki grafiki rastrowej(.jpeg,.png.,gif...)", "jpeg","jpg", "gif","png","bmp");
open_chooser.setFileFilter(rast);
int a=open_chooser.showOpenDialog(null);
if(a==JFileChooser.APPROVE_OPTION){
String image_name=open_chooser.getSelectedFile().getAbsolutePath();
String roz=image_name.substring(image_name.lastIndexOf('.')+1);
my_image=ImageIO.read(open_chooser.getSelectedFile());
draw();
}
}
public void draw(){
panel=new JPanel(){
protected void paintComponent(Graphics g){
Graphics g2 = g.create();
g2.drawImage(my_image, 0, 0, getWidth(), getHeight(), null);
g2.dispose();
}
};
panel.setBounds(0, 0, 200, 200);
add(panel);
revalidate();
repaint();
System.out.print("LOADED!!!!!!");
}
}
Your image does not appear as you
Never display your 2nd JFrame, image_class, by calling setVisible.
Because Java is pass-by-value, you are not assigning my_image to the class member variable of the same name in your load method, but rather a local copy of the variable:
Replace
my_image = ImageIO.read(open_chooser.getSelectedFile());
with
this.my_image = ImageIO.read(open_chooser.getSelectedFile());
(or simply don't pass in the variable)
Would recommend that you use a single JFrame here, add a sub-class of JComponent and draw the image on that component.
Related:
The Use of Multiple JFrames, Good/Bad Practice?
How to add an image to a JPanel?
One of your main problems is that you're misusing inheritance. Your code here from image_class.java:
public void draw() {
panel = new JPanel() {
protected void paintComponent(Graphics g) {
Graphics g2 = g.create();
g2.drawImage(my_image, 0, 0, getWidth(), getHeight(), null);
g2.dispose();
}
};
panel.setBounds(0, 0, 200, 200);
add(panel);
revalidate();
repaint();
System.out.print("LOADED!!!!!!");
}
You are adding the new JPanel to a Main_class instance, but not the one that is displayed but rather to the one that image_class inherits from. These are two completely distinct objects and making changes to one will not affect the other.
The solution is not to misuse inheritance for this but rather to display the image in the original GUI.
Also, you should never dispose of a Graphics object that was given to you from the JVM as this can have nasty side effects.
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.