JPane "paintComponent" can't be called without JLabel - java

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.

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?

Java Won't draw paintComponent from another class

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());
}
}

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);

JPanels don't appear in the JFame

I'm new to java. I do have a java class but I want to get ahead. I really like it! This is my problem. I'm trying to draw the two paddles needed for the game. I did create 2 objects for them, and they both "Show" but the following happens:
Main Runner
import javax.swing.JFrame;
public class PongRunner {
public static void main (String[] args)
{
new PongRunner();
}
public PongRunner()
{
JFrame PongFrame= new JFrame();
PongFrame.setSize(800,600);
PongFrame.add(new PaddleB());
PongFrame.add(new PaddleA());
PongFrame.setLocationRelativeTo(null);
PongFrame.setTitle("My Pong Game");
PongFrame.setResizable(false);
PongFrame.setVisible(true);
PongFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
PaddleA and PaddleB are both drawRect Graphics and draw a specific rectangle.
The graphics that I tell JFrame to add first are the only ones visible. The one under it doesnt get added into the Frame I wanted to know why,and how i can draw both paddles in the same JFrame... I'm reading my book and looking over the internet as much as possible, but no luck within these 2 days. Some help would be nice. I just started learning java and I think I'm making progress. Some tips would be nice too thanks :), specialy on actionListener since im going to have to use them to move the paddles!
SOURCE CODE OF BOTH PADDLES:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class PaddleA extends JPanel implements ActionListener
{
private Timer timer;
public void timer()
{
timer = new Timer(25, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(70,200,20,100);
}
}
PaddleB:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class PaddleB extends JPanel implements ActionListener
{
private Timer timer;
public void timer()
{
timer = new Timer(25, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(500, 200, 20, 100);
}
}
As you've added the 2 paddles into the same BorderLayout.CENTER location of the main PongFrame, only the second paddle will be displayed.
This type of painting is more easily done using one Graphics object on one JComponent.
Swing components aren't really designed to be used like this. Each of your paddle JPanels is trying to paint a rectangle, but the container (the JFrame) and its layout manager will control the size and location of the JPanels. So the rectangle they paint may be outside their bounds, and won't appear at all.
Some Layout Managers will only show one of the JPanels if you add them one after another like this - see answer by Reimus.
To do animated graphics like this, you probably need to start with a single JComponent (e.g. a JPanel) that fills the JFrame, and paint both paddles as rectangles onto it.
This is because you add the first paddle and then add the second paddle over the first thus replacing them:
PongFrame.add(new PaddleB());
PongFrame.add(new PaddleA());
try using LayoutManagers here is just a quick sample:
PongFrame.getContentPane().add(new PaddleB(),BorderLayout.WEST);
PongFrame.getContentPane().add(new PaddleA(),BorderLayout.EAST);
also your g.fillRect(...); has x and y co-ordinates that are too large for the JFrame and thus are not displayed. Change it like so:
g.fillRect(0,0, 20, 100);
Not related though:
dont use JFrame.add() rather JFrame.getContentPane().add()
UPADTE:
As others have mentioned the logic for the game is a bit off. You should have a single drawing panel which is added to the JFrame and all drawing is done on the single JPanel using Graphics and not JPanels. Here is a small example:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PongRunner {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new PongRunner();
}
});
}
public PongRunner() {
JFrame PongFrame = new JFrame();
PongFrame.setTitle("My Pong Game");
PongFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PongFrame.setResizable(false);
PongFrame.setSize(400,400);
//PongFrame.setLocationRelativeTo(null);
PongFrame.getContentPane().add(new DrawingPanel());
PongFrame.setVisible(true);
}
}
class DrawingPanel extends JPanel {
Rect rect1, rect2;
public DrawingPanel() {
super(true);
rect1 = new Rect(80, 80, 20, 100, Color.yellow);
rect2 = new Rect(100, 200, 20, 100, Color.red);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(rect1.getColor());
g.fillRect(rect1.getX(), rect1.getY(), rect1.getWidth(), rect1.getHeight());
g.setColor(rect2.getColor());
g.fillRect(rect2.getX(), rect2.getY(), rect2.getWidth(), rect2.getHeight());
}
}
class Rect {
private Color color;
private int x, y, height, width;
Rect(int x, int y, int width, int height, Color color) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
this.color = color;
}
public Color getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
}
Please note this snippet is not perfect and merely demonstrates the stereotypical logic needed for drawing rectangles to form a game of sorts
I don't have time to try running your code, but try setting the size and position of the "paddle" objects, or set them as non-opaque. I believe that Swing tries to speed up rendering by not drawing components which are completely covered by another opaque component. It may not be drawing one of your paddles for that reason.
If you do set the size and position of your paddles, you may have to modify your paintComponent methods: if my memory is correct, I think the Graphics object which is passed in is clipped to the area of the component, so 0,0 would be the top-left corner of the component.
you shouldn't add your JPanels to your JFrame by the JFrame#add(Component) method. Better add them to the contentPane: frame.getContentPane().add(panel); You should although read about LayoutManagers. They can help you by positining Components in your Frame or mess things up if you use them incorrect.

