Search code examples
javaswingawtgraphics2d

Graphics2D is not drawing correctly, its only drawing part of the oval


I'm trying to draw an oval on a JFrame using Graphics2D, I want it to resize with the window which technically it does, it just not drawing about a 3rd of the oval. It's definitely something I'm doing wrong, I'm new to the Graphics2D part of Java.

I wasn't sure if it was my computer so I have tried running my code on a different PC to have it happen again so I'm not sure where I've gone wrong.

import javax.swing.*;
import java.awt.*;

public class ClockViewer {
    public static void main(String[] args) {
        //create frame
        JFrame frame = new JFrame();
        final int Frame_Width = 110;
        final int Frame_Height = 130;

        //set frame attributes
        frame.setSize(Frame_Width, Frame_Height);
        frame.setTitle("A Really Descriptive Title...");
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //get pane attributes
        System.out.println(frame.getContentPane().getWidth());
        System.out.println(frame.getContentPane().getHeight());

        //create ellipse
        EllipseComponent ellipse = new EllipseComponent();

        //add ellipse to frame
        while(frame.getContentPane().getHeight() > 0 && frame.getContentPane().getWidth() > 0) {
            int posX = Math.round(frame.getContentPane().getWidth() / 100) * 20;
            int posY = Math.round(frame.getContentPane().getHeight() / 100) * 20;
            int Width = Math.round(frame.getContentPane().getWidth() / 100) * 80;
            int Height = Math.round(frame.getContentPane().getHeight() / 100) * 80;
            ellipse.setAll(posX, posY, Width, Height);
            frame.add(ellipse);
        }
    }
}

This is the next file:

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;

public class EllipseComponent extends JComponent {
    //global variables for ellipse drawing
    int posX = 0;
    int posY = 0;
    int Width = 0;
    int Height = 0;

    //getters for getting variables values
    public int getPosX() {
    return this.posX;
    }
    public int getPosY() {
        return this.posY;
    }
    @Override
    public int getWidth() {
        return this.Width;
    }
    @Override
    public int getHeight() {
        return this.Height;
    }

    //setters for setting variable values
    public void setPosX(int newPosX) {
        this.posX = newPosX;
    }
    public void setPosY(int newPosY) {
        this.posY = newPosY;
    }
    public void setWidth(int newWidth) {
        this.Width = newWidth;
    }
    public void setHeight(int newHeight) {
        this.Height = newHeight;
    }

    //setter for all variables
    public void setAll(int newPosX, int newPosY, int newWidth, int newHeight) {
        this.posX = newPosX;
        this.posY = newPosY;
        this.Width = newWidth;
        this.Height = newHeight;
    }

    //paint ellipse using graphics
    @Override
    public void paint(Graphics g) {
        Graphics g2 = (Graphics2D) g;
        g2.drawOval(posX, posY, Width, Height);
    }
}

Thanks for any help you all can provide me ^^

Edit: I'm getting these errors if it helps:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: No such child: 0

at java.awt.Container.getComponent(Container.java:336)

at javax.swing.JComponent.rectangleIsObscured(JComponent.java:4390)

at javax.swing.JComponent.paint(JComponent.java:1054)

at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210)

at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)

at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)

at javax.swing.RepaintManager.paint(RepaintManager.java:1272)

at javax.swing.JComponent._paintImmediately(JComponent.java:5158)

at javax.swing.JComponent.paintImmediately(JComponent.java:4969)

at javax.swing.RepaintManager$4.run(RepaintManager.java:831)

at javax.swing.RepaintManager$4.run(RepaintManager.java:814)

at java.security.AccessController.doPrivileged(Native Method)

at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)

at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)

at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)

at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)

at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)

at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)

at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)

at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)

at java.awt.EventQueue.access$500(EventQueue.java:97)

at java.awt.EventQueue$3.run(EventQueue.java:709)

at java.awt.EventQueue$3.run(EventQueue.java:703)

at java.security.AccessController.doPrivileged(Native Method)

at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)

at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)

at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)

at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)

at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)

at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)

at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)

at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Solution

  • I am not getting the error you get. However, this is not the right way to do custom painting in Swing. I suggest you to take a look on how to perform custom painting. Long story short though, this thing you are trying with the while condition will not work. Instead, let the component to be painted according to its parent size and coordinates without having to set them explicit :

    /**
     * @Overide paint method was a thing in AWT.
     * In Swing you must override paintComponent (and call super.paintComponent())
     * in order to respect the paint chain.
     * 
     */
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (getParent() != null) { //Paint according to parent
            Graphics g2 = (Graphics2D) g;
            //Calculations
            int posX = Math.round(getParent().getWidth() / 100) * 20;
            int posY = Math.round(getParent().getHeight() / 100) * 20;
            int Width = Math.round(getParent().getWidth() / 100) * 80;
            int Height = Math.round(getParent().getHeight() / 100) * 80;
            g2.drawOval(posX, posY, Width, Height);
        }
    }
    

    Another not necessary thing you did is this:

    @Override
    public int getWidth() {
        return this.Width;
    }
    

    Overriding getWidth and getSize of a component would not lead you anywhere.

    Other useful links I recommend you to read are the Initial Threads and Why shouldn't I call setVisible(true) before adding components?

    The full example:

    public class ClockViewer {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(()->{
                // create frame
                JFrame frame = new JFrame();
                final int Frame_Width = 110;
                final int Frame_Height = 130;
    
                // set frame attributes
                frame.setSize(Frame_Width, Frame_Height);
                frame.setTitle("A Really Descriptive Title...");
    
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
                // get pane attributes
                System.out.println(frame.getContentPane().getWidth());
                System.out.println(frame.getContentPane().getHeight());
    
                // create ellipse
                JComponent ellipse = new JComponent() {
                    @Override
                    protected void paintComponent(Graphics g) {
                        super.paintComponent(g);
                        if (getParent() != null) { //Paint according to parent
                            Graphics g2 = (Graphics2D) g;
                            //Calculations
                            int posX = Math.round(getParent().getWidth() / 100) * 20;
                            int posY = Math.round(getParent().getHeight() / 100) * 20;
                            int Width = Math.round(getParent().getWidth() / 100) * 80;
                            int Height = Math.round(getParent().getHeight() / 100) * 80;
                            g2.drawOval(posX, posY, Width, Height);
                        }
                    }
                };
                frame.add(ellipse);
                frame.setVisible(true);
            });
    
        }
    }