Reputation: 15
I'm having an unclear problem with drawing shapes using paintComponent.
public class Shape extends JPanel {
private String shape;
private boolean isFill;
private int x;
private int y;
private int width;
private int height;
private Color color;
public Shape(String shape, boolean isFill, int x, int y, int width, int height, Color color) {
this.shape = shape;
this.isFill = isFill;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
@Override
public boolean equals (Object O){
if (O instanceof Shape){
if (this.getWidth() == ((Shape) O).getWidth() && this.getHeight() == ((Shape) O).getHeight()){
return true;
}
}
return false;
}
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(this.getColor());
if (this.getShape().equals(Constants.IS_RECTANGLE)){
if (this.isFill()){
g.fillRect(this.getX(), this.getY(),this.getWidth(),this.getHeight());
}
else{
g.drawRect(this.getX(), this.getY(),this.getWidth(),this.getHeight());
}
}
else{
if (this.isFill()){
g.fillOval(this.getX(), this.getY(),this.getWidth(),this.getHeight());
}
else{
g.drawOval(this.getX(), this.getY(),this.getWidth(),this.getHeight());
}
}
}
public void drawShape(JPanel panel){
panel.add(this);
panel.setVisible(true);
}
This is my Shape class, and I want to add some into a panel (using JPanel).
public class GuiManagement {
public JFrame createScreen(){
// Creating the screen
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle(Constants.APP_NAME);
frame.setSize(Constants.SCREEN_WIDTH,Constants.SCREEN_HEIGHT);
return frame;
}
public JPanel createPanel(JFrame frame, int x, int y, int width, int height, String borderSpace){
// Creates a panel, set its bounds and direction in the screen
JPanel panel = new JPanel();
panel.setLayout(null);
panel.setBounds(x,y,width,height);
frame.add(panel, borderSpace);
panel.setVisible(true);
return panel;
}
Those are my 2 functions that creates and returns a frame and panel.
public static void main (String [] args){
GuiManagement g = new GuiManagement();
JFrame frame = g.createScreen();
JPanel panel = g.createPanel(frame,0,0,800,800,BorderLayout.NORTH);
panel.setBackground(Color.GREEN);
Shape s = new Shape("Oval",true,40,40,100,100,Color.DARK_GRAY);
s.drawShape(panel);
frame.setLayout(new BorderLayout(30,30));
frame.setVisible(true);
}
And that's the main class. Now, the program should draw a simple oval, but when I'm running the main function, it looks like the panel allocates the right space for drawing the shape (The green part of the frame is the panel I created), but draws it only in a small part of that space. I added an example of the problem in the picture below. PRINT SCREEN EXAMPLE OF THE PROBLEM HERE!!!
Thanks for your help! :)
Upvotes: 0
Views: 84
Reputation: 347334
So, you have a series of compounding issues and a fundamental misunderstanding of how the coordinate system works in Swing.
Doing...
g.fillRect(this.getX(), this.getY(),this.getWidth(),this.getHeight());
is a bad idea.
The getX
and getY
represent the location of the component within it's parent component space, so you've now offset the position of your drawing by x * 2
and y * 2
I renamed the getX and getY functions
Well, that's not in your original code, and it would just cause more mayhem
So, I setup a small test using...
Shape shape = new Shape("", true, 25, 25, 50, 50, Color.RED);
shape.setBorder(new LineBorder(Color.BLUE));
shape.setBounds(25, 25, 50, 50);
(I modified the Shape
to always paint a oval) and this produces...
You can see that the shape is now offset within then Shape
component.
Instead, you should paint from 0x0
, which will be the top/left corner of the Shape
component.
I modified the Shape
code to use...
if (this.isFill()) {
g.fillOval(0, 0, this.getWidth(), this.getHeight());
} else {
g.drawOval(0, 0, this.getWidth(), this.getHeight());
}
and it now generates...
IMHO, using a component in this way is a bad idea. null
layouts are notoriously difficult to manager and if all you want to do is paint shapes, then a pure custom painting route is generally better/easier to manager.
This idea basically constructs a single component which can paint any number of "shape" objects
Because I like to start with a "abstract" concept, which can be built up from, I start with a basic concept of a Shape
, which defines all the information all Shape
implementations would need...
public interface Shape {
public Rectangle getBounds();
public boolean isFilled();
public Color getColor();
public void paint(Graphics2D g2d);
}
Then I define an abstract
implementation of which covers the core/common functionality...
public abstract class AbstractShape implements Shape {
private Rectangle bounds;
private boolean filled;
private Color color;
public AbstractShape(Rectangle bounds, boolean filled, Color color) {
this.bounds = bounds;
this.filled = filled;
this.color = color;
}
public Rectangle getBounds() {
return bounds;
}
public boolean isFilled() {
return filled;
}
public Color getColor() {
return color;
}
}
And then I define the actual implementations, in this case, I've only done the OvalShape
, but you could have a triangle, rectangle, arc and over shapes...
public class OvalShape extends AbstractShape {
public OvalShape(Rectangle bounds, boolean filled, Color color) {
super(bounds, filled, color);
}
@Override
public void paint(Graphics2D g2d) {
g2d.setColor(this.getColor());
Rectangle bounds = getBounds();
if (isFilled()) {
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
} else {
g2d.drawOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
}
Then, we need some way to show those shapes...
public class ShapeContainer extends JPanel {
private List<Shape> shapes;
public ShapeContainer() {
shapes = new ArrayList<>(25);
}
public void add(Shape shape) {
shapes.add(shape);
repaint();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
Graphics2D g2d = (Graphics2D) g.create();
shape.paint(g2d);
g2d.dispose();
}
}
}
This means, all the shapes work within the containers coordinate context and you don't need to worry about trying to translate them should you want to add new features down the track...
And because I know that's probably a bit to take in, a runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
Shape shape = new OvalShape(new Rectangle(25, 24, 50, 50), true, Color.RED);
ShapeContainer container = new ShapeContainer();
container.add(shape);
frame.add(container);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Shape {
public Rectangle getBounds();
public boolean isFilled();
public Color getColor();
public void paint(Graphics2D g2d);
}
public abstract class AbstractShape implements Shape {
private Rectangle bounds;
private boolean filled;
private Color color;
public AbstractShape(Rectangle bounds, boolean filled, Color color) {
this.bounds = bounds;
this.filled = filled;
this.color = color;
}
public Rectangle getBounds() {
return bounds;
}
public boolean isFilled() {
return filled;
}
public Color getColor() {
return color;
}
}
public class OvalShape extends AbstractShape {
public OvalShape(Rectangle bounds, boolean filled, Color color) {
super(bounds, filled, color);
}
@Override
public void paint(Graphics2D g2d) {
g2d.setColor(this.getColor());
Rectangle bounds = getBounds();
if (isFilled()) {
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
} else {
g2d.drawOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
}
public class ShapeContainer extends JPanel {
private List<Shape> shapes;
public ShapeContainer() {
shapes = new ArrayList<>(25);
}
public void add(Shape shape) {
shapes.add(shape);
repaint();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
Graphics2D g2d = (Graphics2D) g.create();
shape.paint(g2d);
g2d.dispose();
}
}
}
}
Upvotes: 2