Object class isn't painting to screen - java

I am currently working on a small coding project and I've run into an issue. I've looked over my past work and I can't seem to figure out why this program wont call the paint method. Currently I'm just trying to draw a circle to the frame.
The following creates the window and the object class for the simple circle I'm trying to draw.
public class Main {
public static void main(String[] args) {
final int WIDTH = 700, HEIGHT = 900;
JFrame frame = new JFrame("Physics Demo");
JPanel content = new JPanel();
content.setLayout(new GridLayout(1, 0, 0, 0));
Character ball = new Character(WIDTH, HEIGHT);
Timer changeFrame = new Timer (100, ball);
frameSetup(frame, content, WIDTH, HEIGHT, ball, changeFrame);
}
public static void frameSetup(JFrame frame, JPanel content, int WIDTH, int HEIGHT, Character ball, Timer changeFrame){
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(content);
content.add(ball);
frame.addKeyListener(ball);
frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
changeFrame.start();
}
}
The class below is the object class, when I run the program I get a response from the console. Character triggers once (as it should) and the actionPreformed method runs on loop with the timer. For some reason it doesn't run the paint class.
public class Character extends JPanel implements ActionListener, KeyListener{
/* Identify the Objects values and physics,
* Characters weight, size and properties are below.
*
*/
private static final long serialVersionUID = 1L;
final int characterRadius = 30;
final double characterWeight = 0.5;
int characterY, characterX;
boolean bouncy;
public Character(int WIDTH, int HEIGHT){
System.out.println("Character called upon... " + WIDTH);
}
public void characterObject(Graphics g, int WIDTH, int HEIGHT){
super.paint(g);
System.out.println("characterObject graphics called upon... " + WIDTH);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}
/*
* Ball does not have any player interactions
*/
#Override
public void keyPressed(KeyEvent buttonPressed) {
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
//******************************************
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("actionPreformed called upon...");
repaint();
}
}
I've been doing trial and error for a while now and I can't seem to figure it out so I'm using this as a last resort.
I can supply more information if needed.

Why are you calling super.paint from characterObject? This is not how custom painting works. You don't control the painting process, the API does
You need to override one of the methods called when the API want's the component to be repainted. As a general recommendation, this would the paintComponent method, for example
public class Character extends JPanel implements ActionListener, KeyListener {
/* Identify the Objects values and physics,
* Characters weight, size and properties are below.
*
*/
private static final long serialVersionUID = 1L;
final int characterRadius = 30;
final double characterWeight = 0.5;
int characterY, characterX;
boolean bouncy;
public Character(int WIDTH, int HEIGHT) {
System.out.println("Character called upon... " + WIDTH);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
System.out.println("characterObject graphics called upon... " + WIDTH);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}
/*
* Ball does not have any player interactions
*/
#Override
public void keyPressed(KeyEvent buttonPressed) {
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
//******************************************
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("actionPreformed called upon...");
repaint();
}
}
I would recommend having a read of Performing Custom Painting and Painting in Swing for more details about how painting actually works in Swing.
I'd also recommend having a look at How to use Key Bindings as a replacement for KeyListener, which will address you next obvious issue
You may also want to have a read of Java Coding Conventions, it will make it easier for other people to read your code and easier for you to read others.
You're passing the width and height to the Character constructor, but are ignoring them, I'd suggest you're going to need to assign those values to instance fields and use them within the paintComponent method

You should not call paint directly. It is called from the framework whenever repaint is needed. To force repaint just call 'repaint()'.
In case you call it from a timer you might need to put the call into the EDT that means the EventDispatchThread:
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
ball.repaint();
}
});
Oh and you really should override the paint method:
#Override
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}

Related

Paint method not painting Java

