Object.drawItself in PaintComponent doesnt work - java

PaintComponent doest paint figures. Just nothing is happening, clean Jframe appear.
I think something is wrong with list or with the way i called method
List is in class with Paint Component
public class Paint extends JPanel implements ActionListener {
List<Figures> figuresList = new ArrayList<Figures>();
Timer t = new Timer(5, this);
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Figures figure : figuresList) {
figure.drawItself(g, figure.getLocationX(), figure.getLocationY());
}
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
{
for (Figures figure : figuresList) {
if (figure.getLocationX() < 0 || figure.getLocationX() > 540) {
figure.setVelocityX(-figure.getVelocityX());
}
if (figure.getLocationY() < 0 || figure.getLocationX() > 220) {
figure.setVelocityY(-figure.getVelocityY());
}
figure.setLocationX(figure.getLocationX()
+ figure.getVelocityX());
figure.setLocationY(figure.getLocationY()
+ figure.getVelocityY());
}
}
repaint();
}
And drawitself:
public class Circle implements Figures {
public int locationX = 12;
public int locationY = 12;
public int velocityX =1;
public int velocityY =1;
public void drawItself(Graphics g, int locationX, int locationY){
this.locationX = locationX;
this.locationY = locationY;
g.drawOval(locationX, locationY, 40, 40);
g.fillOval(locationX, locationY, 40, 40);
}
Main:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint();
p.figuresList.add(c);
p.figuresList.add(q);
GUI.Configuration();
}
GUI
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint();
frame.getContentPane().add(BorderLayout.CENTER, panel);
}

You create and add a Paint instance here:
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint(); // *** new Paint is here, but nothing is added
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
But nothing of use has been added to it. All the important stuff is added to a completely different Paint JPanel, one that is never displayed:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint(); // **** ANOTHER new Paint is here, and it gets goodies
p.figuresList.add(c);
p.figuresList.add(q);
// but is never added to a JFrame and is never displayed.
GUI.Configuration();
}
Don't do this. Create one Paint JPanel, one only, add the important components to it, and then only add that one to the JFrame. Most important, don't just type in code, think and plan your program before committing it to code, and you won't see errors like this.
Also, and again, do not start a Timer from within paintComponent and don't create Circle there. You can draw your Circle instance in paintComponent, but create it and start your Timer within the Paint constructor.

Related

PaintComponent() method gets stuck after second run

I am currently working on a brick breaker game and have run into an issue with the paintComponent method. The first time repaint is called (to invoke paintComponent) everything works fine, but the second time it is called the program gets stuck on the last line of paintComponent.
Below is code and screenshots of the issue I am having.
MyPanel Code:
public class MyPanel extends JPanel {
private static Model model;
public MyPanel(){
setBorder(BorderFactory.createLineBorder(Color.black));
setBackground(Color.BLACK);
}
public void updateModel(Model modelArg){
model = modelArg;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
draw(g2);
}
//Draw each game component
public static void draw(Graphics2D g2){
draw(g2, g, model.getPaddle());
draw(g2, g, model.getBall());
draw(g2, g, model.getBrickArray());
}
//Draw Paddle
public static void draw(Graphics2D g2, Paddle paddle){
Rectangle paddleImage = new Rectangle(paddle.getX(),paddle.getY(), paddle.getWidth(),paddle.getHeight());
g2.setColor(paddle.getColor());
g2.fill(paddleImage);
g2.draw(paddleImage);
}
//Draw Circle
public static void draw(Graphics2D g2, Ball ball){
Double x = Double.valueOf(ball.getX());
Double y = Double.valueOf(ball.getY());
Double height = Double.valueOf(ball.getHeight());
Double width = Double.valueOf(ball.getWidth());
Ellipse2D ballImage = new Ellipse2D.Double(x, y, height, width);
g2.setColor(ball.getColor());
g2.fill(ballImage);
g2.draw(ballImage);
}
//Draw Bricks
public static void draw(Graphics2D g2, Brick[] brickArray){
for(int i = 0; i<brickArray.length; i++){
Brick brick = brickArray[i];
if(!brick.isDestroyed()) {
Rectangle brickImage = new Rectangle(brick.getX(), brick.getY(), brick.getWidth(), brick.getHeight());
g2.setColor(brick.getColor());
//g2.setColor(Color.BLACK);
g2.fill(brickImage);
g2.draw(brickImage);
}
else{
g2.clearRect(brick.getX(), brick.getY(), brick.getWidth(), brick.getHeight());
}
}
}
}
Here is the View class that creates the MyPanel object and calls repaint
public class View extends JFrame {
private JFrame frame;
private static Model model;
private MyPanel panel;
public View() {
model = new Model();
}
//Creates Frame object
//Creates MyPanel object
//Paints MyPanel object
public void createFrame(Model modelArg) {
model = modelArg;
frame = new JFrame();
frame.setSize(500, 550);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Breakout Ball");
frame.getContentPane().setBackground(Color.BLACK);
frame.setResizable(false);
panel = new MyPanel();
panel.updateModel(model);
frame.add(panel);
frame.setVisible(true);
}
public void updateView(Model modelArg) {
model = modelArg;
revalidate();
panel.updateModel(model);
panel.repaint();
}
}
And here is the controller
public class Controller {
private Model model;
public View view;
public Controller(Model model, View view){
this.model = model;
this.view = view;
initView();
int i = 10;
while(i>0){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doCycle();
}});
model.getBrickArray()[0].setDestroyed(true);
i--;
}
}
public void initView(){
model.createGamePieces();
//view.initGamePieces(model.getPaddle());
view.createFrame(model);
}
public void doCycle(){
view.updateView(model);
}
}
Image of debugger when the code gets stuck
If the above image is not showing up, it displays how in the debugger under variables it just says "The application is running" and there are no options to step over, step into etc.
If anyone has any suggestions please let me know!
Edit:
I just cleaned up the code to make it a minimal reproducible example per request. I wasn't sure how much to remove because I'm not exactly sure the scope of what is causing this problem, so I leaned towards removing less code.
Thank you,
Jon

