I want to learn to use ArrayList together with graphical elements in java.
I have a class that creates squares with a random x and a random y position:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Square extends Canvas {
public int x, y;
Random r = new Random();
public void paint(Graphics g) {
x = r.nextInt(640);
y = r.nextInt(480);
g.setColor(Color.BLACK);
g.fillRect(x, y, 30, 30);
}
}
And i have a class that creates a JFrame and Add square elements to an ArrayList. But i can't figure it out. I think the solution might be simple and technical, i just need a slight push.
import java.util.ArrayList;
import javax.swing.JFrame;
public class Frame {
public int width, height;
public String title;
public JFrame jframe;
ArrayList<Square> squares = new ArrayList<Square>();
public Frame(String title, int width, int height) {
this.width = width;
this.height = height;
this.title = title;
display();
}
public void display() {
jframe = new JFrame();
jframe.setTitle(title);
jframe.setSize(width, height);
jframe.setResizable(false);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setVisible(true);
jframe.setLocationRelativeTo(null);
for(int i = 0; i < 20; i++) {
squares.add(new Square());
}
jframe.add(squares);
}
}
Get rid of the Square class and restart.
Do not have it extend Canvas -- you do not want to unnecessarily mix AWT and Swing
And your Square class should be a logical class, not a GUI class, meaning it shouldn't extend any GUI component.
Do not randomize within the painting method.
Give your Square class x and y int fields
Give it a public void draw(Graphics g) method where it draws itself using the Graphics object passed in.
Create another class, one that extends JPanel, and give it an ArrayList<Square>.
Override this JPanel's paintComponent method.
In the override be sure to call the super's method.
In the override, iterate through the array list, calling each Square's draw method.
Place this JPanel into your JFrame.
From what I see you are doing you are adding multiple canvases to the frame which will overlap each other.
Rather create a canvas with a list of Squares(x and y ints) that it draws in its paint and then add one canvas to the frame.
Also I am not sure if JFrame.add() will add the whole canvas and resize it. Rather use a layoutManager to add your components like FlowLayout.
Layout managers
Related
I have a main class and a subclass Circle that has a paintComponent method within it. I am trying to call that method to my main class to draw the Circle but nothing will appear and i'm not sure why?
My Circle class:
public class Circle extends Shape {
Integer rad;
public Circle(int posx,int posy, int rad) {
this.posx = posx;
this.posy = posy;
this.rad = rad;
}
class drawCircle extends JPanel {
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.green);
g.fillOval(posx,posy,rad,rad);
}
}
}
My main method snippets
public class drawFrame extends JFrame {
JPanel panel1;
JPanel panel2;
Square square1;
Circle circle1;
public drawFrame() {
panel2= new JPanel();
panel1= new JPanel();
int rad = 0;
circle1 = new Circle(posx, posy,rad);
Circle.drawCircle drawCi = circle1.new drawCircle();
add(panel1, BorderLayout.CENTER);
panel1.add(drawCi);
So essentially I've just given some snippets of the main part of the code. What I tried doing was creating a new object from the Circle drawCircle inner class,adding it to the mainPanel, so that my Jframe outputs my mainPanel contents which should be the new Circle object I've created? But It seems this doesn't work.
Your code is confusing, and I recommend simplifying.
Problems:
You're using an inner class, drawCircle unnecessarily. Don't nest classes when not needed, but instead give the drawing component its own stand-alone class.
You're creating a drawing component, but never adding it to the GUI. Without adding it to a top-level window, here the JFrame, it will never display. Edit: You are in fact adding it, but its preferred size is 0,0
You're overriding paint not paintComponent. This carries both problems (jerky animations when you later try to do this) and risk (paint also affects a component's child component and border drawing).
Your drawing component has no preferred size set, so it will size to [0,0], and this is not conducive to showing its drawing.
Instead (again) create a stand-alone, non-nested class that extends JPanel, override its paintComponent where you draw the circle and be sure to add it to the displayed JFrame. Also either set the drawing component's preferred size or override its getPreferredSize() method. Of course be sure to pack() the JFrame after adding components and then calling setVisible(true) on it after packing it.
A minor issue: you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
Minor quibble 2: You're rad field (presumably named for radius) should be called "diameter" since it's being used as a diameter, not a radius.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawCircle extends JPanel {
private static final int PANEL_WIDTH = 600;
private static final Color CIRCLE_COLOR = Color.GREEN;
private int posx;
private int posy;
private int diameter;
public DrawCircle(int posx, int posy, int diamter) {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_WIDTH));
this.posx = posx;
this.posy = posy;
this.diameter = diamter;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
g2.fillOval(posx, posy, diameter, diameter);
}
private static void createAndShowGui() {
DrawCircle mainPanel = new DrawCircle(100, 100, 400);
JFrame frame = new JFrame("DrawCircle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I have a class called CharMove,in it are the paint(Graphics g) method, and some custom methods. The class should create a square,then move that square randomly around the screen. However,when I create two instances of this class in my World Class,only one square appears. First the square doesn't move but the new coord.'s are displayed,then after 5 runs the square begins to move randomly. I think the program is getting caught on the Graphics method because only one square is being created,when the CharMove class should be creating another instance of Graphics.I have searched online but can't find a way to create different instances of Graphics.Thanks in advance.
CharMove Class
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import java.util.Random;
public class CharMove extends JPanel {
int x = 250;
int y = 250;
public void paint(Graphics g) {
Graphics pane = (Graphics2D) g;
pane.setColor(Color.blue);
pane.fillRect(x, y, 10, 10);
}
public void movement(JFrame frame) {
for (int i=0;i<5;i++) {
try {
TimeUnit.SECONDS.sleep(1);
this.x = Getx(this.x,frame);
this.y = Gety(this.y,frame);
frame.repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int Getx(int a, JFrame frame) {
Random rn = new Random();
int xnum = rn.nextInt(10)-5;
a += xnum;
System.out.println("x:" + a);
return a;
}
public int Gety(int b, JFrame frame){
Random rn = new Random();
int ynum = rn.nextInt(10)-5;
b += ynum;
System.out.println("y:" + b);
return b;
}
}
World Class
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import java.util.Random;
public class World {
public static void main(String[] args) {
JFrame game = new JFrame();
game.setTitle("Matrix");
game.setSize(500, 500);;
game.getContentPane().setBackground(Color.white);
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.setVisible(true);
CharMove char1 = new CharMove();
CharMove char2 = new CharMove();
game.add(char1);
game.add(char2);
char1.movement(game);
char2.movement(game);
}
}
In swing, all of your painting should be down in paintComponent(Graphics g) (rename your method)
To do animation, you should use a Swing Timer (w/ an ActionListener) to update the positions of your animated items. Once that's done, the timer should call repaint();
public void actionPerformed(ActionEvent ae) {
this.x = Getx(this.x,frame);
this.y = Gety(this.y,frame);
frame.repaint();
}
However,when I create two instances of this class in my World Class,only one square appears.
The default layout manager for a JFrame is a BorderLayout.
game.add(char1);
game.add(char2);
When you add components without specifying a constraint then both components are added to the CENTER. However, only one component can be added to the CENTER so only the last one added is displayed.
Try:
game.add(char1, BorderLayout.PAGE_START);
game.add(char2, BorderLayout.PAGE_END);
However when you do this the componens won't be displayed because they have a (0, 0) preferredSize. So you will also need to override the getPreferredSize() method of your CharMove class.
#Override
public Dimension getPreferredSize()
{
return new Dimension(300, 200);
}
Also, custom painting should be done in the paintComponent(...) method and you need to invoke super.paintComponent(...) at the start to clear the background.
The repaint() method in your movement() method should be on the panel, not the frame, since you are changing properties of the panel.
Each CharMove is essentially a JPanel which draws a single square of size 10 somewhere on itself when it is painted. You are adding two CharMove panels to the game JFrame (which in fact adds them to the default content pane, which has a subclassed BorderLayout). As you are not providing a layout constraints object, in fact both panels are being added to the BorderLayout.CENTER of the content pane, and the second is completely covering the first.
To correct this you should modify CharMove so that it paints all of the squares (eg by maintaining an array or some sort of collection of squares, and painting all of them in the paint method) and just add that one panel to the JFrame.
Apart from this issue, while you are animating the squares in the movement method you are blocking the Event Dispatch Thread, meaning that during the animation you won't be able to move any other windows or respond to any mouse clicks or other inputs. Take ControlAltDel's advice about using the Swing Timer for animation to correct this problem.
I'm trying to write some custom painting code. Specifically, I want to have a bunch of extended JPanels which paint different aspects of my GUI, but each of these extended panels holds the instructions for how it is to be painted.
I've created the code, but for some reason, the extended JPanel isn't being painted on the main JPanel in my JFrame regardless of what I do. Here is a gist of my main class and one of my extended JPanels. What am I missing?
Breakout
//Java imports
import javax.swing.JFrame;
import java.awt.Dimension;
import javax.swing.JPanel;
//Personal imports
import Ball;
public class Breakout {
public static void main (String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {//start the GUI in a new thread
public void run(){
showGUI();
}
});
}
private static void showGUI() {
JFrame frame = new JFrame("Breakout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension d = new Dimension(640,480);
frame.setMinimumSize(d);
frame.setResizable(false);
JPanel p = new JPanel();
p.add(new Ball(200,200,50,255,0,0));
frame.add(p);
frame.setVisible(true);
}
}
Ball
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
public class Ball extends JPanel {
public int x;
public int y;
public int radius;
public Color colour;
public Ball(int x, int y, int radius, int r, int g, int b) {
super();
this.x = x;
this.y = y;
this.radius = radius;
colour = new Color(r,g,b);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
//define constants
int topLeftX = x+radius;
int topLeftY = y+radius;
int diameter = radius *2;
//draw outline
g.setColor(Color.BLACK);
g.drawOval(topLeftX, topLeftY, diameter, diameter);
//fill it in
g.setColor(colour);
g.fillOval(topLeftX, topLeftY, diameter, diameter);
}
}
Using JPanels in this way is going to cause you no end of problems.
The two main problems you have are...
JPanels already have an idea of size and position, adding another x/y coordinate is just confusing and could lead you to painting off the components viewable space
The default preferred size of a JPanel is 0x0. This means when you add it another JPanel, using FlowLayout, the panel is given a size of 0x0, so nothing gets painted.
Instead, create an interface which has a method called paint and takes a Graphics2D object.
For each shape you want to paint, create a new class which implements this interface and use it's paint method to paint the object as you see fit.
Create a custom component, extending from JPanel and maintain a List of these shapes. In it's paintComponent, use a for-loop to paint each shape in the List.
This custom component should then be added to your frame...
In your showGUI method in your main class you have this code:
JPanel p = new JPanel();
p.add(new Ball(200,200,50,255,0,0));
frame.add(p);
This code creates a new JPanel then adds another JPanel to that. This is incorrect because it simply does not make sense to add another JPanel to a perfectly good JPanel that you just created. Instead just do this:
frame.getContentPane().add(new Ball(200, 200, 50, 255,0,0));
Or if you prefer:
Ball ball = new Ball(200, 200, 50, 255,0,0);
frame.getContentPane().add(ball);
I'm trying to make a Weather applet for school but I'm having a problem calling a class to a main class. In the end I will want 3 permanent components (Location, Temperature, Precipitation) then in image box I want to do an if statement that pics the appropriate image from the data in components.
Layout idea
Main Class code
// The "Weather" class.
import java.applet.*;
import javax.swing.*;
import java.awt.*;
public class Weather extends Applet
{
//TempBar Intergers
//int x=
//int y=
//int H= //H = Heat
//PercipBar Intergers
//int x2=
//int y2=
//int P = //P = pericipitation
public void init ()
{
GridLayout umm = new GridLayout(0,2);
PercipBar percip = new PercipBar();
getContentPane.addItem (percip());
}
public void paint (Graphics g)
{
}
}
PercipBar Code
import java.awt.*;
import java.applet.*;
public class PercipBar extends Applet
{
int x2 =2;
int y2 =2;
int P =80;//P = percipitation will be declared in main file
public void paint (Graphics g)
{
g.setColor (Color.black);
g.drawRect (x2, y2, 100, 20);//outline of bar
g.setColor (Color.blue);
g.fillRect (x2+1, y2+4, P, 14 ); //indicator bar (+4 puts space beetween outline bar)
}
}
That GUI seems well suited to being contained in a BorderLayout.
location gui would be put in position BorderLayout.PAGE_START
image would be put in BorderLayout.CENTER
temp would be put in BorderLayout.LINE_END
percip (which is spelled precip) would be put in position BorderLayout.PAGE_END
Note that the precipitation bar should net be another applet! It should just be a Component.
In order to be assigned space in the BorderLayout, a custom painted component will need to return a sensible preferred size.
Preferably, rather than custom paint such things, just use a Label to display the text. That way, we don't need to override either the paint method or getPreferredSize. The GUI will takes its hints from the natural size of the label.
I'm new to java. I do have a java class but I want to get ahead. I really like it! This is my problem. I'm trying to draw the two paddles needed for the game. I did create 2 objects for them, and they both "Show" but the following happens:
Main Runner
import javax.swing.JFrame;
public class PongRunner {
public static void main (String[] args)
{
new PongRunner();
}
public PongRunner()
{
JFrame PongFrame= new JFrame();
PongFrame.setSize(800,600);
PongFrame.add(new PaddleB());
PongFrame.add(new PaddleA());
PongFrame.setLocationRelativeTo(null);
PongFrame.setTitle("My Pong Game");
PongFrame.setResizable(false);
PongFrame.setVisible(true);
PongFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
PaddleA and PaddleB are both drawRect Graphics and draw a specific rectangle.
The graphics that I tell JFrame to add first are the only ones visible. The one under it doesnt get added into the Frame I wanted to know why,and how i can draw both paddles in the same JFrame... I'm reading my book and looking over the internet as much as possible, but no luck within these 2 days. Some help would be nice. I just started learning java and I think I'm making progress. Some tips would be nice too thanks :), specialy on actionListener since im going to have to use them to move the paddles!
SOURCE CODE OF BOTH PADDLES:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class PaddleA extends JPanel implements ActionListener
{
private Timer timer;
public void timer()
{
timer = new Timer(25, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(70,200,20,100);
}
}
PaddleB:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class PaddleB extends JPanel implements ActionListener
{
private Timer timer;
public void timer()
{
timer = new Timer(25, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(500, 200, 20, 100);
}
}
As you've added the 2 paddles into the same BorderLayout.CENTER location of the main PongFrame, only the second paddle will be displayed.
This type of painting is more easily done using one Graphics object on one JComponent.
Swing components aren't really designed to be used like this. Each of your paddle JPanels is trying to paint a rectangle, but the container (the JFrame) and its layout manager will control the size and location of the JPanels. So the rectangle they paint may be outside their bounds, and won't appear at all.
Some Layout Managers will only show one of the JPanels if you add them one after another like this - see answer by Reimus.
To do animated graphics like this, you probably need to start with a single JComponent (e.g. a JPanel) that fills the JFrame, and paint both paddles as rectangles onto it.
This is because you add the first paddle and then add the second paddle over the first thus replacing them:
PongFrame.add(new PaddleB());
PongFrame.add(new PaddleA());
try using LayoutManagers here is just a quick sample:
PongFrame.getContentPane().add(new PaddleB(),BorderLayout.WEST);
PongFrame.getContentPane().add(new PaddleA(),BorderLayout.EAST);
also your g.fillRect(...); has x and y co-ordinates that are too large for the JFrame and thus are not displayed. Change it like so:
g.fillRect(0,0, 20, 100);
Not related though:
dont use JFrame.add() rather JFrame.getContentPane().add()
UPADTE:
As others have mentioned the logic for the game is a bit off. You should have a single drawing panel which is added to the JFrame and all drawing is done on the single JPanel using Graphics and not JPanels. Here is a small example:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PongRunner {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new PongRunner();
}
});
}
public PongRunner() {
JFrame PongFrame = new JFrame();
PongFrame.setTitle("My Pong Game");
PongFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PongFrame.setResizable(false);
PongFrame.setSize(400,400);
//PongFrame.setLocationRelativeTo(null);
PongFrame.getContentPane().add(new DrawingPanel());
PongFrame.setVisible(true);
}
}
class DrawingPanel extends JPanel {
Rect rect1, rect2;
public DrawingPanel() {
super(true);
rect1 = new Rect(80, 80, 20, 100, Color.yellow);
rect2 = new Rect(100, 200, 20, 100, Color.red);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(rect1.getColor());
g.fillRect(rect1.getX(), rect1.getY(), rect1.getWidth(), rect1.getHeight());
g.setColor(rect2.getColor());
g.fillRect(rect2.getX(), rect2.getY(), rect2.getWidth(), rect2.getHeight());
}
}
class Rect {
private Color color;
private int x, y, height, width;
Rect(int x, int y, int width, int height, Color color) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
this.color = color;
}
public Color getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
}
Please note this snippet is not perfect and merely demonstrates the stereotypical logic needed for drawing rectangles to form a game of sorts
I don't have time to try running your code, but try setting the size and position of the "paddle" objects, or set them as non-opaque. I believe that Swing tries to speed up rendering by not drawing components which are completely covered by another opaque component. It may not be drawing one of your paddles for that reason.
If you do set the size and position of your paddles, you may have to modify your paintComponent methods: if my memory is correct, I think the Graphics object which is passed in is clipped to the area of the component, so 0,0 would be the top-left corner of the component.
you shouldn't add your JPanels to your JFrame by the JFrame#add(Component) method. Better add them to the contentPane: frame.getContentPane().add(panel); You should although read about LayoutManagers. They can help you by positining Components in your Frame or mess things up if you use them incorrect.