Java Won't draw paintComponent from another class - java

I have a main class and a subclass Circle that has a paintComponent method within it. I am trying to call that method to my main class to draw the Circle but nothing will appear and i'm not sure why?
My Circle class:
public class Circle extends Shape {
Integer rad;
public Circle(int posx,int posy, int rad) {
this.posx = posx;
this.posy = posy;
this.rad = rad;
}
class drawCircle extends JPanel {
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.green);
g.fillOval(posx,posy,rad,rad);
}
}
}
My main method snippets
public class drawFrame extends JFrame {
JPanel panel1;
JPanel panel2;
Square square1;
Circle circle1;
public drawFrame() {
panel2= new JPanel();
panel1= new JPanel();
int rad = 0;
circle1 = new Circle(posx, posy,rad);
Circle.drawCircle drawCi = circle1.new drawCircle();
add(panel1, BorderLayout.CENTER);
panel1.add(drawCi);
So essentially I've just given some snippets of the main part of the code. What I tried doing was creating a new object from the Circle drawCircle inner class,adding it to the mainPanel, so that my Jframe outputs my mainPanel contents which should be the new Circle object I've created? But It seems this doesn't work.

Your code is confusing, and I recommend simplifying.
Problems:
You're using an inner class, drawCircle unnecessarily. Don't nest classes when not needed, but instead give the drawing component its own stand-alone class.
You're creating a drawing component, but never adding it to the GUI. Without adding it to a top-level window, here the JFrame, it will never display. Edit: You are in fact adding it, but its preferred size is 0,0
You're overriding paint not paintComponent. This carries both problems (jerky animations when you later try to do this) and risk (paint also affects a component's child component and border drawing).
Your drawing component has no preferred size set, so it will size to [0,0], and this is not conducive to showing its drawing.
Instead (again) create a stand-alone, non-nested class that extends JPanel, override its paintComponent where you draw the circle and be sure to add it to the displayed JFrame. Also either set the drawing component's preferred size or override its getPreferredSize() method. Of course be sure to pack() the JFrame after adding components and then calling setVisible(true) on it after packing it.
A minor issue: you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
Minor quibble 2: You're rad field (presumably named for radius) should be called "diameter" since it's being used as a diameter, not a radius.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawCircle extends JPanel {
private static final int PANEL_WIDTH = 600;
private static final Color CIRCLE_COLOR = Color.GREEN;
private int posx;
private int posy;
private int diameter;
public DrawCircle(int posx, int posy, int diamter) {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_WIDTH));
this.posx = posx;
this.posy = posy;
this.diameter = diamter;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
g2.fillOval(posx, posy, diameter, diameter);
}
private static void createAndShowGui() {
DrawCircle mainPanel = new DrawCircle(100, 100, 400);
JFrame frame = new JFrame("DrawCircle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

Related

What did super.paintComponent(g) do?

So in the past few days I've tried to implement an easier version of a graph plotter.
One big problem I was confronted with was a bug that occured on repainting.
Basically I've designed my program in one class which is responsible for drawing the whole coordinate system and the given function after clicking a JButton in an other class. The other class contains the JButton which is pressed. After pressing the JButton it calls a function in the coordinate system class which repaints the picture. Both of those classes are extending JPanel.
The bug was that when I was doing the repainting on pressing the button, the button was drawn on the coordinate System and not in its original place, so in other words on the other JPanel even though I didn't change a thing about placements and stuff. Both classes were added to a JFrame which use a GridLayout.
Can anyone tell me why super.paintComponent(g); solved that bug?
Edit: Added Code
Window class
public class main {
public static void main(String[] args) throws SemanticFailureException {
int x = 800;
int y = 600;
JFrame frame = new JFrame();
frame.setLayout(new GridLayout());
frame.setTitle("Function plotter");
frame.setSize(2*x, 2*y);
Surface test = new Surface(x, y, 10);
CommandDraw test1 = new CommandDraw(x/2,y,test);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(test);
frame.add(test1);
frame.setVisible(true);
}
}
Coordinate System class: (changed drawing the coordinate system to a rectangle for simplicity, the bug still occures with only drawing a rectangle)
public class Surface extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
boolean drawFunct;
public Surface(int x1, int y1, int coordLength) {
setSize(x1,y1);
drawFunct = false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // without this the jbutton occures on the left
// create Graphics object to get more functions
Graphics2D g2 = (Graphics2D) g;
// draw Plotter
drawFunction(g2);
if (drawFunct)
g2.drawLine(0, 0, 80, 80);
}
public void drawFunction(Graphics2D g) {
g.drawRect(40, 40, 30, 30);
}
public void redraw() {
drawFunct = true;
repaint();
}
}
The Class with the JButton:
public class CommandDraw extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
JButton makeDraw;
JTextField inputPoly;
Surface surf;
public CommandDraw(int x, int y, Surface surf) {
this.surf = surf;
setSize(x,y);
setLayout(new FlowLayout());
makeDraw = new JButton("draw Function");
makeDraw.setBackground(Color.LIGHT_GRAY);
makeDraw.setFocusable(false);
makeDraw.addActionListener( new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
surf.redraw();
}
});
add(makeDraw);
inputPoly = new JTextField("Input polynomial");
inputPoly.setHorizontalAlignment(JTextField.CENTER);
add(inputPoly);
}
}
Can anyone tell me why super.paintComponent(g); solved that bug?
Because the call to paintComponent(g) of the superclass (JPanel) will guarantee that panel will be rendered as expected before you do your paint operations. You need to make sure that your JPanel will behave, in the Graphics perspective, like any other JPanel.
A template for a customized JPanel to draw should be something like:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class MyDrawPanel extends JPanel {
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g );
// create a new graphics context based on the original one
Graphics2D g2d = (Graphics2D) g.create();
// draw whatever you want...
g2d.dispose();
}
}
EDIT
You need to call super.paintComponent(g) to tell that the JPanel paintComponent(Graphics) version should be execute before you start doind your customized things. It's something like to ask the JPanel to prepare its paint capabilities to be used. A good starting point to these kind of doubts is the documentation: https://docs.oracle.com/javase/10/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics)
The dispose() method is being called in the copy of the original Graphics. If you dispose the Graphics that is passed to the method, you may have some issues too. You could use the original Graphics to perform you painting operations, but you shouldn't, so a better practice is to make a copy of the original Graphics and dispose it after using.
Take a look here too: How does paintComponent work?