Repainting JPanel

So i'm trying to clear my drawing Panel and I have looked at multiple examples but none of them seem to be working for me? I have a clear button that clears textfields/errors which I got to work perfectly but the Drawing panel still does not clear arraylists or "repaint".
I'm playing around with changing around the size of the oval so please ignore my drawPoints method.
Here is my code:
public class Panel extends JPanel{
ArrayList<Point> pointArray = new ArrayList<>();
ArrayList<Color> colorArray = new ArrayList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
repaint();
//Create the 2D graphics object
Graphics2D myDrawing = (Graphics2D) g;
for (int i = 0; i < pointArray.size(); i++) {
myDrawing.setColor(colorArray.get(i));
myDrawing.fillOval(pointArray.get(i).x,pointArray.get(i).y, 10, 10);
}
}
public void drawPoints(int mouseX, int mouseY, int height, int width){
Point p = new Point(mouseX,mouseY);
pointArray.add(p);
colorArray.add(this.getForeground());
repaint();
}
public void changeColor(){
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
this.setForeground(new Color(red,green,blue));
}
public void mousePressed(MouseEvent event) {
pointArray.clear();
colorArray.clear();
repaint();
}
}
public static void main(String[] args) {
//set the frame
JFrame frame = new JFrame();
frame.setSize(600, 300);
frame.setTitle("Multiple Panels");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create the panel for GUI
JPanel panelGUI = new JPanel();
panelGUI.setBackground(Color.yellow);
//GUIs
//create textfields
JTextField radiusField1 = new JTextField("10", 10);
JTextField radiusField2 = new JTextField("10", 10);
//create buttons
final JButton clearDrawingButton = new JButton("Clear Screen");
final JButton changeColorButton = new JButton("Change Color");
//labels
final JLabel displayLabel = new JLabel("");
//add all GUIs to the GUI panel
panelGUI.add(radiusField1);
panelGUI.add(radiusField2);
panelGUI.add(changeColorButton);
panelGUI.add(clearDrawingButton);
panelGUI.add(displayLabel);
//create the panel to draw
final Panel drawingPanel = new Panel();
drawingPanel.setBackground(Color.white);
//create the initial color
Color drawingColor = new Color(255,0,0);
//set the initial drawing color of the panel
drawingPanel.setForeground(drawingColor);
//add the grid with two columns and two rows to add the three panels
GridLayout grid = new GridLayout(0,2,10,20);
//add the grid to the frame
frame.setLayout(grid);
//add the panels to the frame
frame.add(panelGUI);
frame.add(drawingPanel);
class MouseClickListener implements MouseListener
{
public void mouseClicked(MouseEvent event)
{
int x = event.getX();
int y = event.getY();
System.out.println(x + " " + y);
try {
String text1 = radiusField1.getText();
String text2 = radiusField2.getText();
int height = Integer.parseInt(text1);
int width = Integer.parseInt(text2);
drawingPanel.drawPoints(x, y, height, width);
} catch (NumberFormatException ex) {
displayLabel.setText("Textfields empty! Please enter number.");}
}
// Do­nothing methods
public void mouseReleased(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
if (event.getSource()== changeColorButton){
drawingPanel.changeColor();
}
if(event.getSource()==clearDrawingButton){
radiusField1.setText("10");
radiusField2.setText("10");
displayLabel.setText("");
}
}
}
MouseListener listener1 = new MouseClickListener();
drawingPanel.addMouseListener(listener1);
ActionListener listener = new ButtonListener();
changeColorButton.addActionListener(listener);
clearDrawingButton.addActionListener(listener);
frame.setVisible(true);
}
}
I have a clear button that clears textfields/errors which I got to work perfectly but the Drawing panel still does not clear arraylists or "repaint".
Well look at the code that clears the text fields:
if(event.getSource()==clearDrawingButton){
radiusField1.setText("10");
radiusField2.setText("10");
displayLabel.setText("");
}
Where is the code that clears the ArrayLists?
Add the code to clear the ArrayLists to the ActionListener.
You can also check out Custom Painting Approaches for working code that draws "rectangles". It supports different colors and a "Clear" button.
Also, instead of using a JTextField for the oval size, you might want to consider using a JSpinner. This will allow the user to easily change the numeric value and you don't have to add any special editing to make sure the value entered is a number.
You have this mousePressed method in your main class:
public void mousePressed(MouseEvent event) {
pointArray.clear();
colorArray.clear();
repaint();
}
But it isn't doing anything because your main class does not implement MouseListener and no one is calling this method.
The rest of your code is not very pretty looking. I assume you are doing this as part of a course or otherwise just trying to learn Java Swing in a non-work environment. If this is true, I would recommend that you start over - at least with your MouseListeners, and instead create Actions for your button responses by subclassing AbstractAction and using it with JButton.setAction(myAction); This may seem painful now, but you'll be glad you did it in the future

