I am presently planning to write some code for collision detection. However, I came across a problem. I want to draw multiple spheres on the JFrame window but the following code isn't working... Kindly help me out...
This is my code :-
import javax.swing.*;
import java.awt.*;
class Draw extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i=0;i<20;i++)
drawing(g,new Sphere());
}
public void drawing(Graphics g,Sphere s)
{
g.setColor(s.color);
g.fillOval(s.x,s.y,s.radius*2,s.radius*2);
}
public static void main(String args[])
{
JFrame jf = new JFrame("Renderer");
jf.getContentPane().add(new Draw(),BorderLayout.CENTER);
jf.setBounds(100,100,400,300);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
}
class Sphere
{
int x;
int y;
int radius;
Color color;
public Sphere()
{
this.x = (int)Math.random()*100;
this.y = (int)Math.random()*100;
this.radius = (int)Math.random()*20;
this.color = new Color((int)(Math.random()*255),
(int)(Math.random()*255),(int)(Math.random()*255));
}
}
You cast the random value to int so that it is 0 and then multiply it.
Your Sphere Constructor should look like
public Sphere() {
this.x = (int) (Math.random() * 100); // cast the result to int not the random
this.y = (int) (Math.random() * 100);
this.radius = (int) (Math.random() * 20);
this.color = new Color((int) ((Math.random() * 255)), (int) (Math.random() * 255), (int) (Math.random() * 255));
}
for(int i=0;i<20;i++)
drawing(g,new Sphere());
A painting method is for painting only.
You should NOT be creating Sphere objects in the paintComponent() method. You can't control when Swing repaints the panel.
Instead, in the constructor of your Draw class you need to create an ArrayList of Sphere objects and you add the 20 objects to the list.
Then you need to add a paint(...) method to your Sphere class so the Sphere object knows how to paint itself. Something like:
public void paint(Graphics g)
{
g.setColor( color );
g.fillOval(x, y, width, height) //
}
Then in the paintComponent(...) method your Draw class you need to iterate through the ArrayList and paint each Sphere:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (each sphere in the ArrayList)
sphere.paint(g);
}
Related
I have an array of 100 random ints. I want to create a bar graphic from them. I know how to create a single rectangle in a frame but without passing values.
This is the draw class:
import javax.swing.*;
import java.awt.*;
public class draw extends JPanel
{
public void drawing() {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(100, 150, 12, 3);
}
}
I want to replace the values in g.fillRect(100,150,12,3) with my random int values. But since i call repaint() from main to call paintComponent, i cant pass values to paintComponent. How do i do it? if its impossible, what alternatives do i have?
You can do it by introducing fields and intitialize then using a constructors, setters, or both :
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Draw extends JPanel {
int x, y, width, height;
public Draw(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
void setX(int x) {this.x = x;}
void setY(int y) {this.y = y;}
void setWidth(int width) {this.width = width;}
void setHeight(int height) {this.height = height;}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(x, y, width, height);
}
}
How can I pass values to paintComponent
You can't pass values to paintComponent().
But since i call repaint() from main to call paintComponent, i cant pass values to paintComponent. How do i do it?
For whatever object you want to customize and draw by yourself, you can create a class for that and have a method like draw() to do the painting, for example:
//Just a normal class with a draw() method
class BarGraph{
private int x;
private int y;
private int width;
private int height;
private Color color;
public BarGraph(int x, int y, int width, int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void setColor(Color color){
this.color = color;
}
public void draw(Graphics g){
g.setColor(color);
g.fillRect(x, y, width, height);
}
}
Then in your main panel for displaying your customized image:
class DrawingSpace extends JPanel{
private BarGraph barGraph;
public DrawingSpace(){
barGraph = new BarGraph(50, 50, 400, 100);
barGraph.setColor = (Color.RED);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g){
barGraph.draw(g); //draw your own customized object
}
}
}
So instead of trying to pass values to dictate how paintComponent draws, you can pass/set your values in your own class (in this case, BarGraph class). From the Graphics content, implement how you want it to be painted.
In your paintComponent, simply call the draw method which already has "a set of instructions" for how it should be drawn.
We cannot pass values to paintComponent method as parameters because, we are just overriding an existing method which is called by Swing. But we can have an instance variable (values in my example) in the class which is accessible inside paintComponent method (as suggested in other answers/comments).
I'm putting an example program below since, sometimes examples convey ideas better.
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
public class BarGraph extends JPanel
{
private static final int GRAPH_HEIGHT = 300;
private static final int BAR_WIDTH = 50;
private static final int GAP_BETWEEN_BARS = 20;
private static final int GRAPH_X_OFFSET = 50;
private int[] values;
public BarGraph(int[] values)
{
this.values = values;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
for (int i = 0; i < values.length; i++)
{
g.fillRect(
GRAPH_X_OFFSET + (i * (GAP_BETWEEN_BARS + BAR_WIDTH)),
GRAPH_HEIGHT - values[i],
BAR_WIDTH,
values[i]
);
}
}
public static void main(String[] args)
{
int[] graphValues = {100, 150, 50, 250, 200, 75};
JFrame frame = new JFrame("Graph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new BarGraph(graphValues), BorderLayout.CENTER);
frame.setBounds(300, 200, 600, 400);
frame.setVisible(true);
}
}
So I've been stuck on this problem for a while now and I'm desperate for help. Please help me. I've got 3 classes:
Circle is just suppose to draw a circle in the frame created by Frame with random starting position (and defind the radius).
Frame is the mainclass with methods such as addCircle(), bounce(), start(), stop(), run() (moves the circles) and quit(). This class also creates the frame in which the circles are added to.
Interfa is just for now a inteface frame where I define the radius, number of circles and Frame size.
No matter what I try I cannot add more than two circle (one is colored and one is not):
The "recursive way":
private static void addCircle(int n){
Circle[] circles = new Circle[n+10];
if (n > 0){
circles[circleAdd] = new Circle();
frame.add(circles[circleAdd]);
circleAdd = circleAdd + 1;
addCircle(n-1);
}
}
Normal itterative way
private static void addCircles(int n){
ArrayList<Circle> circles = new ArrayList<Circle>();
for(int i = 0; i<=n;i++){
circles.add(new Circle());
frame.add(circles.get(i));
}
}
This is how I create my Frame:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public Class Frame{
private static JFrame frame;
private static int circleAdd = 0;
private static JPanel fra;
public static void mainFrame(){
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
fra = new JPanel();
frame.add(fra);
...
//addCircle and addCircles
...
public static void main..
}
}
This is my circle:
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Circle extends JPanel{
private Random random = new Random();
public void paint(Graphics g){
int randX = random.nextInt(250)+50;
int randY = random.nextInt(250)+50;
g.drawOval(randX,randY,50,50);
g.setColor(Color.ORANGE);
g.fillOval(100,100,50,50);
}
}
I would suggest that your general approach is wrong. Instead of using a JPanel as the element, you should have a JPanel capable of painting any number of "circles". The Graphics2D API is capable of drawing complex shapes (including ovals).
The main issues I can see are:
JFrame by default is using a BorderLayout, this only allows a single component to be placed in each of the five available positions
Layout managers rely on the preferred/minimum/maximumSize hints to make determinations about the size of the components. They are also responsible for deciding on where the component should be placed. In your current implementation, this would mean that it's possible for you to paint beyond the visible range of the component
Overriding paint is not recommend, and failing to call super.paint could cause a number of unexpected and difficult to diagnose issues
Painting can occur at any time, so using random values in the paint method will cause the UI to constantly change
Instead, you could define your own Circle class which takes the location and size you want and simply acts as a container
public class Circle {
private int x;
private int y;
private int radius;
private Ellipse2D shape;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.shape = new Ellipse2D.Double(x, y, radius * 2, radius * 2);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getRadius() {
return radius;
}
public Rectangle getBounds() {
return shape.getBounds();
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.ORANGE);
g2d.fill(shape);
}
}
This is simply a container class, it represents the information need to generate the desired outcome. It has a convince method which is capable of then painting the shape itself.
You would then need to create a List of these shapes and paint them to your component
public class TestPane extends JPanel {
private List<Circle> circles = new ArrayList<>(10);
private Dimension size;
public TestPane() {
Random random = new Random();
int maxX = 0;
int maxY = 0;
for (int index = 0; index < 10; index++) {
int randX = random.nextInt(250) + 50;
int randY = random.nextInt(250) + 50;
circles.add(new Circle(randX, randY, 25));
maxX = Math.max(maxX, randX + 50);
maxY = Math.max(maxY, randY + 50);
}
size = new Dimension(maxX, maxY);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Circle circle : circles) {
Graphics2D g2d = (Graphics2D) g.create();
circle.paint(g2d);
g2d.dispose();
}
}
}
One of the things you seem to lack understanding in is how painting actually works in Swing.
Start by having a look at Performing Custom Painting and Painting in AWT and Swing for more details.
A deeper understanding of how layout managers and the component hierarchy work also wouldn't hurt
I've written an app that custom draws everything inside paint() based on fixed pixel positions. Then I disabled resize of the frame so its always visible.
However, now I would like to be able to resize it but I dont want to change my drawling code. I was hoping I could grab the 300x300 square of the Graphics g object and resize it to the JFrame current size after all of my drawling code, but I have no idea what I'm doing.
Here sample code. In this I want the 100x100 square to remain in the middle, proportionate to the resized JFrame:
package DrawAndScale;
import java.awt.Color;
import java.awt.Graphics;
public class DASFrame extends javax.swing.JFrame {
public DASFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.setSize(300, 300);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new DASFrame().setVisible(true);
}
});
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fill3DRect(100, 100, 100, 100, true);
}
}
Thanks.
Assuming you rename your method that paints for 300x300 as paint300, define a buffered image:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300); // empty image
paint300(bufferImage.getGraphics()); // fill the image
g.drawImage(bufferImage, 0, 0, null); // send the image to graphics device
}
Above is when you want to draw at full size (300x300).
If your window is resized:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300);
paint300(bufferImage.getGraphics());
int width = getWidth();
int height = getHeight();
CropImageFilter crop =
new CropImageFilter((300 - width)/2, (300 - height)/2 , width, height);
FilteredImageSource fis = new FilteredImageSource(bufferImage, crop);
Image croppedImage = createImage(fis);
g.drawImage(croppedImage, 0, 0, null);
}
The new classes are from from java.awt.image.*.
I didn't test this code. It's just to send you in the right direction.
if you want to painting Custom paint then look for JLabel or JPanel and including Icon/ImageIcon inside, simple example about that
import java.awt.*;
import javax.swing.*;
public class MainComponentPaint extends JFrame {
private static final long serialVersionUID = 1L;
public MainComponentPaint() {
setTitle("Customize Preffered Size Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponent());
pack();
setMinimumSize(getSize());
setPreferredSize(getSize());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setVisible(true);
}
});
}
public static void main(String[] args) {
MainComponentPaint main = new MainComponentPaint();
main.display();
}
}
class CustomComponent extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
for (int i = 0; i < Math.max(w, h); i += 20) {
g.drawLine(i, 0, i, h);
g.drawLine(0, i, w, i);
}
}
}
Not an expert, but you could just scale the Graphics2D object (the passed Graphics is in fact a Graphics2D instance), where the x and y ratios are the ratios of the fixed size you chose to draw and the actual size of the frame.
See http://download.oracle.com/javase/6/docs/api/java/awt/Graphics2D.html#scale%28double,%20double%29
You could do this with some math.
public void paint(Graphics g){
int height = 100;
int width = 100;
int x = (this.getWidth() / 2) - (width / 2);
int y = (this.getHeight() / 2) - (height / 2);
g.setColor(Color.BLACK);
g.fill3DRect(x, y, width, height, true);
}
Or if you wanted to keep the width and height of the box with the same proportion, use int width = this.getWidth() / 3; and int height = this.getHeight() / 3.
The other option is to use Graphics2D.scale(), as JB pointed out, the passed Graphics object is actually a Graphics2D object.
I have a problem to create circle whenever I like to call the paint function it will draw me another circle. Here is my code:
import java.awt.*;
import javax.swing.*;
public class MyOnto extends JFrame
{
int weight = 960;
int heigh = 960;
int x = 200;
int y = 100;
//Graphics p;
private static MyOnto my = new MyOnto();
public MyOnto()
{
setTitle("My Ontology");
setSize(weight, heigh);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
g.setColor(Color.black);
drawing(g,x,y,100,50); //g, x ,y, w, h of circle
}
public void drawing(Graphics g, int x, int y, int w, int h)
{
g.drawOval(x,y,w,h);
g.drawString("Helo", x+25,y+20);
x = x + 100;
y = y + 100;
}
public static void main(String[] args)
{
//my = new MyOnto();
my.paint(null);
my.paint(null); //try to print one more circle
}
}
The output is always just one circle. How can I make it like a function call whenever I want to draw extra one circle it will just a simple call a function?
Don't override paint() on a JFrame.
Custom painting is done by override paintComponent(...) on a JPanel and then you add the panel to the frame.
it will draw me another circle.
There are two common approaches:
Keep a List of circles to draw
Draw the circles on a BufferedImage
Check out Custom Painting Approaches for working examples of both approaches.
I'm attempting to teach myself some Java AWT and simple graphics but have had difficulty with using the contains and intersects method.
The problem is that it seems to detect the collision several pixels up from where the mouse is clicked and the actual shape.
GameDemo.java
package uk.co.mhayward.games.sandbox;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameDemo extends JFrame {
GamePanel gamePanel = new GamePanel();
public static void main(String[] args) {
new GameDemo();
}
public GameDemo() {
super("click me");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(gamePanel);
this.setSize(200, 200);
this.setVisible(true);
this.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
System.out.println(e.getPoint().toString());
if (gamePanel.shape.contains(e.getPoint())) {
System.out.println("IN");
} else {
System.out.println("out");
}
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
});
}
public class GamePanel extends JPanel {
Shape shape = new RegularPolygon(100, 100, 100, 6, 0);
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(1));
g2d.setPaint(Color.WHITE);
g2d.fill(shape);
g2d.setPaint(Color.BLACK);
g2d.draw(shape);
}
}
public static class RegularPolygon extends Polygon {
private static final long serialVersionUID = 8828151557263250246L;
/**
* #param x
* #param y
* #param r
* #param vertexCount
*/
public RegularPolygon(int x, int y, int r, int vertexCount) {
this(x, y, r, vertexCount, 0);
}
/**
* #param x
* #param y
* #param r
* #param vertexCount
* #param startAngle
* 360deg = PI
*/
public RegularPolygon(int x, int y, int r, int vertexCount, double startAngle) {
super(getXCoordinates(x, y, r, vertexCount, startAngle),
getYCoordinates(x, y, r, vertexCount, startAngle),
vertexCount);
}
protected static int[] getXCoordinates(int x, int y, int r, int vertexCount, double startAngle) {
int res[] = new int[vertexCount];
double addAngle = 2 * Math.PI / vertexCount;
double angle = startAngle;
for (int i = 0; i < vertexCount; i++) {
res[i] = (int) Math.round(r * Math.cos(angle)) + x;
angle += addAngle;
}
return res;
}
protected static int[] getYCoordinates(int x, int y, int r, int vertexCount, double startAngle) {
int res[] = new int[vertexCount];
double addAngle = 2 * Math.PI / vertexCount;
double angle = startAngle;
for (int i = 0; i < vertexCount; i++) {
res[i] = (int) Math.round(r * Math.sin(angle)) + y;
angle += addAngle;
}
return res;
}
}
}
EDITS
04/Jan/12 - changed Override paint(g) to paintComponent(g) - still doesn't detect collision properly.
05/Jan/12 - created a SSCCE to more easily demonstrate the problem.
For reference, this short example examines a transformed Polygon using the contains() method. The result seems correct to the nearest pixel. You might compare it to your result.
Listen on the panel, rather than the JFrame. The offset you are seeing is from the titlebar.
you have override paintComponent(Graphics g) for Swing JComponents instead of method valid for AWT paint(Graphics g), more in tutorials 2D Graphics and Performing Custom Painting
you have override paintComponent(Graphics g) for Swing JComponents instead of method valid for AWT paint(Graphics g)