Panel.repaint(); messes up layout - java

JPanel Initiation
p = new JPanel() {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) {
if(errors == 1)
g.drawOval(215, 50, 75, 75);
else if(errors == 2)
g.drawOval(200,200,200,200);
}
};
Method that calls repaint
static void drawHead() {
System.out.println("Head");
errors = 1;
p.removeAll();
p.revalidate();
p.repaint();
}
Before repaint my frame looks like this, http://i.imgur.com/XQlQeul.png
And afterwards it looks like this, http://i.imgur.com/RnVuUzt.png
I'm thinking there is an error in my drawHead() method but I cannot seem to resolve the issue. Does anyone know what is going on? My desired outcome would be the first image, but with a head drawn in.

You've broken the paint chain by failing to call super.paintComponent first before you performed any custom painting
Graphics is shared resource, every component painted in a during a paint cycle will share the same Graphics context, this means that whatever was previously painted to the Graphics context will remain unless you clear it.
One of the jobs of paintComponent is to prepare the Graphics context for painting by filling it with the background color of the component

Related

How to repaint in Swing without clearing the window? [duplicate]

This question already has answers here:
Repaint without clearing
(2 answers)
Closed 3 years ago.
I am implementing a simple Canvas where items can be drawn like a person would in real life with a paper and a pencil, without clearing the entire page every time an object is drawn.
What I have so far...
A Canvas to implement the drawing:
public class Canvas extends JPanel {
private final Random random = new Random();
public Canvas() {
setOpaque(false); // I thought setting this flag makes the drawn pixels be preserved...
}
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(random.nextInt(getWidth()), random.nextInt(getHeight()), 5, 5);
}
}
The Window as an actual window:
public class Window extends JFrame {
public Window(Canvas canvas) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(canvas);
pack();
setVisible(true);
}
}
And the Controller with an entry-point to the application. Also starts a timer so the repaint on Canvas is called every second to force drawing another circle.
public class Controller {
public static void main(String[] args) {
Canvas canvas = new Canvas();
SwingUtilities.invokeLater(() -> new Window(canvas));
new Timer(1000, e -> canvas.repaint()).start();
}
}
The problem is that whenever a new circle is drawn, the previous one is cleared. Seems like there is still some process filling the JPanel or maybe the entire JFrame with white color.
Painting in Swing is destructive. It is an expected requirement that each time a component is painted, it is painted from scratch, again.
You need to define a model which maintains the information needed in order to restore the state from scratch.
Your paint routines would then iterate this model and draw the elements each time.
This has the benefit of allowing you to modify the model, removing or inserting elements, which would allow you to update what is been painted simply.
Alternatively, you could use a "buffer" (ie a BufferedImage) on to which all you painting is done, you would then simply paint the image to the component each time the component is painted.
This, however, means that you can't undo or layer the paintings, it's drawn directly to the image. It also makes resizing the drawing image area more difficult, as you need to make these updates manually, where as the "model" based implementation is far more adaptable
Consider calling the alternate constructor of repaint(...)
repaint(long tm, int x, int y, int width, int height)
This allows you to set a specified area to be repainted.
Also you can just store what you drew in a list and then reprint the drawing to the canvas after repaint is called.

Why do JFrame's update, revalidate, and repaint not update the window?