My paint method doesnt seem to paint my 20x20 cells. I have a boolean array for the cells to control their state and that if true, call the cells paint method, a cell is painted however I have two problems;
Only one is painted at a time which is odd because i should have a 40x40 array of booleans meaning i have 40x40 cells
They dont actually paint exactly where I click. I do not know how this is the case as when I get the co-ordinates of my click I immediately place those co-ordinates as my x, and y values in my paint method.
Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class mainApplication extends JFrame implements Runnable, MouseListener {
private static final Dimension windowsize = new Dimension(80, 600);
private BufferStrategy strategy;
private Graphics offscreenGraphics;
private static boolean isGraphicsInitialised = false;
private static int rows = 40;
private static int columns = 40;
private static int height = windowsize.height;
private static int width = windowsize.width;
private static Cells cells = new Cells();
private int xArrayElement,yArrayElement, xPosition, yPosition;
private static boolean gameState[][] = new boolean[rows][columns];
public mainApplication() {
System.out.println(System.getProperty("user.dir"));
setDefaultCloseOperation(EXIT_ON_CLOSE);
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - windowsize.width / 2;
int y = screensize.height / 2 - windowsize.height / 2;
setBounds(x, y, screensize.width, screensize.height);
setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenGraphics = strategy.getDrawGraphics();
isGraphicsInitialised = true;
// MouseEvent mouseEvent = new MouseEvent();
addMouseListener(this);
// addMouseMotionListener(MouseEvent);
Thread t = new Thread(this);
t.start();
}
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 1){
xPosition = e.getX();
yPosition = e.getY();
cells.setPosition(xPosition,yPosition);
xArrayElement = (xPosition/20);
yArrayElement = (yPosition/20);
if(gameState[xArrayElement][yArrayElement]){
gameState[xArrayElement][yArrayElement] = false;
}
else if (!gameState[xArrayElement][yArrayElement]) {
gameState[xArrayElement][yArrayElement] = true;
}
else(gameState[xArrayElement][yArrayElement]) = true;
}
}
#Override
public void run() {
while (true) {
try { //threads entry point
Thread.sleep(20); //forces us to catch exception
}
catch (InterruptedException e) {
}
this.repaint();
}
}
public void paint(Graphics g) {
if (isGraphicsInitialised) {
g = strategy.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
if (gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.WHITE);
cells.paint(g);
System.out.println(xPosition);
}
else if (!gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.BLACK);
g.fillRect(xPosition, yPosition, 20, 20);
}
strategy.show();
}
}
public static void main(String[]args){
mainApplication test = new mainApplication();
}
}
Cell Class
import java.awt.*;
public class Cells {
int x;
int y;
public Cells(){
}
public void setPosition(int xi, int xj){
x = xi;
y = xi;
}
public boolean cellState(boolean visible){
return visible;
}
public void paint(Graphics g){
g.drawRect(x, y, 20,20);
}
}
You are doing a number of things wrong. My first suggestion would be to forget about offscreen graphics and ensure you are doing what you want. You can always create an image latter. Here are some basic guidelines:
Don't extend JFrame. Use an instance.
Extend JPanel or create a class that extends JPanel and add to frame instance
Then override paintComponent(g) and use that graphics context to draw.
Here is an earlier answer that may help Can't add Graphics into JPanel in Java
More information may be found in the Java Tutorials on painting.
Updated. It took me a few minutes to find this.
public void setPosition(int xi, int xj){
x = xi;
y = xi; // <--- should be xj
}
Regarding (1) above. You must repaint every cell each time you enter paintComponent. This means you will need to iterate across the list and paint them in the correct spot. Right now you are only painting one upon each entry.
A couple more suggestions. Instead of messing with the thread and calling repaint every 20ms in a loop, why not just invoke repaint in the mouseClicked() method.
If you do eventually need to paint every 20ms. I suggest using a swing Timer as follows: (check JavaDoc to ensure I got the syntax correct!!)
Timer timer = new Timer(0, (ev)-> frame.repaint());
timer.setDelay(20);
timer.start();
And you can create your own mouseListener class and extending MouseAdapter. The purpose of these adapter classes is to keep the clutter down so you don't have to have empty methods to satisfy the interface requirements. Put the class inside your main class so it has access to the appropriate data structures. Then just add an instance of it to the mouse listener of the target Component.