Cannot repaint new elements without repainting old ones

Here is my problem :
I have made a program that draws squares at random locations, its a tad crude but it works.
However the problem is that it will not repaint properly, I don't know where but somewhere in the code I made a mistake.
This causes the following to happen : I tell the application to draw 5 squares it does so but then when I tell it to draw 6 it will draw the previous 5 + 6.
The code is listed below in two parts RandomSquares and DrawField :
public class RandomSquares extends JPanel {
private static JFrame frame = new JFrame("Random Squares");
private static DrawField f;
private static JButton button = new JButton("Make squares");
private static final JTextField field = new JTextField(10);
private static int amount = 0;
private static void prepareFrame() {
//knoppen
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println(amount);
amount = Integer.parseInt(field.getText());
f = new DrawField(amount);
frame.add(f, BorderLayout.CENTER);
frame.repaint();
}
});
frame.add(button, BorderLayout.NORTH);
frame.add(field, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
Listener l = new Listener();
frame.addKeyListener(l);
frame.setSize(640, 480);
}
public static class Listener implements KeyListener {
#Override
public void keyTyped(KeyEvent ke) {
//unused
}
#Override
public void keyPressed(KeyEvent ke) {
if (ke.getKeyCode() == KeyEvent.VK_R) {
System.out.println("woot!");
}
}
#Override
public void keyReleased(KeyEvent ke) {
//unused
}
}
public static void run() {
f = new DrawField(amount);
prepareFrame();
frame.setVisible(true);
}
}
public class DrawField extends JComponent {
private int amount;
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public DrawField(int amount) {
this.amount = amount;
this.setSize(540, 380);
this.setBackground(Color.GREEN);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Random r = new Random();
for (int i = 0; i < amount; i++) {
g.fillRect(r.nextInt(getWidth()), r.nextInt(getHeight()),
20, 20);
}
}
}
Painting methods should only be used for painting, not setting properties of your class.
g.fillRect(r.nextInt(getWidth()), r.nextInt(getHeight()), 20, 20);
For example if you resize the frame a whole new set of rectangles will be painted at new random locations.
When you invoke super.paintComponent(), the old drawing will be lost and new random rectangle will be created.
Instead your painting code should be based on properties of the class. That is once you create the objects they should be fixed so a repaint of the component does not change the painting, unless you change the properties.
See Custom Painting Approaches for examples of how to painting with a random number of objects by:
keeping a List of objects to paint
add objects to a BufferedImage and then just paint the image.
In your case you would invoke the addRectangles(...) method for the specified number of rectangle you want to paint. Then the painting code will do the rest.
Edit:
To answer your basic question. In general you need to invoke super.paintComponent() to clear the background of a component before doing the custom painting of the component.
The problem is that you are extending JComponent, which doesn't not have any default painting code, so the background doesn't get cleared. Two solutions:
Extend JPanel instead (JPanel does clear the background).
Add custom painting code to your class to clear the background:
Something like:
g.setColor( getBackground() );
f.fillRect(0, 0, getWidth(), getHeight());
// paint rectangle here
I tell the application to draw 5 squares it does so but then when I tell it to draw 6 it will draw the previous 5 + 6.
The Component is not being cleared of any previous painting calls - be sure to always call the parent painting method to clear any previous painting. For example:
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
//custom painting here.
}