I am trying to create a window frame to display a game window. I extended JFrame in my GameWindow class and created two methods: drawBackground, which fills the screen with a solid rectangle, and drawGrid, which draws successive lines using a for-loop to make a grid. Here is my code.
public class GameWindow extends JFrame {
// instance variables, etc.
public GameWindow(int width, Color bgColor) {
super();
// ...
this.setVisible(true);
}
public void drawBackground() {
Graphics g = this.getGraphics();
g.setColor(bgColor);
g.fillRect(0, 0, this.getWidth(), this.getWidth());
// I suspect that the problem is here...
this.update(g);
this.revalidate();
this.repaint();
g.dispose();
}
public void drawGrid() {
Graphics g = this.getGraphics();
g.setColor(Color.BLACK);
for (int i = tileWidth; i < TILE_COUNT * tileWidth; i += tileWidth) {
g.drawLine(0, i * tileWidth, this.getWidth(), i * tileWidth);
g.drawLine(i * tileWidth, 0, i * tileWidth, this.getHeight());
}
// ... and here.
this.update(g);
this.revalidate();
this.repaint();
g.dispose();
}
}
However, when I try to test this class in a program like this:
public class Main {
public static void main(String[] args) {
GameWindow game = new GameWindow(700);
game.drawBackground();
game.drawGrid();
}
}
the frame appears on screen but remains blank; neither the background nor the grid is being drawn. I tried Graphics g = this.getGraphics() to this.getContentPane().getGraphics(). I also tried using many different combinations and orders in drawBackground and drawGrid of revalidate, update, etc. None of these attempts seemed to work. How can I fix this issue?
Well, Graphics g = this.getGraphics(); would be a good place to start. Since repaint just schedules a paint pass to occur with the RepaintManager, all the code using getGraphics will simply be ignored.
This is not how custom painting works. getGraphics can return null and is, at best, a snapshot of the last paint cycle, anything you paint to it will be wiped clean on the next paint cycle.
Also, don't dispose of a Graphics context you did not create, on some systems this will stop other components from been able to use it
Start by taking a look at Performing Custom Painting and Painting in AWT and Swing for a better understanding into how painting works and how you should work with it.
You're also, likely, going to want to have a read through Concurrency in Swing and How to Use Swing Timers for ideas on creating a "main loop" to update the UI at constant rate, as Swing is single threaded AND not thread safe

How can I remove all lines while Drawing in jPanel in java?

I'm trying to make a Paint program using java , I have three events in the jPanel to draw my line.
my problem is that when I am drawing the new line , the first one removed (I think the problem in the dragged event!) .. and so on.
Note that while the mouse is dragged the line will be stucked to the mouse
here is my events code:
private void jPanel1MousePressed(java.awt.event.MouseEvent evt) {
g1=(Graphics2D) jPanel1.getGraphics();
p1=jPanel1.getMousePosition();
}
JLayer lpane;
private void jPanel1MouseDragged(java.awt.event.MouseEvent evt) {
if(p1!=null){
lpane = new JLayer();
jPanel1.add(lpane, BorderLayout.CENTER);
lpane.setBounds(0, 0, 328, 257);
g2=(Graphics2D) lpane.getGraphics();
l=new Line(p1.x,p1.y,jPanel1.getMousePosition().x,jPanel1.getMousePosition().y);
l.draw(g2);
//lpane.repaint();
lpane.setVisible(false);
lpane.removeAll();
lpane.disable(); jPanel1.remove(lpane);
}
}
private void jPanel1MouseReleased(java.awt.event.MouseEvent evt) {
if(p1!=null)
{
g1=(Graphics2D) jPanel1.getGraphics();
p2=jPanel1.getMousePosition();
l=new Line(p1.x,p1.y,p2.x,p2.y);
g1.setColor(Color.red);
l.draw(g1);
p1=null;
}
}
Graphics2D g1,g2; Point p1=null,p2=null; Line l;
getGraphics is not how painting should be done in Swing, instead override the panels paintComponent and paint your components state there.
The paintComponent method needs to know what to paint whenever it is called, as it may be called any number of times, many times without your interaction or knowledge.
One approach is to build a List of shapes or Points, which can then be looped through and painted each time paintComponent is called. The benefit of this is you can remove these shapes/points should you wish.
See Pinting in AWT and Swing and Performing Custom Painting for more detals
Also take a look at this example for an idea
The usual way of doing this is to create a (Buffered)Image the size of your Component, fill the background color, and then draw each new line on the Image as well. In your paintComponent method, all you call is g.drawImage(...);
In your panel:
public void paintComponent(Graphics g) {
if (mSizeChanged) {
handleResize();
}
g.drawImage(mImg, 0, 0, null);
}
In your MouseMotionListener:
public void mouseDragged(MouseEvent me) {
Graphics g = mImg.getGraphics();
Point p = me.getPoint();
g.drawLine(mLastPoint.x, mLastPoint.y, p.x, p.y); }

Calling the paint() method from another class?