Adding random graphical squares to ArrayList (JAVA)

I want to learn to use ArrayList together with graphical elements in java.
I have a class that creates squares with a random x and a random y position:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Square extends Canvas {
public int x, y;
Random r = new Random();
public void paint(Graphics g) {
x = r.nextInt(640);
y = r.nextInt(480);
g.setColor(Color.BLACK);
g.fillRect(x, y, 30, 30);
}
}
And i have a class that creates a JFrame and Add square elements to an ArrayList. But i can't figure it out. I think the solution might be simple and technical, i just need a slight push.
import java.util.ArrayList;
import javax.swing.JFrame;
public class Frame {
public int width, height;
public String title;
public JFrame jframe;
ArrayList<Square> squares = new ArrayList<Square>();
public Frame(String title, int width, int height) {
this.width = width;
this.height = height;
this.title = title;
display();
}
public void display() {
jframe = new JFrame();
jframe.setTitle(title);
jframe.setSize(width, height);
jframe.setResizable(false);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setVisible(true);
jframe.setLocationRelativeTo(null);
for(int i = 0; i < 20; i++) {
squares.add(new Square());
}
jframe.add(squares);
}
}
Get rid of the Square class and restart.
Do not have it extend Canvas -- you do not want to unnecessarily mix AWT and Swing
And your Square class should be a logical class, not a GUI class, meaning it shouldn't extend any GUI component.
Do not randomize within the painting method.
Give your Square class x and y int fields
Give it a public void draw(Graphics g) method where it draws itself using the Graphics object passed in.
Create another class, one that extends JPanel, and give it an ArrayList<Square>.
Override this JPanel's paintComponent method.
In the override be sure to call the super's method.
In the override, iterate through the array list, calling each Square's draw method.
Place this JPanel into your JFrame.
From what I see you are doing you are adding multiple canvases to the frame which will overlap each other.
Rather create a canvas with a list of Squares(x and y ints) that it draws in its paint and then add one canvas to the frame.
Also I am not sure if JFrame.add() will add the whole canvas and resize it. Rather use a layoutManager to add your components like FlowLayout.
Layout managers

JPane "paintComponent" can't be called without JLabel