Swing paintComponent not working properly

I don't understand why i have to set Component.setPreferredSize() to draw and why my ovals are not placed in one place. Also i got some other questions, which are described below.
public class PaintPanel extends JPanel implements ActionListener
{
private void initStructure()
{
for (int i : new Range(MAX_AGENTS)) {
Agent agent = new Agent();
agents.add(agent);
add(agent);
}
}
//i want to override parent class 'add', to easy call 'actionPerformed' on children elements.
public Component add(Element comp)
{
elements.add(comp);
return super.add(comp);
}
#Override
public void actionPerformed(ActionEvent e)
{
for(Element element: elements){
element.actionPerformed(e);
}
repaint();
}
//ELEMENT is abstract class: 'public class Element extends JComponent implements ActionListener'
public class Agent extends Element
{
public Agent()
{
super();
// setPreferredSize(new Dimension(120,120)); !!!! a-a, i don't know the future size of the oval or triangle, i don't want and i can't set this :(
setVisible(true);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
System.out.println("draw"); //called
Point p1 = new Point(1, 1);
Point p2 = new Point(101, 101);
Point p3 = new Point(10, 10);
int[] xs = { p1.x, p2.x, p3.x };
int[] ys = { p1.y, p2.y, p3.y };
Polygon triangle = new Polygon(xs, ys, xs.length);
g.setColor(new Color(255,255,255)); // !! never painted
g.fillPolygon(triangle); //!! never painted
g.drawOval(10,10,10,10); // !!!! painted only when i set preferredSize
}
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("i am alive"); //working
}
}
In case, if i set preferred size i got the picture below.
So.
Question
1) Is there any patterns to not set preferred size and draw component inside Component calling paint() on contentPanel?
2) Why g.fillPolygon not working?
3) Why my ovals are not placed in one point?
Why my ovals are not placed in one point?
I would guess you are adding your Agent components to a JPanel. By default a JPanel uses a FlowLayout. So each component is placed 120 pixels apart and flow to a new line when the row is filled.
i don't know the future size of the oval or triangle, i don't want and i can't set this
Don't use the drawOval(...) method. Instead use a Shape object that represents an oval. Then you can get the size of the Shape and use this value in the getPreferredSize() method mentioned by #hovercraft.
Check out Playing With Shapes for more info on this concept. Of course if you use this concept you would need to define the Shapes as instance variable so the Shape can be referenced by both the paintComponent() and getpreferredSize() methods.
The issue is if the JPanel doesn't have a preferred size, and if it is being added to a container that uses a layout manager that doesn't fill the container (such as FlowLayout), then how will the GUI know what size the drawing JPanel should be? I've heard that better than calling setPreferredSize(...) on your JPanel is to override its Dimension getPreferredSize() method (ask kleopatra, a Swing expert on this site).
Regarding:
Why g.fillShape not working?
Check the API -- does Graphics have a fillShape method? Nope. But Graphics2D has a fill(Shape s) method, and that's what you want.
Why my ovals are not placed in one point?
Please clarify this and provide details. What do you mean by "placed in one point"? What behavior exactly are you expecting and why?
Edit: your triangle is not being drawn because all the points are co-linear!
For example:
import java.awt.*;
import javax.swing.*;
public class MyDrawingPanel extends JPanel {
private static final int PREF_W = 100;
private static final int PREF_H = PREF_W;
// private Point p1 = new Point(1, 1);
private Point p1 = new Point(30, 1);
private Point p2 = new Point(100, 101);
// private Point p3 = new Point(10, 10);
private Point p3 = new Point(50, 10);
private int[] xs = { p1.x, p2.x, p3.x };
private int[] ys = { p1.y, p2.y, p3.y };
private Polygon triangle = new Polygon(xs, ys, xs.length);
public MyDrawingPanel() {
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(255, 255, 255)); // !! never painted
g.fillPolygon(triangle); // !! never painted
g.drawOval(10, 10, 10, 10); // !!!! painted only when i set preferredSize
}
private static void createAndShowGui() {
int rows = 4;
int cols = 4;
JPanel gridPanel = new JPanel(new GridLayout(rows, cols));
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
gridPanel.add(new MyDrawingPanel());
}
}
JFrame frame = new JFrame("PaintPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gridPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Adding multiple graphic objects to one JFrame

Here is my code it is a guy and a background but only one thing shows up at a time or sometimes nothing at all.
I have a brain class, a frame class, a redPlayer class, and a background class.
The way it works is the brain makes a player and a brain and adds it to the frame.
I think it has something to do with layouts but I tried everything but nothing works.
Please Help!!!
Thanks in advance.
here is the brain:
public class Brain
{
private Frame frame;
private static RedPlayer redPlayer;
private Background background;
private SensorKeys sensor;
public Brain()
{
frame = new Frame();
redPlayer = new RedPlayer();
background = new Background();
sensor = new SensorKeys();
frame.addComponent(redPlayer);
frame.addComponent(background);
frame.addKeySensor(sensor);
redPlayer.revalidate();
}
public static void setRedPlayerVelX(double vx)
{
redPlayer.setVelX(vx);
}
public static void setRedPlayerVelY(double vy)
{
redPlayer.setVelY(vy);
}
public static void makeRedPlayerBullet()
{
}
}
`
here is the frame class
public class Frame
{
private JFrame jf;
public Frame()
{
drawFrame();
}
public void drawFrame()
{
jf = new JFrame();
jf.setSize(800, 600);
jf.setLocation(10, 10);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBackground(Color.WHITE);
jf.setLayout(null);
}
public void addComponent(JComponent jc)
{
jc.setBounds(jc.getX(), jc.getY(), 100, 100);
jf.add(jc);
}
public void addPanel(JPanel jp)
{
jf.add(jp);
}
public void addKeySensor(KeyListener kl)
{
jf.addKeyListener(kl);
}
}
here is the player class:
public class RedPlayer extends JComponent implements ActionListener
{
private int x,y;
private double velX = 0, velY = 0;
private Timer timer = new Timer(2,this);
private Image redplayer;
public RedPlayer()
{
x = 100;
y = 100;
ImageIcon II = new ImageIcon("redPlayerRight.png");
redplayer = II.getImage();
revalidate();
timer.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
graphics.drawImage(redplayer, x,y, null);
}
public void actionPerformed(ActionEvent arg0)
{
x += velX;
y += velY;
repaint();
revalidate();
}
public void setVelX(double vx)
{
velX = vx;
}
public void setVelY(double vy)
{
velY = vy;
}
}
And Lastly here is the background class:
public class Background extends JComponent
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
graphics.setColor(Color.green);
graphics.fillRect(0, 400, 500, 200);
}
}
I think it has something to do with layouts
Yes, you should read the Swing tutorial on Using Layout Managers to better understand how they work and for examples.
The default layout manager for a JFrame is a BorderLayout. You can't just add 3 components to the same area of the BorderLayout.
I don't know what you are trying to accomplish but start simple. Since you have a background the basic code should be something like:
redPlayer = new RedPlayer();
background = new Background();
background.add( redPlayer );
frame.addComponent(background);
So the red player should display on top of the background. And the background is added to the frame.
Of course you must use a proper layout manager for the background. And you must make sure you override the getPreferredSize() method when you do custom painting so the layout manager knows what the size of every component should be.
Get those two components working first, then move on to the 3rd component.

Categories

Resources