I keep mixing up JFrame and JPanel. I have a code that compiles but nothings happens and the program ends.
This is a homework assignment that requires us to draw a triangle. Then add a button, when button is pressed the triangle will flip upside down. Press again to return to the first location.
Any assistance would be great. This is my countless rough draft and I'm just trying to initiate the object to view.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import java.awt.Color;
public class Triangle extends JPanel implements ActionListener
{
JButton button = new JButton("Just Flip It");
public Triangle()
{
add(button);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawLine(125,75,100,200);
g.drawLine(125,75,150,200);
g.drawLine(100,200,150,200);
}
public static void main(String[] args)
{
Triangle frame = new Triangle();
frame.setSize(400, 400);
frame.setVisible(true);
}
}
Think of a JPanel as a piece of paper and a JFrame as a book.
If you never add the piece of paper to the book, it will never be shown.
So, here are the steps to get your program running:
Create a JFrame that will hold your JPanel (Triangle) inside it
Add some logic in your ActionListener to have a boolean flag, that will be changed to true or false depending on the current state and based on it repaint the pane
Have some logic inside the paintComponent(...) method that draws the triangle upside down or upside up based on the state of the boolean flag above
So, from the above in your main(...) method you should have
JFrame frame = new JFrame("My frame");
//Initialize your Triangle here
frame.add(triangle);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
Your actionPerformed() method should look like this:
public void actionPerformed(ActionEvent e)
{
clicked = !clicked;
repaint();
}
And your paintComponent():
super.paintComponent();
if (clicked) {
//Draw upside up
} else {
//Draw upside down
}
As we're using pack() instead of setSize(...) method, we should override getPreferredSize() method of our JPanel as well:
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
Otherwise we could get a really small JFrame (and it's a good practice to do it this way)
Also don't forget to place your program on the EDT (as I show in the point #2 of this answer)
As a side note, you might also want to move to use the Shape's API as recommended by #MadProgrammer in this answer
Both links above have cool examples and tips, check them out!
You need to add your JPanel to a JFrame object in your main method in the same way you add a button to your panel. The JFrame is the window that your JPanel exists inside of.
Make sure that you set the JFrame object to visible and set its size.
Not related to your question, but I would suggest at least separating out a private class for you ActionListener rather than having the Triangle class be both. If you're feeling adventurous, you could look into using an anonymous inner class or a lambda expression.
Related
I have 2 issues.
First issue: I have to set the JFrame as non resizable, however, an error is thrown up when I enter in frame.setResizable(false);
Second issue: I have ran into the problem of the JFrame not fitting the component within it perfectly. I have set the dimensions for the JFrame to 600x720 and the board component as being 600x600. However, when I extend the JFrame I can see that there is more of the component to be revealed. How would I change this to accommodate the component to fit snugly but also leave space for another component of size 100x120? My understanding of this is that JFrame sets the size with the borders included, however, I want the space within the JFrame to be exactly 600x720 pixels without including the border.
The code is set out below.
Game Class
package snake;
//This class is used to run the game.
public class Game {
/**
* #author HyperBlue
*/
//Declaring a static variable of type Board. This can be accessed from anywhere in the program. The fact that it is static means that it cannot be edited.
public static Board board;
public static void main(String[] args) {
// TODO Auto-generated method stub
//Creates an object board from the Board() construct
board = new Board();
}
}
Board Class
package snake;
//Importing allows us to use pre-defined classes in Java, this contains its methods. We can also import entire packages which contain a number of classes in that package.
//This class allows us to assign/capture the width and height of an object.
import java.awt.Dimension;
//The Toolkit is an abstract class containing abstract and (possibly) non-abstract methods. Abstract classes cannot be instantiated (i.e. we cannot make an object from them). Abstract methods have no body (no code), for example we declare it as "public abstract boolean isChanged() ;", the semi colon shows it has no body (i.e. no {}).
import java.awt.Toolkit;
//ActionEvent gets information about an event (input) and its source. You can create an object from this.
import java.awt.event.ActionEvent;
//The ActionListener defines what should be done when a certain action is performed by the user.
import java.awt.event.ActionListener;
//This imports the JFrame class from the swing package.
import javax.swing.JFrame;
import javax.swing.Timer;
//This class is used to create the game board.
//The ActionListener is implemented because it is implementing an interface. What ActionListener does is it handles events; the ActionListener defines what should be done when a certain action is performed by the user.
public class Board implements ActionListener {
//The JFrame is the window in which everything will be placed into, this will provide the framed window (what is visible to us) in which the game will run in. We are creating a variable frame of type JFrame.
public JFrame frame;
//Creating a variable drawBoard of type DrawBoard. This allows us to add the component of drawBoard to the Board.
public DrawBoard drawBoard;
//Defining a new Timer called ticker. This is using the form new Timer(int delay in milliseconds, ActionListener listener). What the timer does is it allows threads to schedule the execution of instructions. In this case to constantly refresh the drawBoard component at regular intervals. This will give the appearance of motion. What "this" does is it is in reference to the current instance,
public Timer ticker = new Timer(20, this);
//This is a constructor for the class Board. This will allow us to create an object.
public Board() {
//Making an instance of dimension dim and assigning it to the size of the screen.
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
//Declaring instance of the JFrame 'frame'. This JFrame is called to declare a title for this frame - "Snake".
frame = new JFrame("Snake");
//JFrame is initially set to invisible, so we use the setVisible method (setting it to true) to make the JFrame 'frame' visible.
frame.setVisible(true);
frame.setPreferredSize(new Dimension(600, 720));
frame.getContentPane().add(drawBoard = new DrawBoard());
frame.pack();
//What this does is it places the JFrame 'frame' into the middle of the user's screen, this diminishes the issue of not all screens being the same resolution and size. This is done by setting the (x, y) position of the JPanel. For example, the x position is gained by dividing the size of the monitor by 2 and negating the size of the JPanel by 2 from that value, this places it in the middle of the screen's x axis. This is true for the y-axis too.
frame.setLocation((dim.width / 2) - (frame.getWidth() / 2), (dim.height / 2) - (frame.getHeight() / 2));
//Sets the operation which will happen when the user closes the JFrame 'frame', the EXIT_ON_CLOSE exits the application using the System exit method. This means that when the JFrame is closed, the application will be exited (closed).
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Starts the timer, this starts is sending action events to its listeners.
ticker.start();
}
//Overriding the actionPerformed method from the ActionListener class.
#Override
//This is the actionPerformed method. We parse it the ActionEvent e, what this is is an object which gives information about the event and its source. This allows us to perform an action based upon a specific event (e.g. a keyboard key pressed).
public void actionPerformed(ActionEvent e) {
//This repaints this component for every tick
//drawBoard.repaint();
}
}
DrawBoard Class
package snake;
//Allows use of default sRGB colours.
import java.awt.Color;
//Graphics is an abstract class that allows us to draw onto components.
import java.awt.Graphics;
import javax.swing.JPanel;
//Warnings will not be thrown (are suppressed).
#SuppressWarnings("serial")
//This class is used to create the board component in which the snake can move on.
//What extending does is it allows us to inherit the methods and attributes (properties) of another class. In this case, the DrawBoard class (subclass - inherits state and behaviour from all of its ancestors) inherits properties from the JPanel class (superclass - gives properties to its subclasses).
public class DrawBoard extends JPanel{
//Declaring the colour 'yellow' as the hex colour code (turned to decimal using a hex calculator so Java can use it) which was chosen in the design stage.
public static Color yellow = new Color(13816442);
//We are overriding the protected method in order to define our own body (and properties) for the paintComponent method. Overriding this allows us to define how we will paint the component DrawBoard. Protected means that it can only be accessed by things within the same package.
#Override
//A component is an object which has a graphical representation that can interact with the user (e.g. buttons).
//What this does is it paints the component using the graphics class, defined as instance g.
protected void paintComponent(Graphics g) {
//'Super.' refers to the method calling its super class, which in this case is JPanel. Doing this allows me to use in built 'drawings' such as rectangle and oval, which can be drawn by calling their methods.
super.paintComponent(g);
//Setting the colour in which graphics objects are made to the colour defined in the colour 'yellow'
g.setColor(yellow);
//Filling in a rectangle which starts at the point (0, 120) - [this is from the top left of the screen, with (0, 120) referring to 120 pixels down] and has a width and height of (600, 600), in other words provides a background of colour 'yellow' defined.
g.fillRect(0, 120, 600, 600);
}
}
Regarding:
How to Get JPanel to Fit Snugly With It's Components?
Let the layout managers do this work for you.
Suggestions:
Don't set sizes or preferred sizes.
Instead let the component's preferred size and your layout managers do the sizing for you.
If you do need to actively have a hand in setting some sizes, override getPreferredSize() and return an appropriate dimension, but do so taking care not to upset the preferred size of the constituent components. This must be done with care.
Re " I have set the dimensions for the JFrame to 600x720 and the board component as being 600x600. However, when I extend the JFrame I can see that there is more of the component to be revealed." -- You're forgetting the size of the top bar of the JFrame, something that may change size depending on the look and feel. Again, don't set the JFrame's size, and this will be a moot point.
To center a JFrame, simply call frame.setLocationRelativeTo(null); after it has been packed.
Avoid over-use of comments as these make your code nearly unreadable.
If you have problems and need help with an error such as you mention here: "I have to set the JFrame as non resizable, however, an error is thrown up when I enter in frame.setResizable(false);", then show the offending code and the error message.
Don't call frame.setVisible(true); until all components have been added and the JFrame has been packed.
For example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class Game {
public static Board board;
public static void main(String[] args) {
board = new Board();
}
}
class Board implements ActionListener {
public JFrame frame;
public DrawBoard drawBoard;
public Timer ticker = new Timer(20, this);
public Board() {
frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setPreferredSize(new Dimension(600, 720));
frame.getContentPane().add(drawBoard = new DrawBoard(), BorderLayout.CENTER);
frame.getContentPane().add(new BottomComponent(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ticker.start();
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
class DrawBoard extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
public static Color yellow = new Color(13816442);
public DrawBoard() {
setBorder(BorderFactory.createTitledBorder("Draw Board"));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(yellow);
g.fillRect(0, 120, 600, 600);
}
}
class BottomComponent extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 120;
public BottomComponent() {
setBorder(BorderFactory.createTitledBorder("Bottom Component"));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
I am new to Java....I studied that we can add two things on frame... I added button and in response by clicking on button I want rectangle as output....but i don't understand that..Why i am not getting output.....
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class customizedgui5 implements ActionListener {
JButton button;
JFrame frame;
public static void main(String[] args) {
customizedgui5 hi = new customizedgui5();
hi.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("click me");
button.addActionListener(this);
myclass a = new myclass();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.getContentPane().add(a, BorderLayout.SOUTH);
frame.setSize(100, 100);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
frame.revalidate();
}
}
class myclass extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
I would start by taking a look at Performing Custom Painting.
The main problem in your code is you are getting NullPointerException when you click the button because the reference of frame is null.
It is null because you've shadowed it in the constructor (basically, declared another variable of the same name within the constructor)...
// I'm null..
JFrame frame;
public void go() {
// Not the same as frame above...
JFrame frame = new JFrame();
You are also going to not see any changes because of a number of reasons...
The myclass panel has no size. With BorderLayout, this won't be "too" much of a problem, but...
You've drawing outside of the visible range of the panel. The rectangle you are painting won't appear because it is being painted outside of the width and height of the panel.
The rectangle will appear before you press the button as paintComponent will be called to update the state of the panel once it's made visible on the screen...
The first thing you need to is provide some kind of size hints to the BorderLayout. Try adding...
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
To myclass.
You also don't need to repaint the frame, what you really want to repaint is the instance of myclass. Try updating customizedgui5 so that a becomes a instance variable (like frame...
//...
myclass a;
//...
public void go() {
//...
a = new myclass();
//...
}
public void actionPerformed(ActionEvent event) {
a.repaint();
}
Now, the rectangle will still be shown the moment that the panel is made visible on the screen. Sure you could try setting it invisible, but this will affect the layout of the frame, hiding your component to start with, so, instead, we need some kind of flag we can trip so we know when to paint the rectangle. This is easily achieved by using a simple boolean variable, for example...
class myclass extends JPanel {
private boolean paintRect;
public void setPaintRect(boolean paint) {
paintRect = paint;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (paintRect) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
}
Then in you actionPerformed method, you just need to set the flag...
public void actionPerformed(ActionEvent event) {
a.setPaintRect(true);
}
You may also want to take a read through Code Conventions for the Java Programming Language. It will make it easier for people to read your code...
When you click on your button, you're calling the method actionPerformed(ActionEvent event)
Take a look at what you did there. Currently, you repaint and re-validate the frame. If you want to add a rectangle to your frame, you need to do so by adding a new component to the frame that will draw the rectangle.
You could add another instance of your myclass JPanel which paints a rectangle like so:
public void actionPerformed(ActionEvent event) {
frame.getContentPane().add(new myclass(), BorderLayout.NORTH);
frame.repaint();
}
This would add your custom rectangle-drawing panel to the North section of your BorderLayout. If you want to add the rectangle "on top of" your button, you should embed your button within a JPanel, then add the rectangle-drawing panel to that instead of your main JFrame
I have been playing around with Java's 2d painting tools and have hit a snag. I am attempting to move the objects. Here is the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test extends JPanel{
private int[] location = new int[2];
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillArc(location[0], location[1], 100, 100, 45, 90);
g.setColor(Color.black);
g.fillArc((location[0]+50-10),(location[1]+50-10), 20, 20, 0, 360);
new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setLocation((location[0]+50),50);
repaint();
System.out.println("repainting");
}
}).start();
}
public void setLocation(int x, int y){
this.location[0] = x;
this.location[1] = y;
}
public static void main(String[] args){
JFrame jf=new JFrame();
jf.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
jf.setPreferredSize(new Dimension(300,500));
jf.setLocation(100,100);
jf.add(new Test());
jf.pack();
jf.setVisible(true);
}
}
This only paints one of the two objects to the screen... it seems to be the second one as when I change the parameters of setLocation on [1] the one object it does paint moves. Any thoughts? Thanks
Edit: Edited above code to reflect what was said below.
You are adding two components to the JFrame in a default way. This will add the components BorderLayout.CENTER and so the second component will cover and obscure the first. You will want to read up on layout managers to fix this. Also read up on Swing Timers for simple animations, since your code, even if written correctly would do no animation.
If you want to move the drawing, then
Use only one Test JPanel
Override JPanel's paintComponent(...) method, not paint(...) method.
call the super.paintComponent(g) method first thing in your paintComponent method override.
Give the Test JPanel public methods to allow outside classes to change the location without having them directly futz with the field. Make the location field (name should begin with a lower-case letter) private just to be safe.
Use a Swing Timer to periodically call this method and change location, then call repaint() on the JPanel.
I have a class with two JFrames and am trying to draw a line on a particular frame .
I tried the code below but it only appears in the first frame which is the success frame.
It also appears above all the other components of the success frame thus making all other
components invisible. It does not appear in the comp Frame.
How do I correct this.
Here is the code I have so far :
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
public class lineGUI{
public static void main(String []args){
Success s=new Success();
s.setVisible(true);
}
}
class Success extends JFrame{
JPanel alas =new JPanel();
JFrame comp =new JFrame();
public Success(){
JPanel panel=new JPanel();
getContentPane().add(panel);
setSize(450,450);
JButton button =new JButton("press");
panel.add(button);
comp.setSize(650,500);
comp.setTitle("View Report");
JRootPane compPane=comp.getRootPane();
Container contePane=compPane.getContentPane();
contePane.add(alas);
ActionListener action =new ActionListener(){
public void actionPerformed(ActionEvent e){
if (e.getSource()==button){
comp.setVisible(true);
}
}
};
button.addActionListener(action);
JButton button2=new JButton("access");
alas.add(button2);
}
public void paint(Graphics g) {
comp.paint(g);
Graphics2D g2 = (Graphics2D) g;
Line2D lin = new Line2D.Float(100, 100, 250, 260);
g2.draw(lin);
}
}
You've got some crazy code up there. Suggestions:
Don't draw directly in a JFrame, but in a the paintComponent method of an object derived from JComponent such as JPanel or JComponent itself.
Your drawing directly in another component's paint(...) method is not kosher at all. Why not simply share the data between classes, and use the data (the ints) to draw where desired.
You would rarely want to have a GUI display more than one JFrame at a time. Usually one window is the main window (the JFrame), and it often owns any other windows which would be dialog windows such as JDialogs.
Read the graphics tutorials to learn the correct way to do Swing Graphics
Two things:
If you want to draw in the "comp" frame, then you should extend that frame explicitly to overload its paint method. Right now you're overloading the paint method of "Success" frame. The line comp.paint(g) is using the paint method of comp (a standard JFrame) to draw on the Graphics object of the "Success" frame. You probably want to make that into super.paint(g) instead, then put this paint function into it's own JFrame and create comp from that.
http://pastebin.com/ZLYBHpmj
(Sorry, first post, couldn't figure out how to get Stackoverflow to quit complaining about format)
I want to put a JPanel in a JApplet, the problem is that I can't see it :( I've overridden the paintComponent of my JPanel in order to have a background image, but I can't see anything. When I remove the paintComponenet method that I had overriden, and set a color to the background of this panel, it seems that JPanel fills the JApplet and still no component is visible :-S I've tried different layouts. I also put my panel in the center of another panel which fills my JApplet but nothing changed, and still no component and no background image is visible :(
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main extends JApplet implements Runnable{
private JTextArea display;
private Thread outputThread;
JPanel boardPanel;
private ClientViewManager view;
#Override
public void init() {
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
private void createGUI() {
display = new JTextArea(4, 30);
display.setEditable(false);
getContentPane().add(new JScrollPane(display), BorderLayout.SOUTH);
setFocusable(true);
setVisible(true);
setName("CE Tanks");
setSize(600, 600);
setLocation(100, 100);
boardPanel = new JPanel();
boardPanel.setLayout(null);
boardPanel.setBackground(new java.awt.Color(128, 255, 255));
getContentPane().add(boardPanel, BorderLayout.CENTER);
}
public void start() {
outputThread = new Thread(this);
outputThread.start();
}
public void run() {
view = new ClientViewManager();
boardPanel.add(view);
boardPanel.repaint();
repaint();
}
}
class ClientViewManager extends JPanel {
private int rows=8;
private int columns=8;
public ClientViewManager() {
super(null);
JLabel lb= new JLabel("lb.jpg");
lb.setLocation(10, 10);
lb.setSize(50, 50);
lb.setOpaque(false);
lb.setVisible(true);
this.add(lb);
}
public void paintComponent(Graphics g) {
g.drawImage(new ImageIcon("ground.jpg").getImage(), 0, 0, columns * 50,
rows * 50, this);
}
}
The code above can be compiled. I cant even add Keylistener to neither my JPanel nor to my JApplet. I used java.awt.KeyEventDispatcher and in dispatchKeyEvent(KeyEvent e) I printed something in console but, it was printed 3 times. :(
I've overridden the paintComponent of my JPanel inorder to have a background image,
But you didn't add the custom component to your applet:
//boardPanel = new JPanel();
boardPanel = new ClientViewManager();
Also:
get rid of setVisible(). This is not required for any of the controls in your program. By default all components except top level Container (Jframe, JDialog etc) are already visible. In the case of JApplet, you don't need to make it visible as this is part of the process of displaying an applet.
get rid of setSize() and setLocation() you can't control the position of the applet this way.
Don't read the image file in the paintComponent() method. This is not efficient as this method is invoked whenever Swing determines the component needs to be repainted.
JLabels are opaque by default so there is not need to invoke the setOpaque method.
When doing custom painting you should also override the getPreferredSize() method of the component to return the proper size of the custom painting so layout managers can use this information. It works in this case because you added the panel to the CENTER of the BorderLayout. Try adding the panel to the NORTH to see what happens.
Edit:
Now I see where you are adding the ClientViewManager. I'm not sure why you are trying to do this with a Thread but once again there are several problems.
When you add/remove components from a visble GUI then the basic code is:
panel.add(...);
panel.revalidate();
panel.repaint();
However this still won't work because you are using a null layout and the size of the panel is 0. Use a proper layout manager and implement the getPreferredSize() method as suggest above and the component will be displayed properly.
I recommend you to use the GUI Builder of Netbeans to build a GUI like that, and then compare the generated code to your code. Netbeans results really useful to help you create swing code.