My problem that I am having is that in this code, the line will not draw at all if I get rid of the JLabel from it. I tested it out and without the JLabel stuff with in it, the paintComponent(Graphics g) won't run at all. I want to know why this is and why JLabel effects the JPanel like this.
I have read online that sometime the paintComponent(Graphics g) might not run if it is in a infinite loop but i still don't understand how the JLabel would effect this trait.
I would guess that it might have to do something with the layout but i don't really understand how layout effects things like this
MouseAction Class
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MouseAction extends JFrame{
private JPanel mousePanel;
private JLabel statusBar;
private GraphicsPanel g;
private int xS;
private int yS;
public MouseAction (){
super("Mouse example");
mousePanel = new JPanel();
statusBar = new JLabel("Deafault");
g = new GraphicsPanel(0,0,0,0);
add(mousePanel, BorderLayout.CENTER);
add(statusBar, BorderLayout.SOUTH);
MouseExploits handler = new MouseExploits();
mousePanel.addMouseListener(handler);
mousePanel.addMouseMotionListener(handler);
}
private class MouseExploits extends MouseAdapter{
public void mousePressed(MouseEvent mouse){
if(mouse.isMetaDown()){
xS= mouse.getX();
yS= mouse.getY();
}
}
public void mouseDragged(MouseEvent mouse){
if(!(mouse.isMetaDown() || mouse.isAltDown())){
g.updateCoordinates(mouse.getX(),mouse.getY(), xS,yS);
add(g);
System.out.printf("%d,%d\n",mouse.getX(),mouse.getY());
statusBar.setText(String.format("%d,%d",mouse.getX(),mouse.getY()));
}
}
}
}
GraphicsPanel class
import java.awt.*;
import javax.swing.*;
public class GraphicsPanel extends JPanel{
private int x;
private int y;
private int xS;
private int yS;
private Graphics dB;
private Image dBImage;
public graphics(int mouseX, int mouseY, int xSm, int ySm){
x = mouseX;
y = mouseY;
xS=xSm;
yS=ySm;
//System.out.println("Breaks2");
}
public void updateCoordinates(int mouseX, int mouseY, int xSm, int ySm){
x = mouseX;
y = mouseY;
xS=xSm;
yS=ySm;
//repaint();
}
public void paint(Graphics g){
dBImage = createImage(getWidth(),getHeight());
dB = dBImage.getGraphics();
paintComponent(dB);
g.drawImage(dBImage,0,0,this);
}
public void paintComponent(Graphics g){
//super.paintComponent(g);
System.out.println("Breaks3");
g.drawLine(xS,yS,x,y);
repaint();
}
}
MainProg class
import javax.swing.JFrame;
public class MainProg {
public static void main(String args[]){
MouseAction frameObj= new MouseAction ();
frameObj.setSize(300,230);
frameObj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameObj.setVisible(true);
}
}
EDIT: Code now contains double buffering and been revamped so the GraphicsPanel doesn't instantiate multiple of time how ever the issue of not be able to remove JLabel without breaking the program continues.
EDIT 2: after some messing around with the window after removing the JLabel, It seems to only work when i dragged on the screen,resized the window and then drag on the screen again. So it would seem there is some jpanel layout error which some how gets fixed by the jlabel being there.
First problem with your code is that you instanciate graphics at each mouseDragged event. This is weird and bad. You must only handle each mouseDragged, store the coordinates and call repaint on the single graphics instance created and added to the interface at the beginning. This will cause a refresh.
Second, the paintComponent should just make the drawings so you must use some kind of (1) double buffering (create a off-line BufferedImage and its associated Graphics, draw into it and use it in the refresh), (2) collection of Point caught in mouseDragged and use them to drawPolyline in the paintComponent method.
Third don't call repaint() inside paintComponent. This will unnecessarily triggers calls to paintComponent in a kind of never ending loop.
Fourthly don't create a double buffer each time paint is called. At each refresh you will create a new Image. Create it once, draw in it in your mouseDraggedmethod and trigger from it a repaint(), then make a drawImage in paintComponent.

Custom painting not being performed when the custom painting code is in another class

I'm trying to write some custom painting code. Specifically, I want to have a bunch of extended JPanels which paint different aspects of my GUI, but each of these extended panels holds the instructions for how it is to be painted.
I've created the code, but for some reason, the extended JPanel isn't being painted on the main JPanel in my JFrame regardless of what I do. Here is a gist of my main class and one of my extended JPanels. What am I missing?
Breakout
//Java imports
import javax.swing.JFrame;
import java.awt.Dimension;
import javax.swing.JPanel;
//Personal imports
import Ball;
public class Breakout {
public static void main (String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {//start the GUI in a new thread
public void run(){
showGUI();
}
});
}
private static void showGUI() {
JFrame frame = new JFrame("Breakout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension d = new Dimension(640,480);
frame.setMinimumSize(d);
frame.setResizable(false);
JPanel p = new JPanel();
p.add(new Ball(200,200,50,255,0,0));
frame.add(p);
frame.setVisible(true);
}
}
Ball
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
public class Ball extends JPanel {
public int x;
public int y;
public int radius;
public Color colour;
public Ball(int x, int y, int radius, int r, int g, int b) {
super();
this.x = x;
this.y = y;
this.radius = radius;
colour = new Color(r,g,b);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
//define constants
int topLeftX = x+radius;
int topLeftY = y+radius;
int diameter = radius *2;
//draw outline
g.setColor(Color.BLACK);
g.drawOval(topLeftX, topLeftY, diameter, diameter);
//fill it in
g.setColor(colour);
g.fillOval(topLeftX, topLeftY, diameter, diameter);
}
}
Using JPanels in this way is going to cause you no end of problems.
The two main problems you have are...
JPanels already have an idea of size and position, adding another x/y coordinate is just confusing and could lead you to painting off the components viewable space
The default preferred size of a JPanel is 0x0. This means when you add it another JPanel, using FlowLayout, the panel is given a size of 0x0, so nothing gets painted.
Instead, create an interface which has a method called paint and takes a Graphics2D object.
For each shape you want to paint, create a new class which implements this interface and use it's paint method to paint the object as you see fit.
Create a custom component, extending from JPanel and maintain a List of these shapes. In it's paintComponent, use a for-loop to paint each shape in the List.
This custom component should then be added to your frame...
In your showGUI method in your main class you have this code:
JPanel p = new JPanel();
p.add(new Ball(200,200,50,255,0,0));
frame.add(p);
This code creates a new JPanel then adds another JPanel to that. This is incorrect because it simply does not make sense to add another JPanel to a perfectly good JPanel that you just created. Instead just do this:
frame.getContentPane().add(new Ball(200, 200, 50, 255,0,0));
Or if you prefer:
Ball ball = new Ball(200, 200, 50, 255,0,0);
frame.getContentPane().add(ball);