JFrame not displaying anything

I cannot seem to get a rectangle to be displayed in the JFrame. Per the parameters of this project, I have to have this class implement the Icon interface. When I run the code as is, I get the JFrame, but nothing shows up inside. It should display a black square. I'm assuming the problem has something to do with how I am initializing the graphics instance variable. I don't have much experience working with GUI graphics, so I'm not entirely clear on how to do this correctly.
And yes, I do know that the getIconWidth and getIconHeight methods are redundant since I am using constants, but I have to have these methods in order to implement the interface.
public class MugDisplay extends JFrame implements Icon {
private int width;
private int height;
private JPanel panel;
private Graphics graphics;
private static final int ICON_WIDTH = 100;
private static final int ICON_HEIGHT = 100;
public MugDisplay() {
this.configureGui();
this.panel = new JPanel();
this.panel.setLayout(new BorderLayout());
this.add(this.panel, BorderLayout.CENTER);
this.graphics = this.getGraphics();
int xPos = (this.panel.getWidth() - this.getIconWidth()) / 2;
int yPos = (this.panel.getHeight() - this.getIconHeight()) / 2;
this.paintIcon(this.panel, this.graphics, xPos, yPos);
}
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.fillRect(x, y, ICON_WIDTH, ICON_HEIGHT);
}
#Override
public int getIconWidth() {
return ICON_WIDTH;
}
#Override
public int getIconHeight() {
return ICON_HEIGHT;
}
private void configureGui() {
this.setPreferredSize(new Dimension(600, 600));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().setLayout(new BorderLayout());
this.pack();
this.setVisible(true);
}
}
In the interest of having a MCVE, here is the driver class that calls this class.
public class Main {
public static void main(String[] args) {
MugDisplay md = new MugDisplay();
md.setVisible(true);
}
}
this.graphics = this.getGraphics() is not how custom painting works in Swing. What you should do is create a panel, override its paintComponent method, make the call to super, and then do your painting. For instance:
panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int xPos = ...
paintIcon(this, g, xPos, yPos);
}
}
Calling getGraphics will provide you with a short-lived Graphics object that can soon become invalid, which is why you should opt for overriding paintComponent instead, which will always give you a usable Graphics object. See Performing Custom Painting.
On a separate note, it looks like you're calling setVisible(true) before you're finished adding the necessary components to the JFrame. To ensure that your components show up, call setVisible(true) after adding them all to the frame.

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.
}

why need call two repaints if only paint specific area?

I cannot figure out why I need to call repaint two times as in the below program. I expected that the first repaint will keep the rectangle as it was when I click in another position, and so now there are two rectangles in the window. But it actually removes the first rectangle. Can someone explain this?
class MyPanel extends JPanel {
private int squareX = 50;
private int squareY = 50;
private int squareW = 20;
private int squareH = 20;
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
}
private void moveSquare(int x, int y) {
int OFFSET = 0;
if ((squareX!=x) || (squareY!=y)) {
//repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
repaint(squareX,squareY,squareW,squareH);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
//repaint(squareX,squareY,squareW,squareH);
}
}
public Dimension getPreferredSize() {
return new Dimension(250,200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLUE);
g.drawRect(squareX,squareY,squareW,squareH);
}
}
It's all about timing. You are scheduling two different repaint requests, one for the area which the rectangle use to occupy and one for the area that the rectangle now occupies.
The repaint requests are pushed to the Event Queue (via the RepaintManager), which means that they occur some time in the future and are not immediately handled (within the method that is calling them).
This ensures that you are "erasing" the area where the rectangle "use" to be before you paint the area where the rectangle now is.
By using repaint(int, int, int, int) you reduce the amount of area which needs to be painted and can make the paint process more efficient.
Take a look at Painting in AWT and Swing for more details

Painted content invisible while resizing in Java

