Drawing Multiple Jcomponents to a Frame

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.

Sample Image

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:

Sample Image

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 add panel.revalidate() before jpanel.repaint();

  • (final look) everything depends of used LayoutManager for JPanel

  • 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:

  1. to display components at random locations you will need to use a null layout on the content pane.

  2. 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.

  3. you will need to set the size of each location, otherwise the default will be (0, 0)

  4. 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 Listeners where possible/allowed

  • Make sure JFrame#setVisible(..) is the last call on JFrame instance specifically pointing here:

        this.setVisible(true);
    this.setSize(500, 500);
    this.setLocationRelativeTo(null);
    HelloHere(index.toString());
  • Call pack() on JFrame instance rather than setSize(..)

  • Do not use multiple JFrames 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 last JPanel added with the new one (this is default the behavior of BorderLayout). To solve this read below 2 points:

  • Call revalidate() and repaint() 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());

Sample Image

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



Leave a reply



Submit