Drawing Multiple JComponents to a Frame
What you want to do is use a data structure of Car
objects and loop through them in the paintComonent
method. Something like
List<Car> cars = new ArrayList<>();
....
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Car car : cars) {
car.drawCar(g);
}
}
The drawCar
method would come from your Car
class
public class Car {
int x, y;
public Car(int x, int y) {
this.x = x;
this.y = y;
}
public void drawCar(Graphics g) {
g.setColor(Color.BLACK);
// do everything here as you would in a paintComponent method
}
}
See more examples here and here and here and here and here and here.
UPDATE
Here is a simple example use some "Ferraris" I whipped up, also using some animation, but with the same basic points I have above.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class DrawCar extends JPanel{
private static final int D_W = 400;
private static final int D_H = 400;
List<Car> cars;
public DrawCar() {
cars = new ArrayList<>();
cars.add(new Car(100, 300));
cars.add(new Car(200, 100));
Timer timer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e) {
for (Car car : cars) {
car.move();
repaint();
}
}
});
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Car car : cars) {
car.drawCar(g);
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Car {
private static final int INCREMENT = 5;
int x, y;
public Car(int x, int y) {
this.x = x;
this.y = y;
}
public void drawCar(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(x, y, 100, 30);
g.setColor(Color.BLACK); // body
g.fillOval(x + 15, y + 20, 15, 15); // wheel
g.fillOval(x + 60, y + 20, 15, 15); // wheel
g.fillRect(x + 15, y - 20, 60, 20); // top
}
public void move() {
if (x == D_W) {
x = 0;
} else {
x += INCREMENT;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new DrawCar());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Drawing multiple graphic2d components into JPanel
Your MyLine class should not be a Swing component and thus should not extend JComponent. Rather it should be a logical entity, and in fact can be something that implements Shape such as a Line2D, or could be your own complete class, but should know how to draw itself, i.e., if it does not implement Shape, then it should have some type of draw(Graphics2D g)
method that other classes can call. I think instead you should work on extending your panel's JPanel class, such that you override its paintComponent
method, give it a collection to hold any MyLine items added to it, and draw the MyLine items within the paintComponent.
Other options include drawing directly on to a BufferedImage, and then displaying that BufferedImage in your JPanel's paintComponent method. This is great for static images, but not good for images that need to change or move.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class DrawChit extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private List<Shape> shapes = new ArrayList<>();
public DrawChit() {
setBackground(Color.white);
}
public void addShape(Shape shape) {
shapes.add(shape);
repaint();
}
@Override // make it bigger
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Shape shape : shapes) {
g2.draw(shape);
}
}
private static void createAndShowGui() {
DrawChit drawChit = new DrawChit();
drawChit.addShape(new Line2D.Double(10, 10, 100, 100));
drawChit.addShape(new Ellipse2D.Double(120, 120, 200, 200));
JFrame frame = new JFrame("DrawChit");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(drawChit);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Or an example using my own MyDrawable class, which produces a GUI that looks like this:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class DrawChit extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private List<MyDrawable> drawables = new ArrayList<>();
public DrawChit() {
setBackground(Color.white);
}
public void addMyDrawable(MyDrawable myDrawable) {
drawables.add(myDrawable);
repaint();
}
@Override
// make it bigger
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (MyDrawable myDrawable : drawables) {
myDrawable.draw(g2);
}
}
public void clearAll() {
drawables.clear();
repaint();
}
private static void createAndShowGui() {
final List<MyDrawable> myDrawables = new ArrayList<>();
myDrawables.add(new MyDrawable(new Line2D.Double(100, 40, 400, 400),
Color.red, new BasicStroke(40)));
myDrawables.add(new MyDrawable(new Ellipse2D.Double(50, 10, 400, 400),
Color.blue, new BasicStroke(18)));
myDrawables.add(new MyDrawable(new Rectangle2D.Double(40, 200, 300, 300),
Color.cyan, new BasicStroke(25)));
myDrawables.add(new MyDrawable(new RoundRectangle2D.Double(75, 75, 490, 450, 40, 40),
Color.green, new BasicStroke(12)));
final DrawChit drawChit = new DrawChit();
JFrame frame = new JFrame("DrawChit");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(drawChit);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
private int drawCount = 0;
@Override
public void actionPerformed(ActionEvent e) {
if (drawCount >= myDrawables.size()) {
drawCount = 0;
drawChit.clearAll();
} else {
drawChit.addMyDrawable(myDrawables.get(drawCount));
drawCount++;
}
}
}).start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MyDrawable {
private Shape shape;
private Color color;
private Stroke stroke;
public MyDrawable(Shape shape, Color color, Stroke stroke) {
this.shape = shape;
this.color = color;
this.stroke = stroke;
}
public Shape getShape() {
return shape;
}
public Color getColor() {
return color;
}
public Stroke getStroke() {
return stroke;
}
public void draw(Graphics2D g2) {
Color oldColor = g2.getColor();
Stroke oldStroke = g2.getStroke();
g2.setColor(color);
g2.setStroke(stroke);
g2.draw(shape);
g2.setColor(oldColor);
g2.setStroke(oldStroke);
}
public void fill(Graphics2D g2) {
Color oldColor = g2.getColor();
Stroke oldStroke = g2.getStroke();
g2.setColor(color);
g2.setStroke(stroke);
g2.fill(shape);
g2.setColor(oldColor);
g2.setStroke(oldStroke);
}
}
How to add multiple components to a JFrame
Your overall program design looks to be in error and needs to be re-done, and specifically I'm talking about having your drawable entity classes, Base, Enemy, and Player, each extend JComponent, since that allows these components to be drawn within their JComponent and their JComponent only and nowhere else. Instead:
- Make your drawable entity classes, such as Player, Base, and Enemy implement a common interface, say Drawable, that has a painting method, say
draw(Graphics g)
. - Do not have them extend JComponent, JPanel or any other Swing component.
- Instead create one single class for graphics, say call it DrawingPanel, and have it extend JPanel or JComponent.
- Within the single paintComponent method of this DrawingPanel, draw each entity, if it is located within the section of the game that requires drawing.
- Strive to separate your view code, the GUI code, from your program model code, the code that governs your logical entity behaviors and locations, preferably having them in separate classes, and probably even separate packages.
Regarding questions:
I am currently trying to make Drawable interface, what should I put in the method?
The interface would not have any code within the method. Any classes that implement the interface would need to have code that allows the objects of that class to draw themselves.
If I have the DrawingPanel class, would I even need the Drawable interface?
Yes because DrawingPanel would hold a collection of your Drawable objects and would draw them within its paintComponent
method, as already mentioned above.
Cant draw two items to Jframe
The reason of this, is that a JFrame
uses a BorderLayout by default. When you frame.add(component)
without any constraints, the component will be added to BorderLayout.CENTER
position. So, no matter how many components you will add without constraints, borderlayout will override the older since all of them are being added to CENTER
.
The solution would be either to choose where you want your components to be added:
frame.add(home,BorderLayout.CENTER);
frame.add(sun,BorderLayout.LINE_START);
either to change the layout of your container (the JFrame in your case):
frame.setLayout(new FlowLayout());
frame.add(home);
frame.add(sun);
Worth to read: A Visual Guide to Layout Managers
Finally, do not @Override
paint()
method. @Override
paintComponent()
method instead.
Drawing Multiple Items to a JPanel
remove all code for
frame.repaint();
and to addpanel.revalidate()
beforejpanel.repaint();
(final look) everything depends of used
LayoutManager
forJPanel
for better help sooner post and SSCCE
this post will help you
drawing multiple rectangles with swing
The default layout manager for the content pane of a JFrame
is the BorderLayout
.
By default each component is added to the CENTER
of the BorderLayout
. Only the last component added will be given a size/location. So only the last component will be visible.
The solution is:
to display components at random locations you will need to use a null layout on the content pane.
you will need to set the size of each component equal to the preferred size, otherwise the default size will still be (0, 0) and there is nothing to paint.
you will need to set the size of each location, otherwise the default will be (0, 0)
the "random" color of the Vehicle should be assigned in the constructor, not the painting method. A painting method should only paint the current state, not change the state of the component.
Note:
This answer only addresses the issues of using a JPanel to represent your Vehicle. You need to understand how a layout manager works to give components a size/location. In this case because you are randomly positioning components on the panel you would need to use a null layout and set the size/location yourself.
This is not the preferred approach. The better approach is to do custom painting yourself of all the Vehicles as demonstrated by Gilbert.
Java Swing - More efficient to draw in multiple panels or a single panel
It is slightly more efficient.
However, consider using several panels and rely on the layout managers to handle sizing etc. It will most likely simplify your code. If you are using one panel only you will have to handle resizing yourself.
In programming, don't optimise before you know performance is a problem. Measure!
Java Drawing Multiple Squares in Same JFrame
So, starting with...
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
@Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
@Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
As general recommendation, it's preferred to put custom painting in the paintComponent
method (note, there's no s
at the end)
When paint
is called, the Graphics
context has already been translated to the component coordinate position. This means that 0x0
is the top/left corner of the component, this also means that...
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
is painting the rect at x + x
x y + y
, which will, at the very least, paint the rect in the wrong position, at worst paint it beyond the visible scope of the component.
You're also not providing any sizing hints for the component, so it's default size will be 0x0
, which prevent it from been painted.
Since I am using multi thread I assume I have to control each squares.
Well, since I can't really see what's driving the animation, I imagine that when you say "multi thread" you're suggesting that each square has it's own `Thread. In this case, that's a bad idea. Let's put aside the thread synchronisation issues for a moment, more threads doesn't equate to more work you can do, at some point, it will begin to degrade the system performance.
In most cases, a single, well managed thread, is all you really need. You also have to understand that Swing is NOT thread safe. This means that you shouldn't update the UI (or states that the UI relies on) from outside the context of the Event Dispatching Thread.
So, why you're thread can update the position of the rects, you need to take care to ensure that they are not been painted why they are been update. Once you've updated the state, you then need to trigger a paint pass (which is trivial in of itself)
So I have to store each square object in the ArrayList.
Yep, good start
However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show.
Okay, so instead of using multiple components, use one. Run through your ArrayList
within the paintComponent
method of this component and paint all the rects to it. This provides a much simpler way to manage things like bounds detection, as you have only one container to worry about.
I'd highly recommend you have a look at:
- Java Bouncing Ball which demonstrates many of the concepts discussed here
- Concurrency in Swing
- How to use Swing Timers
- Performing Custom Painting
- Painting in AWT and Swing
Drawing multiple shapes depending on user Java Swing
Some pointers:
Use EDT for creation and manipulation of Swing Components.
Dont extend
JFrame
class unnecessarily.Use anonymous
Listener
s where possible/allowedMake sure
JFrame#setVisible(..)
is the last call onJFrame
instance specifically pointing here:this.setVisible(true);
this.setSize(500, 500);
this.setLocationRelativeTo(null);
HelloHere(index.toString());Call
pack()
onJFrame
instance rather thansetSize(..)
Do not use multiple
JFrame
s see: The Use of Multiple JFrames: Good or Bad Practice?The problem you are having is here:
Draw d = new Draw(p);
this.add(d);You are creating a new instance of
Draw
JPanel
each time and then overwriting the lastJPanel
added with the new one (this is default the behavior ofBorderLayout
). To solve this read below 2 points:Call
revalidate()
andrepaint()
on instance after adding components to show newly added components.Use an appropriate
LayoutManager
As I am not sure of you expected results I have done as much fixing of the code as I could hope it helps:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;
public class FileChoose {
JFrame frame;
public FileChoose() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMenuBar l = new JMenuBar();
JMenu file = new JMenu("File");
JMenuItem open = new JMenuItem("Addvertex");
open.addActionListener(new ActionListener() {
Integer index;
@Override
public void actionPerformed(ActionEvent e) {
JPanel pn = new JPanel();
JTextField jt = new JTextField(5);
JTextField jt1 = new JTextField(5);
pn.add(jt);
pn.add(jt1);
int result = JOptionPane.showConfirmDialog(null, pn, "Enter the values", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
index = Integer.parseInt(jt.getText());
System.out.println(jt1.getText());
}
HelloHere(index.toString());
}
});
JMenuItem close = new JMenuItem("Exit");
close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
JMenu tools = new JMenu("Tools");
file.add(open);
file.add(close);
frame.setJMenuBar(l);
l.add(tools);
l.add(file);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void HelloHere(String p) {
Draw d = new Draw(p);
frame.add(d);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.revalidate();
frame.repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
FileChoose f = new FileChoose();
}
});
}
}
class Draw extends JPanel {
String inp;
public Draw(String gn) {
inp = gn;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Random r = new Random();
int x = r.nextInt(100);
g2.drawOval(x, x * 2, 100, 100);
g2.drawString(inp, x + (x / 2), x + (x / 2));
}
}
UPDATE:
Declare this globally in your Draw class: Random r=new Random();
as if you iniate a new Random instance each time you call paintComponent()
the distributions will not be significant/random enough
Adding Multiple Components In Seperate Classes to JFrame In Another Class
Each component should be responsible for managing it's own size, you should start by overriding getPreferredSize
of the panels and returning the size you would like to use.
You should also not rely on magic numbers, but instead should use actual physical values, for example, instead of
g.fillRect(0, 0, 600, 120);
You should use...
g.fillRect(0, 0, getWidth(), getHeight());
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test1 {
public static void main(String[] args) {
new Test1();
}
public Test1() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
add(new DrawBoard());
add(new QuestionBox(), BorderLayout.SOUTH);
}
}
public static class DrawBoard extends JPanel {
public static Color yellow = new Color(13816442);
@Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(yellow);
g.fillRect(0, 0, 600, 600);
}
}
public static class QuestionBox extends JPanel {
@Override
public Dimension getPreferredSize() {
return new Dimension(600, 120);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 600, 120);
}
}
}
You should also know that Toolkit.getDefaultToolkit().getScreenSize();
is not the most reliable method for determining the visible screen area, as it does not take into account various OS elements, like the task bar or dock, which can take up screen space.
Related Topics
Masking Password Input from the Console:Java
How to Get the X and Y of a Program Window in Java
How Does Java "Week Year" Really Work
Java Type Inference: Reference Is Ambiguous in Java 8, But Not Java 7
When Would You Call Java's Thread.Run() Instead of Thread.Start()
Log4J Redirect Stdout to Dailyrollingfileappender
Spring Boot - Inject Map from Application.Yml
Parsing a Date's Ordinal Indicator ( St, Nd, Rd, Th ) in a Date-Time String
Windowbuilder Design Tab Incomplete in Eclipse
Get Edited Treenode from a Celleditorlistener
Break When Exception Is Thrown
Inetaddress.Getlocalhost() Throws Unknownhostexception
In Java, What Are the Advantages of Streams Over Loops
Differencebetween a Javabean and a Pojo
How to Create a Linked List Data Structure in Java