I am trying to call the paint() method from another class, but it just doesn't work.
Here is the code:
Main.java:
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
private int WIDTH = 600;
private int HEIGHT = 400;
private String NAME = "Dark Ages";
private String VERSION = "0.0.1 Pre-Alpha";
static boolean running = false;
private Image dbImage;
private Graphics dbg;
public Main() {
//Initialize window
JFrame frame = new JFrame();
frame.setTitle(NAME + " - " + VERSION);
frame.setSize(WIDTH, HEIGHT);
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
//Running
running = true;
}
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
// Draw Images
repaint();
}
public static void main(String args[]) {
new Main();
Player player = new Player();
}
}
Player.java:
public class Player {
public void paint(Graphics g) {
g.drawRect(100, 100, 100, 100);
}
}
How do I call the paint() method from another class in java?
In brief, you don't. And in fact, you shouldn't call it directly from the same class either. Instead you should change the state of the class (change its fields), and then call repaint() on it, which will suggest to the JVM that the component should be painted.
Other issues and suggestions:
An obvious issue I see immediately in your code is that your class has a Graphics field, something that is dangerous to do. I suggest that you get rid of that field so that you're not tempted to use it.
Do not draw directly within a JFrame, JDialog or other top-level window. These windows have too many roles to play behind the scenes that you don't really want to mess with how they render themselves or their children (unless you really know what you're doing and have definite need).
Better to draw in the paintComponent(...) method override of a JPanel or other JComponents.
In your paintComponent(...) override, don't forget to call the super's method: super.paintComponent(g);. This will allow Swing to do housekeeping drawing, such as erasing old out of date or deleted images, before you do your drawing.
Run, don't walk to your nearest Swing painting tutorial.
Your current Player class extends no Swing component and is added to no top level window, so its code will do nothing useful. Again, read the tutorials.
Never call repaint() from within paint(...) or paintComponent(...).
Please post modified code if possible.: please don't ask us to create code for you as that's your job. Please understand that the more code you create, the better a coder you'll be, and because of this, most of us don't feel that we should cheat you out of the opportunity to create your own code.
Useful Java Tutorials:
The Really Big Index: The main tutorial where you should start.
Using Swing Components: How to create Swing GUI's
Lesson: Performing Custom Painting: Introductory tutorial to Swing graphics
Painting in AWT and Swing: Advanced tutorial on Swing graphics

Java Paint Problem

Ok I'm wondering why the code below will not display the JLabel.
MyPanel is getting added correctly to a JFrame and everything because it all displays but will not draw the JLabel. Any help is appreciated.
public class MyPanel extends JPanel {
private Root root;
...
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
root.paint(g2);
}
}
class Root {
private Node1 node1;
...
public void paint(Graphics g) {
node1.paint(g);
}
}
class Node1 {
...
public void paint(Graphics g) {
JLabel jtp = new JLabel();
jtp.setLocation((int) x, (int) y);
jtp.setSize((int) width, (int) height);
jtp.setLocation(40, 40);
jtp.setSize(40, 40);
jtp.setText("Hello world");
//jtp.setVisible(true);
jtp.paint(g);
}
}
I suggest that you don't add Components to a Container in a paint method as 1) you do not have absolute control when or even if a paint method will be called and 2) paint and paintComponent have to be as blazing fast as possible, and this is not the time or place to update a GUI. 3) Since paint is often called many times, you will be adding components many times to your container, and all out of your direct control.
Also, while you're adding a component into Root (whatever Root is since it doesn't extend JComponent, JPanel, or similar) in the paint method, the Root object is never added to anything else that I can tell, and so it makes sense that nothing "added" to a component that is not added eventually to a top-level window will be visible.
Bottom line: I think you need a gui re-design as your solution. If you tell us more about it we can help you with it. Next we'll need to talk about use of layout managers and why setting absolute position and sizes of components is usually frowned on.
If anything I say is confusing, please ask for clarification, or if anything is wrong, please help me correct it!
You should not create your JLabel inside the paint method - instead, create it once when initializing your MyPanel. Your label is kind of a renderer component for your nodes, which in principle is a good thing. You may look how the renderers for JTable, JList, JTree work.
In your case, don't set the location of your label (it does not change anything, since it's paint-method expects its graphics object to be oriented by its own upper left corner), instead translate the Graphics-context:
Graphics copy = g.create((int)x, (int)y, (int)width, (int)height);
jtp.paint(copy);
(Graphics2D has some more fancy methods for shifting, rotating, scaling the context, too.)
Other than this, I don't see any problems. Make sure your Node1.paint() method gets actually called by putting some System.out.println() in there.

Categories