JFrame Menu's being drawn over and Graphics disappearing upon JFrame Resize

I am a high school student, and I am working on my final project for my computer science class. My assignment was to make a simple paint application, however I am having some problems. These include: the painted image being erased when the window is resized and that I can draw over the menu's in my window.
I believe I am drawing over the menu's because I am using the graphics object of the JFrame itself. However I can not find any alternatives. I have tried making separate components to draw in, and even using BufferedImage, but none of my attempts have been successful.
Here is my code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.*;
public class Paint implements MouseMotionListener{
private JFrame window;
private drawingComponent pComp;
private int xPos; //xPos of mouse;
private int yPos; //yPos of mouse;
private Color color; // new color
private JTextArea sizeBox;
private int brushShape = 0;
public static void main(String[] args) {
Paint paint = new Paint();
paint.ImplementGUI();
}
public Paint() {
this.pComp = new drawingComponent();
this.window = new JFrame("JPaint");
this.window.setSize(500, 500);
this.window.setVisible(true);
this.window.addMouseMotionListener(this);
this.window.add(this.pComp);
}
public void ImplementGUI() {
JMenuBar menu = new JMenuBar();
JMenu brush = new JMenu("Brush Settings");
JMenu brushShape = new JMenu("Shape");
brush.setSize(100,100);
brush.add(brushShape);
menu.add(brush);
menu.setSize(window.getWidth(), 20);
menu.setVisible(true);
this.sizeBox = new JTextArea("Brush Size:");
this.sizeBox.setText("20");
brush.add(this.sizeBox);
this.window.setJMenuBar(menu);
}
public void mouseDragged(MouseEvent pMouse) {
if (pMouse.isShiftDown() == true) {
this.pComp.paint(this.window.getGraphics(), pMouse.getX(), pMouse.getY(),
window.getBackground(), Integer.parseInt(sizeBox.getText()));
}
else {
this.pComp.paint(this.window.getGraphics(), pMouse.getX(), pMouse.getY(),
color, Integer.parseInt(sizeBox.getText()));
}
}
public void mouseMoved(MouseEvent pMouse) {
}
}
class drawingComponent extends JComponent {
public void paint(Graphics g, int x, int y, Color color, int size) {
g.setColor(color);
g.fillOval(x-(size/2), y-(size/2), size, size); // 10 is subtracted from
// coordinates so the mouse pointer is at the exact middle of the brush.
}
}
How can I fix this problem?
Never use the getGraphics() method to do painting. The effect is only temporary as you have noticed.
Custom painting is done by overriding the paintComponent() method, not the paint() method.
See Custom Painting Approaches for a couple of example of how to paint multiple objects on a panel.
camickr is right. When you resize the window, the ImplementGUI component is repainted, so ImplementGUI.paintComponent() will be invoked to repaint the whole surface of the component, and what you did paint within mouseDragged() will be lost.

Categories

Resources