Please note I haven't tested this on a Windows-machine only on a Mac-machine. I'm not so sure whether this also occurs on a Windows-machine...
When I resize my Java-application the content is invisible. I already found a way to fix it after resizing it, but not while the user is resizing the window.
I'm not using Swing or something because it makes my binary so slow (in my opinion).
The structure is like this:
Frame My main-window
Container Content view of main-window
Container-based subviews that including the paint(Graphics g)-method
I've added all listeners to My main-window and now I'm able to redraw the Content-view after resizing the window.
public void componentResized(ComponentEvent e) {
this.contentView.paint(this.contentView.getGraphics());
}
I am beware of the fact using the paint(getGraphics())-method isn't a really good way to do this, but since the repaint()-method doesn't do anything at all, it's the only working possibility.
While resizing, all painted content becomes invisible. However, when I add a Button-instance to my Content-view and resize my Main-window, the button doesn't get invisible.
I am able to trace the 'live'-resize event:
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
}
When I start resizing this method is not being called.
While resizing it generates "Live-resize" in my log every single pixel I resize the window.
When I stop resizing this method is not being called, the componentResized-method does.
When I add my repaint-method (or the official repaint-method) to the 'live'-resize event like this, I still get the output, however, it's not repainting or something
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
this.contentView.paint(this.contentView.getGraphics());
}
Or
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
this.contentView.repaint();
}
When I minimize my application to the dock and maximize the application again, the same thing happens, I guess that the same code is needed to fix this.
I'm not using Graphics2D or something, just Graphics.
Could you please explain me how I can repaint the views?
Thanks in advance,
Tim
For reference, here is the same program using Swing. Because JPanel is double buffered, it doesn't flicker as the mouse is released after resizing.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class SwingPaint {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new CirclePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static class CirclePanel extends JPanel {
private static final Random r = new Random();
public CirclePanel() {
this.setPreferredSize(new Dimension(320, 240));
this.setForeground(new Color(r.nextInt()));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
CirclePanel.this.update();
}
});
}
public void update() {
this.setForeground(new Color(r.nextInt()));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = this.getSize();
int d = Math.min(size.width, size.height) - 10;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
}
}
I'm more familiar with Swing, but the article Painting in AWT and Swing distinguishes between system- and application-triggered painting. The example below shows how the system invokes paint() as the window is resized, while the application invokes repaint(), which calls update(), in response to a mouse event. The behavior is cross-platform.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class AWTPaint {
public static void main(String[] args) {
Frame frame = new Frame();
frame.add(new CirclePanel());
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
private static class CirclePanel extends Panel {
private static final Random r = new Random();
public CirclePanel() {
this.setPreferredSize(new Dimension(320, 240));
this.setForeground(new Color(r.nextInt()));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
CirclePanel.this.repaint();
}
});
}
#Override
public void update(Graphics g) {
this.setForeground(new Color(r.nextInt()));
}
#Override
public void paint(Graphics g) {
Dimension size = this.getSize();
int d = Math.min(size.width, size.height) - 10;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
}
}
Okay, I finally fixed it.
Instead of redrawing it every time in the paint(Graphics g)-method, you need to buffer the output and only redraw that image (I kinda hoped Java would be already doing that, just like Obj-C).
public BufferedImage buffer;
public void redraw() {
buffer = new BufferedImage(
200, // height
300, // width
BufferedImage.TYPE_4BYTE_ABGR); // ABGR = RGBA, 4-byte (r, g, b, a) per pixel
Graphics g = buffer.getGraphics();
// do your drawing here
if (this.getGraphics()) {
// 'this' is already shown, so it needs a redraw
this.paint(this.getGraphics()); // little hack
}
}
public void update(Graphics g) {
this.paint(g);
}
public void paint(Graphics g) {
g.drawImage(buffer, 0, 0, this);
}
Now, when you minimize the window and maximize it again, the paintings remain. Only, the window's flickering now for .1-second or so, but I don't really care about that.

Categories

Resources