I'm writing a Java GUI program that uses a JFrame object and several JPanel objects. The JPanel objects use the paint() and repaint() methods, and some of the JPanel ojbects have JLabels and JButtons. When I run the program some of these various components are not visible in the way I want them to be i.e. it seems though some are hiding others.
What determines which components are visible when the program is run? Does it depend on the order in which the components are added to the top-level container, or the order in which each object's paint() method is called? Can this default visibility be overridden?
Edit: I should add that some JPanels (and their components) overlap each other.
Edit:
Here is an example program. What determines which components are visible on screen? For example, why is Button1 visible when Button2 is not?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test1 extends JFrame {
public Test1() {
setLayout(null);
setSize(500, 700);
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(new Test2());
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Test1();
}
});
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test2 extends JPanel {
public Test2() {
setBounds(0, 0, 300, 500);
setLayout(null);
add(new Test3());
add(new Test4());
setVisible(true);
}
public void paint(Graphics g) {
g.drawRect(0, 0, 250, 450);
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test3 extends JPanel {
public Test3() {
setBounds(0, 0, 300, 300);
setVisible(true);
}
public void paint(Graphics g) {
g.fillRect(40, 50, 200, 150);
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test4 extends JPanel {
public JLabel label1 = new JLabel("Label1");
public JLabel label2 = new JLabel("Label2");
public JButton button1 = new JButton("Button1");
public JButton button2 = new JButton("Button2");
public Test4() {
setBounds(0, 300, 300, 200);
add(label1);
add(label2);
add(button1);
add(button2);
setVisible(true);
}
}
I should add that some JPanels (and their components) overlap each other.
The ZOrder controls the order in which components are painted. A component with a higher ZOrder is painted first.
Does it depend on the order in which the components are added to the top-level container
Yes, the ZOrder is assigned as a Component is added to the Container, so the last component added will be painted first.
You can use the setComponentZOrder(...) method of the Container class to manually change the ZOrder (and thus the order of painting). For example if you drag a component and want it painted on top, you would set its ZOrder to 0.
You may want to check out the Overlap Layout. It allows you to control how the components are displayed wen they overlap (although is does make all components the same size)
Here is an example program.
You should never write an application like that as you will get all kinds of problems:
Don't use the null layout managers.
Don't override paint(). Custom painting is done by overriding paintComponent() and making sure you invoke super.paintComponent(...).
Don't stack panels on top of one another. A JPanel is opaque, so adding components on top of one another will hide the component below.
why is Button1 visible when Button2 is not?
Button1 is painted because it has focus. If you move your mouse beside button2 it will also be painted because it responds to the mouseOver event.
If you resize the frame, both button will disappear because the panels will be repainted based on ZOrder.
I really have no idea what you are trying to do with that code. All I can say is don't do it. Use layout managers.
Related
I have issue with drawing shapes inside of JPanel that I already added using Netbeans GUI. Now, I have no idea where to add code for drawing a circle inside of that JPanel and how to insert and call it in the JPanel that is sitting empty now, waiting for this shape to be drawn. I already set up destination JPanel to be Flow layout.
Netbeans Designer created a big class in which I have entire frame with this JPanel, and I want to keep it inside of it as I can't really add it any other way because Designer doesn't let me change main initComponents method in which all components are sitting now. I have been reading tutorials and previous posts but noone really encountered this using Netbeans Designer.
SO can someone just help me with adding proper method in this frame class and how to call it from JPanel I want to draw in. JPanel is 50x50 pixels.
So as per #Abra, I changed some code:
so I made a new Circle Class, adjusted it a bit as I don't want to create a new frame but put this in JPanel.
public class Circle extends JPanel {
Color color;
public void circle(Color color) {
this.color = color;
setPreferredSize(new Dimension (30,30));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(0, 0, r, r);
g.setColor(color);
}
private void showGUI() {
JPanel panel = new JPanel();
panel.add(this, FlowLayout.CENTER);
panel.setVisible(true);
}
}
Then I opened JPanel in Designer, and added code to run it, in initComponents method like this:
circlePanel.setPreferredSize(new java.awt.Dimension(40, 40));
new Circle().showGUI();
PanelDS.add(circlePanel);
circlePanel is destination for this drawing and is inside PanelDS itself. It doesn't work this way tho, but Netbeans shows no errors in code. Additionally, how can I forward color to circle class.
In order to draw on a JPanel you need to override the paintComponent() method of JPanel. In order to override the method, you need to create a class that extends JPanel. I don't think that there exists a GUI designer that can generate the required code for you. So you have to write the code of the class that extends JPanel.
Here is a minimal example. It displays a blue circle.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class Drawing2 extends JPanel {
private JFrame frame;
public Drawing2() {
setPreferredSize(new Dimension(100, 100));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(25, 25, 50, 50);
}
private void showGui() {
frame = new JFrame("Drawing");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(this, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
new Drawing2().showGui();
}
}
Here's what you should see when you run the above code.
I've made a JFrame with Diferent JButtons and i'd like to get an image from another class. Any ideas? Or how draw on the same class but on the action performed?
Because it doesnt let me to do any drawings...my complier always gives me error messages
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.*;
public class red extends JFrame {
public JButton b;
public JButton b1;
public JButton b2;
public JButton b3;
public JButton b4;
public static Image p;
public static Graphics g;
public red() throws IOException {
gui1 x = new gui1();
setTitle(" ");
setSize(1200,700);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
b= new JButton("click");
b1= new JButton();
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e0){
b1.setBounds(0, 0, 200, 200);
b.show(false);
add(x);
}
});
b.setBounds(0, 0, 100, 100);
add(b1);
add(b);
setVisible(true);
}
public static void main(String[] args) throws IOException {
red k = new red();
}
}
import java.awt.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class gui1 extends Canvas {
public static Image p;
public void paint(Graphics g){
g.drawImage(p, 700, 200, 100, 100, this);
}
{
try {
p= ImageIO.read(new File("Lighthouse.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Phew! I see A LOT of errors in your code (even after I corrected the compilation errors):
You're not following the Java naming conventions:
Class names should be nouns, in mixed case with the first letter of each internal word capitalized
while red is a noun it should be more descriptive and be capitalized. The same goes for gui1
You're extending JFrame which in plain english would say: red is a JFrame, you should really avoid this and create your GUI based on JPanels instead... see Java Swing using extends JFrame vs callint it inside of class
You're setting size (a REAAAAAAALLY big one window for the JButton sizes you're using), instead use pack()
You're using null-layout, while pixel-perfect GUIs might seem like the easiest way to create complex GUIs for Swing newbies, the more you use them the more problems related to this you'll find in the future, they are hard to maintain and cause random problems, they don't resize, etc. Please read Null layout is evil and Why is it frowned upon to use a null layout in Swing? for more information about why you should avoid its use and why you should change your GUI to work with Layout Managers along with Empty Borders for extra spacing between components.
You're making use of a deprecated method JFrame#show() you should be using JFrame#setVisible(...) instead.
Related to point #4, you shouldn't be calling setBounds(...) method, but let that calculations to the layout managers.
You're not placing your program on the Event Dispatch Thread (EDT), Swing is not thread safe, you can fix this by changing your main() method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're mixing AWT and Swing components, instead of using AWT's Canvas use Swing's JPanel which has more functionality and support.
Images will become embedded resources once they're packaged in a JAR file, so it's wise to start treating them as if they already were, not as external files as shown in the embedded-resource tag.
Once you change from Canvas to JPanel you should override its paintComponent(...) method and not paint(...) and call it's super.paintComponent(g) method as the first line, also don't forget to add the #Overrides annotation. See the tutorial on Swing custom painting.
You're abusing the use of static keyword, see how does the static keyword works?
After seeing all the above errors I recommend you to go back and Learn the basics of the language before starting with a graphical environment which will only add more difficulty to your learning.
From what I understand you want to draw an image on a button click, if that's the case then you can wrap your image in a JLabel and add that JLabel to a JPanel which then is added to a parent JPanel which is later added to the JFrame:
As you can see in the GIF above, the icon is displayed after user presses the button.
Obviously this can be improved for the GUI to be more "attractive" with combinations of layout managers and empty borders as stated before.
This was done with the following code:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ImageDrawingFromOneClassToAnother {
private JFrame frame;
private JPanel pane;
private JPanel leftPane;
private JPanel rightPane;
private ImageIcon icon;
private JButton button;
private JLabel label;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageDrawingFromOneClassToAnother().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
icon = new ImageIcon(this.getClass().getResource("king.png")); //Read images as if they were already embedded resources
button = new JButton("Draw image");
label = new JLabel(""); //Create an empty label
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setIcon(icon); //On button click, we set the icon for the empty label
}
});
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200); //Set a size for the main panel
}
};
pane.setLayout(new GridLayout(1, 2)); //The main panel
leftPane = new JPanel(); //The button panel
leftPane.setLayout(new BoxLayout(leftPane, BoxLayout.PAGE_AXIS));
leftPane.add(button);
rightPane = new JPanel(); //The panel where the image will be drawn
rightPane.add(label);
//We add both (button and image) panels to the main panel
pane.add(leftPane);
pane.add(rightPane);
frame.add(pane); //Add the main panel to the frame
frame.pack(); //Calculate its preferred size
frame.setVisible(true); //Set it to be visible
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I want to create menu buttons in Screen class and add menu to frame then. I don't know what is wrong with it. How to create buttons in other class and add it to to the frame?
My frame class:
import java.awt.*;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
public class Start extends JFrame {
public static String title = "Bozenka";
public static Dimension size = new Dimension(700,500);
public static String backgroundPath = "/home/alpha_coder/Eclipse/Bozenka/images/bg.jpg";
public Start(){
setTitle(title);
setSize(size);
setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(null);
setResizable(false);
initialization();
}
public void initialization(){
Screen screen = new Screen();
screen.setBounds(20, 20, 660, 60);
add(screen);
try {
setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File(backgroundPath)))));
setBackground(Color.WHITE);
} catch (IOException e) {
System.out.println("Image doesn't exist");
}
setVisible(true);
}
public static void main(String[] args){
Start start = new Start();
}
}
The class where I want to create a menu:
import java.awt.*;
import javax.swing.*;
public class Screen extends JPanel{
public JButton test;
public Screen(){
setBackground(Color.pink);
test = new JButton("test");
test.setBounds(2, 2, 40, 10);
}
}
Most important: get rid of setLayout(null) and setBounds(...) as that will lead to an extremely difficult to create and adjust GUI. Learn and use the layout managers.
Create your JButtons in your new class, Screen
add them to this, the Screen JPanel but first give it a decent layout manager, such as a GridLayout,
In another class, create an instance of your JFrame class and an instance of the Screen object, and add the Screen JPanel to your JFrame's contentPane in whatever desired location you want, be it BorderLayout.CENTER or one of the other locations.
Again, most important: google and study the layout manager tutorial. Here's the link.
Note, a major problem with your current code is that you add your JButton to nothing. It needs to be added to Screen, to this for your code to work in any way.
Here is my code snippet containing child JButton and JPanel objects but it's not working. And it's not showing any compilation errors in Eclipse.
import java.awt.FlowLayout;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class gui extends JFrame implements ActionListener {
private JButton b;
private TextField c;
private JLabel l;
private String sn;
// Constructor for making framework
public gui() { setLayout(new FlowLayout());
JFrame f=new JFrame("Hello!");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.setSize(200,200);
f.setTitle("GUI");
b=new JButton("Click");
l=new JLabel("Enter Name");
c=new TextField("Enter..",10);
c.setEditable(true);
l.setBounds(20,20,20,20);
f.setBounds(10, 10, 10, 10);
b.addActionListener(this);
add(b);
add(f);
add(l);
add(c);
}
public static void main(String[] args) {
gui g=new gui();
g.setVisible(true);
} //main method
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Working");
}
}
Your class "is a" GUI and then you also create a new JFrame, so you really have two frames in your code.
However the frame you make visible does not have any components added to it so all you see is the frame.
You then attempt to add components to your class the is a frame. However, you then have two problems:
you never make this frame visible and
Swing uses layout managers (you don't need to use setBounds(...)). By default it is using the BorderLayout. When you add components to the frame without specifying a constraint the components get added to the "CENTER". However, only one component can be displayed in the "CENTER" so only the last one added will ever be visible.
You also have other problems because you don't create the GUI on the Event Dispatch Thread. So there are really too many problems to correct.
I suggest you read the section from the Swing tutorial on How to Use BorderLayout for a working example of how to create a simple GUI. Then modify that code.
Your JFrame f=new JFrame("Hello!"); is not needed.
You need to use this which is already your JFrame like:
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.setSize(200,200);
this.setTitle("GUI");
Also remove: add(f); and f.setBounds(10, 10, 10, 10);
Because you already extend JFrame, you don't have to create a new JFrame.
Because now your class is a JFrame itself. That means that you can replace every usage of your f-JFrame by using this instead:
That way, also your other Components will be added correctly. Because at the moment you add b, f, i and c to the right JFrame.
So use this:
this.setVisible(true);
this.setSize(200,200);
this.setTitle("GUI");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
or even more simple:
setVisible(true);
setSize(200,200);
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Can somebody please help me understand why my custom JComponent 'Bar', only displays when added directly to the JFrame, and not when added to a JPanel (which is then added to the JFrame)?
Thanks!
package main;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Board {
public void start(){
JFrame frame = new JFrame();
JButton button1 = new JButton("Button 1");
Bar statusbar = new Bar();
JLabel status = new JLabel("Status: ");
JPanel topPanel = new JPanel();
topPanel.add(status);
topPanel.add(statusbar);
JPanel mainPanel = new JPanel();
mainPanel.add(button1);
mainPanel.add(statusbar);
frame.getContentPane().add(BorderLayout.NORTH, topPanel);
frame.getContentPane().add(BorderLayout.SOUTH, mainPanel);
frame.getContentPane().add(BorderLayout.CENTER, statusbar);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200,100);
frame.setVisible(true);
}
}
Here is my Bar Class...
package main;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Bar extends JComponent{
public void paint(Graphics g){
g.fillRect(0, 0, 100, 10);
}
}
You're adding statusbar to several different places in the component tree, Swing doesn't deal with that well (or at all).
Create a separate Bar instances each time you use it, if you want their display to be synchronized, they should share the same model.
Edit
Ah, on a second glance, the problem here is that you never set a size (or preferred size) for the Bar components, so they get squished to 0 by the layout manager.
Try:
public static class Bar extends JComponent {
private Bar() {
setPreferredSize(new Dimension(25, 5));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect(0, 0, 100, 10);
}
}
You should also add a frame.pack() before display.
(the multiple references to the same component thing is still true, too)
The dimension of the custom component is (0, 0).
When added to a container with a BorderLayout layout manager, it will expand to fill the available space, and therefore become visible.
When added to a container with a FlowLayout layout manager, it will not expand and will instead remain at its preferred size (i.e. (0, 0)). And therefore, will not become visible, albeit it is still there.
This explains why the custom component is only displayed when added directly to the JFrame, since it uses a BorderLayout layout manager, whereas a JPanel uses a FlowLayout layout manager.