Trying to add a dynamically positioned image in a JPanel on a button click

I am trying to add/draw a single Graphics object to an existing JPanel. I am generating 10 initial Graphics objects randomly sized and place in the panel, but would like to add additional drawn objects one a time, randomly sized and placed like the initial 10.
Currently, the AddNewDrawItem class is not rendering the new Graphics object.
Thank you for input.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class Painter{
private DrawingPanel dp = new DrawingPanel();
//constructor
public Painter(){
buildGUI();
}
private void buildGUI(){
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.setTitle("Paint drawing demonstration");
JPanel headerPanel = new JPanel();
headerPanel.add(new JLabel("The drawing panel is below"));
JButton addNew = new JButton("Add New Graphic");
addNew.addActionListener(new addNewClickHandler());
headerPanel.add(addNew);
frame.add(BorderLayout.NORTH,headerPanel);
frame.add(BorderLayout.SOUTH,this.dp);
frame.pack();
frame.setVisible(true);
}
class DrawingPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.white);
int x, posx, posy, width, height;
for(x=0;x<=10;x++){
//even number differentiation
if(x % 2 == 0){
g.setColor(Color.red);
}else{
g.setColor(Color.blue);
}
Random rand = new Random();
posx = rand.nextInt(300);
posy = rand.nextInt(300);
width = rand.nextInt(40);
height = rand.nextInt(40);
//System.out.println("the ran x pos is: " + posx);
g.fillRect(posx, posy, width, height);
}//end for
}//end paintComponent
public Dimension getPreferredSize() {
return new Dimension(400,400);
}
}// end DrawingPanel
private class addNewClickHandler implements ActionListener{
public void actionPerformed(ActionEvent e){
System.out.print("in addNew_click_handler click handler");//trace debug
AddNewDrawItem newItem = new AddNewDrawItem();
newItem.repaint();
System.out.print("after repaint() in addNew_click_handler click handler");//trace debug
}
}
class AddNewDrawItem extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.white);
int posx, posy, width, height;
Random rand = new Random();
posx = rand.nextInt(300);
posy = rand.nextInt(300);
width = rand.nextInt(40);
height = rand.nextInt(40);
g.setColor(Color.cyan);
g.fillRect(posx, posy, width, height);
}//end paintComponent
}//end AddNewDrawItem
public static void main(String args[]){
new Painter();
}
}//end class Painter
You've got some problems with your code, one of which is that you've got program logic in your paintComponent method: the code randomly changes values that are displayed within this method, meaning your display will change when it repaints, whether you want it to or not. To see that this is so, try to resize your GUI and you'll see some psychedelic changes in the drawn red and blue rectangles.
Now as to your current problem, the solution to it is similar to the solution to the problem I describe above. I suggest...
that you create an ArrayList<Rectangle2D>,
that you create your random rectangles in your class's constructor so that they're created only once, and then place them in the ArrayList above.
that you iterate through this ArrayList in your JPanel's paintComponent method, drawing them as you go. This way paintComponent does nothing but paint which is as it should be,
that you create a MouseAdapter-derived object and add it as a MouseListener and MouseMotionListener to your DrawingPanel
that you use the listener above to create a new Rectangle2D object and when done add it to the ArrayList and call repaint on the DrawingPanel
that you activate the mouse adapter via your button's action listener.
I'll stop there, but I think you get the idea, and if you don't, please ask any questions you may have.

Categories

Resources