Hello i'm trying to learn Graphics in java and at the same time Classes and Objects. My goal now is to make a programm that contains different classes with Rectangles or Circles and then I want to use those Rectangles and Circles in other classes and change their parameters like size, color and position to draw some kind of Pattern.
My problem right now is that I can make a rectangle and I think I can even make a second one, but i can't change parameters of it (Color, size and position) I tried adding variables to this part of code Rect rect = new Rect(int variables); but it didn't work.
Normally I can solve easy problems like this but i really don't understand how classes and objects works in java if someone can give me some help would be great.
Here is my code
public class Main{
public static void main(String[] args ) {
Pattern.drawPattern();
}
}
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Rect extends JPanel{
public static Color myColor = Color.RED;
public static int myX = 10;
public static int myY = 10;
public static int myWidth = 200;
public static int myHeight = 200;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(myColor);
g.fillRect(myX, myY, myWidth, myHeight);
}
}
import java.awt.Color;
import java.awt.Container;
import javax.swing.JFrame;
public class Pattern {
public static void drawPattern() {
JFrame window = new JFrame("test");
window.setSize(1000, 800);
window.setVisible(true);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Rect rect = new Rect();
Rect rect1 = new Rect();
window.add(rect);
window.add(rect1);
Rect.myColor = Color.lightGray;
}
}
So many problems here, but main ones I can see are:
Over-use of static modifier in the Rect class. By using static fields, the Rect instances will not have their own unique state, their own color and position. Make all those fields private non-static (instance). If this causes compilation problems, fix it by not making the fields static but rather by not trying to access them from the class.
Also give those fields setter methods if you're going to want to change them outside the class. And getter methods if you want to query them
You're ignoring the fact that a JFrame's contentPane uses BorderLayout by default. This layout will cover over previously added components by anything added next. If you need multiple components within this container, use a different layout
But your main problem is that Rect should not extend JPanel, it should not be a component class but rather it should be a logical class.
Instead create one class that extends JPanel and does all the drawing, and then give it multiple Rect instances to draw within its single paintComponent method. You could use an ArrayList<Rect> for this.
Add this single drawing JPanel to the JFrame. Then there would be no need to change the JFrame's layout manager if you do this since BorderLayout would work nicely, allowing the drawing JPanel to fill the center of the JFreme.
Minor quibbles:
Avoid giving your classes names that clash with common Java core classes, such as Pattern which is frequently used in Java regular expressions analysis
Not sure why you even need the Pattern class since it doesn't do anything useful that couldn't be done in the main method.
For instance, Rect (or here named Rect2 to show it's different from your class) could look something like:
// imports here
public class Rect2 {
private Color myColor = Color.RED;
private int x = 10;
private int y = x;
private int width = 200;
private int height = width;
public Rect2() {
// default constructor
}
public Rect2(Color myColor, int x, int y, int width, int height) {
this.myColor = myColor;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
// method used to allow the rectangle to be drawn
public void draw(Graphics g) {
g.setColor(myColor);
g.fillRect(x, y, width, height);
}
public void setMyColor(Color myColor) {
this.myColor = myColor;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
// more setters and some getters if need be
}
and the drawing JPanel something like:
// imports here
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private List<Rect2> rectList = new ArrayList<>();
// ..... more code
public void addRect2(Rect2 rect) {
rectList.add(rect);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// iterate through the rectList and draw all the Rectangles
for (Rect2 rect : rectList) {
rect.draw(g);
}
}
// ...... more code
}
and it could be put into a JFrame like so....
Rect2 rectA = new Rect2();
Rect2 rectB = new Rect2();
rectB.setMyColor(Color.BLUE);
rectB.setX(300);
rectB.setY(300);
// assuming that the class's constructor allows sizing parameters
DrawingPanel drawingPanel = new DrawingPanel(1000, 800);
drawingPanel.addRect2(rectA);
drawingPanel.addRect2(rectB);
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawingPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Related
I'm drawing Circles on JFrame using JComponent (AWT/SWING) and I want to make sure that when the user resizes the frame, that certain calculations are made and circles are drawn on screen dynamically (whether if it's bigger, smaller, moved to left or right etc.). I implemented the ComponentAdapter event and componentResized method however I'm struggling with coming up with something that is dynamic. Here's my code:
CircleViewer Class
import javax.swing.JFrame;
import java.awt.event.*;
public class CircleViewer
{
public static void main(String[] args)
{
final JFrame frame = new JFrame("Circle Shapes");
final CirclePanel panel = new CirclePanel();
// Class for Mouse Listener which implements the necessary interfaces
class MousePressListener implements MouseListener, MouseMotionListener
{
public void mouseClicked(MouseEvent event) { }
public void mouseEntered(MouseEvent event) { }
public void mouseExited(MouseEvent event) { }
public void mouseWheelMoved(MouseWheelEvent event) { }
public void mouseMoved(MouseEvent event) { }
public void mousePressed(MouseEvent event) { }
#Override
public void mouseDragged(MouseEvent event)
{
var x = event.getX();
var y = event.getY();
panel.moveTo(x, y);
}
#Override
public void mouseReleased(MouseEvent event)
{
panel.finalMove();
}
}
panel.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent event)
{
panel.frameResizeCalculation(frame.getWidth(), frame.getHeight());
}
});
MousePressListener listener = new MousePressListener();
panel.addMouseListener(listener);
panel.addMouseMotionListener(listener);
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setVisible(true);
}
public static final int FRAME_WIDTH = 700;
public static final int FRAME_HEIGHT = 500;
}
CirclePanel Class
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.util.ArrayList;
import java.awt.Color;
import java.lang.Math;
import java.awt.BasicStroke;
import java.awt.Stroke;
public class CirclePanel extends JComponent
{
private int mouseX;
private int mouseY;
private ArrayList<Circle> circleList;
private final BasicStroke dashLine = new BasicStroke(1,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL,
0, new float[]{6}, 0);
private Circle newCircle;
private final Color newCircleColor = Color.RED;
private final Color finalCircleColor = Color.BLUE;
public CirclePanel()
{
this.circleList = new ArrayList<Circle>();
this.mouseX = 0;
this.mouseY = 0;
}
public void moveTo(int x, int y)
{
mouseX = x;
mouseY = y;
if (newCircle == null)
{
newCircle = new Circle(x,y,0);
}
else
{
int dX = newCircle.get(0) - mouseX;
int dY = newCircle.get(1) - mouseY;
newCircle.set(2, (int)Math.sqrt(dX*dX + dY*dY));
}
repaint();
}
public void finalMove()
{
if (newCircle != null)
{
circleList.add(newCircle);
newCircle = null;
repaint();
}
}
// Do something here and change X-Y coordinates and radius of the circles and finally call repaint() method of Graphics2D
public void frameResizeCalculation(int width, int height)
{
var dX = CircleViewer.FRAME_WIDTH - width;
var dY = CircleViewer.FRAME_HEIGHT - height;
}
public void paintComponent(Graphics g)
{
g.setColor(finalCircleColor);
for (Circle circle : circleList)
{
drawCircle(g, circle);
}
Circle c = newCircle;
if (c != null)
{
g.setColor(newCircleColor);
drawCircle(g, c);
Graphics2D g2 = (Graphics2D)g.create();
g2.setStroke(dashLine);
g2.drawLine(c.get(0), c.get(1), mouseX, mouseY);
g2.dispose();
}
}
public void drawCircle(Graphics g, Circle c)
{
g.drawOval(c.get(0) - c.get(2), c.get(1) - c.get(2), c.get(2) * 2, c.get(2) * 2);
}
}
and lastly, the Circle
public class Circle
{
private int x;
private int y;
private int radius;
public Circle(int x, int y, int radius)
{
this.x = x;
this.y = y;
this.radius = radius;
}
public int get(int option)
{
switch (option)
{
case 0:
return this.x;
case 1:
return this.y;
case 2:
return this.radius;
}
return 0;
}
public void set(int option, int value)
{
switch (option)
{
case 0: //set x
this.x = value;
break;
case 1:
this.y = value;
break;
case 2:
this.radius = value;
break;
}
}
}
that certain calculations are made and circles are drawn on screen dynamically (whether if it's bigger, smaller, moved to left or right etc.).
Well you need to define what YOU want to happen when the frame is resized. We can't tell you what to do.
However, before you worry about that you need to restructure your classes to make it possible to have the dynamic painting.
I see the following issues with the basic code:
Forget about the frame size. That size is irrelevant to the custom painting that will be done in your CirclePanel. That is the size of the CirclePanel is NOT the same as the size of your frame, since the frame size includes the frame borders and title bar. Your logic should be based on the size of the panel, not the frame.
When doing custom painting it is also the responsibility of the component to override the getPreferredSize() method to return the preferred size of the component. Then in your code you add the panel to the frame and then invoke the pack() method on the frame.
You call your class CirclePanel, but you extend JComponent. Why? Give your class a proper name. If you want to extend JComponent then call your class CircleComponent. If you want to call your class CirclePanel, then extend JPanel. But you also need to understand the difference between extending JComponent and JPanel. All Swing components are responsible for clearing the background of the component BEFORE doing any painting. A JPanel does this for you automatically, you just invoke super.paintComponent(...) at the start. A JComponent does NOT clear the background so you must clear it by setting the color of the Graphics object and then invoke fillRect(0, 0, getWidth(), getHeight()) to paint the background.
The Circle object should contain all the information needed to paint itself. You already have the x, y, radius values. I would suggest you also need a Color property so each Circle can be a different color.
The Circle object should then know how to paint itself using its properties. Therefore your Circle class should have a method, lets say draw(Graphics grapics). You then use the properties of the class to draw the oval. So this means the paintComponent() method would invoke the draw(...) method of the Circle class and you would remove the drawOval(...) method you currently have.
A "getter" method does not take parameters. If you feel other classes need to know the x, y, radius properties then create getX(), getY() and getRadiout() methods. I would suggest you don't need the get()/set() methods.
I suggest you first need to implement the above suggestions before making the painting dynamic.
Next, you don't need a ComponentListener added to the panel. Instead you need to add logic to the paintComponent(...) method of your CirclePanel class. The paintComponent() method will be invoked automatically every time the size of the panel changes. The basic logic would be to determine a "multiplier" to be used when painting each Circle.
So you can use the getPreferredSize() method to get the preferred width and you can use the getWidth() method of the panel to get the current size. So your multiplier would be:
double multiplierX = getWidth() / getPreferredSize().x;
Now this information needs to be passed to the Circle objects draw(...) method, so the method signature would become draw(Graphics g, double multiplierX). When you invoke the drawOval(...) method you apply the multiplier to the "x" parameter. This should cause the Circles to shift in the horizontal direction as the frame is resized.
You would then repeat the above step for the multiplierY to have the Circles shift in a vertical direction.
You would then need to decide how you want to affect the radius?
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 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.
Hello people,
I am trying to write an animated character for a multitouch screen. I want my object to have 5 eyes and each of whose pupil to be dragged and dropped differently, within the eye of course.
I have tried to do it all in a single class and the problem seems to be assigning mouse handlers to each of the five pupils! In other words, if I move one pupil, all the pupils are moving.
Then, I resorted to using a bespoke class just to the pupil. When I use it by itself, the pupil is draggable. However, when I use it as an object in the eyes class, the pupil is static! No clicks registered, no mouse activity tracked.
I have looked at tutorials, other related issues with mouse handlers and could not make any progress. I changed the code a dozen times from various tutorials and suggestions before finally posting here. Any ideas where I am missing the cue? Any pointers would be greatly appreciated.
Thanks in advance.
PS: I know I am yet to put the constraints on the pupil movement within the eye.
Code for main eyes class:
package rollEyes;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FiveEyes extends JPanel
{
private static final long serialVersionUID = 1L;
private static final int SIZE = 512;
private int a = SIZE / 2;
private int b = a;
private int r = 4 * SIZE / 5;
private int n;
int circleSize=30;
Pupil dc = new Pupil(1);
public FiveEyes(int n)
{
super(true);
this.setPreferredSize(new Dimension(SIZE, SIZE));
this.n = n;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
a = getWidth() / 2;
b = getHeight() / 2;
int m = Math.min(a, b);
r = 4 * m / 5;
int r2 = Math.abs(m - r) / 2;
int numOfEyes = 5;
for (int i = 0; i < numOfEyes ; i++)
{
Graphics2D g2d2 = (Graphics2D) g;
double t = 2 * Math.PI * i / n;
int x = (int) Math.round(a + r * Math.cos(t));
int y = (int) Math.round(b + r * Math.sin(t));
drawEyeSockets(g2d2, x,y, 2*r2,2*r2);
}
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
create();
}
});
}
public void drawEyeSockets(final Graphics2D g2, int x, int y, int w, int h)
{
g2.drawOval(x,y,w,h);
dc.drawCircle(g2, x+12, y+12);
}
private static void create()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FiveEyes fivey = new FiveEyes(5);
f.add(fivey);
f.pack();
f.setVisible(true);
}
}
Code for the Pupil class:
package rollEyes;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Pupil extends JPanel
{
private static final long serialVersionUID = 1L;
int radius=50;
int x_after = 50;
int y_after = 50;
MouseHandler mh ;
private static int n =1;
public Pupil(int n)
{
super(true);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
drawCircle(g2d,x_after,y_after);
}
public void drawCircle(final Graphics2D g2d, int x, int y)
{
g2d.setColor(Color.BLUE);
g2d.fillOval(x, y, radius/2, radius/2);
mh = new MouseHandler();
this.addMouseListener(mh);
this.addMouseMotionListener(mh);
}
private static void create()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Pupil dc = new Pupil(n);
f.add(dc);
f.pack();
f.setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
create();
}
});
}
private class MouseHandler extends MouseAdapter
{
boolean circleClicked=false;
public void mouseReleased(MouseEvent e)
{
circleClicked = false;
}
public void mousePressed(MouseEvent me)
{
circleClicked = true;
}
public void mouseDragged(MouseEvent me)
{
if (circleClicked)
{
x_after = me.getX();
y_after = me.getY();
repaint();
}
}
}
}
You have Pupil extend JPanel, but really shouldn't be doing that. Instead, use the concepts that you've learned in your current Pupil class -- how to draw a movable circle, and extend it in the larger FiveEyes class, only this time create a List<Pupil> and draw them. My suggestions:
Make Pupil not extend JPanel. Instead give it the machinery to draw circles in certain locations and to have that location changed.
Also you will need to give it a way to recognize if its circle has been clicked by giving it a contains(Point p) method. One way to do this is to use a Shape object, or you can roll your own method.
Give FiveEyes a List<Pupil> that in reality is an ArrayList<Pupil> and fill it with Pupil objects.
In FiveEyes paintComponent(...) method, iterate through this List telling each Pupil to draw itself.
In your FiveEyes MouseAdapter's mousePressed(...) method, iterate through your Pupil List to see if a Pupil has been clicked on. If so, move it.
Alternatively, you could create a Pupil BufferedImage, put it into an ImageIcon, and put that into a JLabel, and then allow your FiveEyes class's MouseAdapter to drag the labels around.
First of all, this code
mh = new MouseHandler();
this.addMouseListener(mh);
this.addMouseMotionListener(mh);
must only be called once. You're adding millions of mouse handlers to the component!
And you need five instances of Pupil, one for each eye. Right now, you have only one, so of course the rendering will only yield one result.
Lastly, you must make sure that only one eye is "active" at a time (i.e. not all of them should receive mouse events or not all of them should process them).
Here's my code:
class Ramka extends JFrame
{
public static final int SZEROKOSC = 800;
public static final int WYSOKOSC = 600;
Container powZawartosci = getContentPane();
public Ramka()
{
setSize(SZEROKOSC, WYSOKOSC);
setTitle("Siatka bryły by Paweł Mysior");
}
public void addRectangle(int startX, int startY, int sizeX)
{
drawRectangle rect = new drawRectangle(startX, startY, sizeX);
powZawartosci.add(rect);
}
class drawRectangle extends JPanel
{
private int a, startX, startY;
public drawRectangle(int startX, int startY, int a) // square
{
this.a = a;
this.startX = startX;
this.startY = startY;
}
public void paintComponent(Graphics g)
{
Rectangle2D rect = new Rectangle2D.Double(startX, startY, a, a);
Graphics2D g1 = (Graphics2D) g;
g1.draw(rect);
}
}
public class Main
{
public static void main(String[] args)
{
Ramka ramka = new Ramka();
ramka.addRectangle(200, 200, 50);
ramka.addRectangle(100, 100, 100);
ramka.addRectangle(300, 300, 150);
ramka.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ramka.setVisible(true);
}
}
What I want it to do is draw three rectangles (set aside the functionality and sense of doing so, I'm still just learning).
But it draws only the last one, starting at 300 and 300. I don't really understand the paintComponent thing...
Thanks in advance for any help,
Paul
I beleive that you are adding three JPanels on top of each other. This seems like an odd way to draw rectangles, but with this design, you need to use a LayoutManager.
Check out this link, and try to learn. The code below should do the trick though.
...
Container powZawartosci = getContentPane();
public Ramka()
{
setSize(SZEROKOSC, WYSOKOSC);
setTitle("Siatka bryły by Paweł Mysior");
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));//Only this line is inserted.
}
public void addRectangle(int startX, int startY, int sizeX)
{
drawRectangle rect = new drawRectangle(startX, startY, sizeX);
powZawartosci.add(rect);
}
...
In your JPanel derivative, you can keep track of the Rectangles that you need to draw. I am writing the code below spontanously, so check for errors first.
class RectangleDrawer extends JPanel{
ArrayList<Rectangle> rList = new ArrayList()<Rectangle>;
public void addRectangle(Rectangle rect){
rList.add(rect);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
for(int i=0; i<rList.size(); r++){
g.drawRectangle(rList.get(i));
}
}
}
The problem basically is that you're using two different levels of abstraction here.
In the first, you are adding a component to your JFrame, which is fine at some point.
You're adding your "DrawRectangle" instance, just the same way you would add a new button, a label or another panel. The problem comes when you add components in the same position. JFrame's main panel ( the content pane ) uses a "Border" layout manager that places the component in the middle if you don't add any constraint.
As a convenience, BorderLayout interprets the absence of a string specification the same as the constant CENTER
So, this line:
powZawartosci.add(rect);
Always adds your component in the "center", overriding the previous one. That's why you only saw one rectangle.
The second level of abstraction used here is painting the component yourself. This is low level and you have to tell the component who to draw each line and where.
That's fine, but if you want to draw several rectangles in the same component, you have to hold the references for each one ( using a collection like a list ) and then iterate that collection and draw them all.
Like this:
many http://img40.imageshack.us/img40/8125/capturadepantalla201001nd.png
I took your code, and changed it, to reflect what I'm saying. The final result, uses the same component, but this component in turn draws all the rectangles.
Notice also the naming/brace style, while is not mandatory it is common while programming in Java
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
class Ramka extends JFrame {
public static final int SZEROKOSC = 800;
public static final int WYSOKOSC = 600;
Container powZawartosci = getContentPane();
DrawRectangle rectangle = new DrawRectangle();
public Ramka() {
setSize(SZEROKOSC, WYSOKOSC);
setTitle("Siatka bryły by Paweł Mysior");
powZawartosci.add( new JLabel("Several rectangles are being displayed"), BorderLayout.NORTH );
powZawartosci.add(rectangle);
}
public void addRectangle(int startX, int startY, int sizeX) {
this.rectangle.addRectangle( startY, startY, sizeX );
}
}
class DrawRectangle extends JPanel {
private java.util.List<Rectangle2D> squares;
//private int a, startX, startY;
public DrawRectangle(){
squares = new ArrayList<Rectangle2D>();
}
public void addRectangle(int startX, int startY, int a) { // square
squares.add( new Rectangle2D.Double(startX, startY, a, a) ) ;
//this.a = a;
//this.startX = startX;
//this.startY = startY;
}
public void paintComponent(Graphics g) {
Graphics2D g1 = (Graphics2D) g;
for( Rectangle2D rect : squares ) {
g1.draw(rect);
}
}
}
public class Main {
public static void main(String[] args) {
Ramka ramka = new Ramka();
//ramka.addRectangle(200, 200, 50);
//ramka.addRectangle(100, 100, 100);
//ramka.addRectangle(300, 300, 150);
for( int i = 0 ; i < 20 ; i++ ){
ramka.addRectangle( i * 10 , i * 10 , i * 20 );
}
ramka.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ramka.setVisible(true);
}
}