Window's content disappears when minimized
Asked Answered
Z

4

6

I have a simple class that draws a line when mouse dragging or a dot when mouse pressing(releasing).

When I minimize the application and then restore it, the content of the window disappears except the last dot (pixel). I understand that the method super.paint(g) repaints the background every time the window changes, but the result seems to be the same whether I use it or not. The difference between the two of them is that when I don't use it there's more than a pixel painted on the window, but not all my painting. How can I fix this?

Here is the class.

package painting;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;

class CustomCanvas extends Canvas{   
    Point oldLocation= new Point(10, 10);
    Point location= new Point(10, 10);
    Dimension dimension = new Dimension(2, 2);     
    CustomCanvas(Dimension dimension){  
        this.dimension = dimension;   
        this.init();
        addListeners();
    }    
    private void init(){                     
        oldLocation= new Point(0, 0);
        location= new Point(0, 0);
    }
    public void paintLine(){
        if ((location.x!=oldLocation.x) || (location.y!=oldLocation.y)) {         
            repaint(location.x,location.y,1,1);                                   
        } 
    }
    private void addListeners(){
        addMouseListener(new MouseAdapter(){
            @Override
            public void mousePressed(MouseEvent me){                   
                oldLocation = location;
                location = new Point(me.getX(), me.getY());
                paintLine();
            }
            @Override
            public void mouseReleased(MouseEvent me){                
                oldLocation = location;
                location = new Point(me.getX(), me.getY());
                paintLine();
            }
        });
        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent me){                
                oldLocation = location;
                location = new Point(me.getX(), me.getY());
                paintLine();
            }
        });
    }
    @Override
    public void paint(Graphics g){  
        super.paint(g);
        g.setColor(Color.red);       
        g.drawLine(location.x, location.y, oldLocation.x, oldLocation.y);                
    }
    @Override
    public Dimension getMinimumSize() {
        return dimension; 
    }
    @Override
    public Dimension getPreferredSize() {
        return dimension;
    }

}
class CustomFrame extends JPanel {
    JPanel displayPanel = new JPanel(new BorderLayout());
    CustomCanvas canvas = new CustomCanvas(new Dimension(200, 200));        
    public CustomFrame(String titlu) {            
        canvas.setBackground(Color.white);
        displayPanel.add(canvas, BorderLayout.CENTER);            
        this.add(displayPanel);
    }   
}
public class CustomCanvasFrame {
    public static void main(String args[]) {
        CustomFrame panel = new CustomFrame("Test Paint");
        JFrame f = new JFrame();
        f.add(panel);
        f.pack();
        SwingConsole.run(f, 700, 700);
    }
}
Zebulun answered 11/4, 2012 at 16:54 Comment(4)
Don't mix Swing & AWT components together! If it is not causing problem right now, it will when you add menus, tool-tips, combos or other floating elements. How many drawn elements will be typical for this use-case? Will they ever be removed? If the answer to the 1st is 'thousands' and the 2nd is 'no', a single BufferedImage (displayed in a label) might be a better option. A mouse listener can be added to the label.Pitt
Re Swing/AWT. Specifically it is the Canvas that I am referring to. For a Swing equivalent, extend either a JComponent or JPanel then override paintComponent() instead of paint(). BTW - rendering to an image means not having to extend anything, & while the java.awt.Label does not support images, the javax.swing.JLabel does. ;)Pitt
Yes, I've been looking over the example found here and I'm planning to use a buffered image since I need also to save my images, but for the moment I am trying to understand how paint and update work. For example, in my code(which I will post as soon as I found out how and where), I don't understand why calling the method paint in update isn't the same as writing the exactly same code(from the paint method) in the update method.Zebulun
"Yes, I've been looking over the example found here" Ah yes. That was fun code. Thanks for reminding me. :) "..for the moment I am trying to understand how paint and update work." Sorry I'm a bit hazy on that. When I did the tutorial on custom painting long ago, the thing I carried away from it was that there were a lot of methods that were not intended to be directly called, the right place to do custom painting was in the paint()/paintComponent() method & the right time to paint was "when requested to do so".Pitt
D
5

You are not storing the state of the points you are drawing. When the panel is repainted, it only has information for the last point it drew.


Response to comment:

You would need to have a collection of Points, for instance ArrayList<Point> location = new ArrayList<Point>();

Then, in your listeners: location.add(new Point(me.getX(), me.getY()));

Finally, in paintLine():

for (Point location : locations) {
  repaint(location.x,location.y,1,1); 
}

The collection locations is usually referred to as a Display List. Most graphics programs use them.


Response to comment:

Yes, I expect so. I just tossed off an idea based on your code to give you a starting point. It is almost certainly a bad idea to do exactly as I have described.

Danziger answered 11/4, 2012 at 17:13 Comment(3)
Doesn't that mean I will draw all the points(instead of one) everytime I press or drag the mouse?Zebulun
Yes, thanks for the idea. It's been very helpful. I managed to make some improvements to my code and now I can draw points and lines and my image doesn't disappear anymore. However, there's another issue I don't get. If I replace the code from my update method with "paint(g)", my image flashes everytime I try to paint on it. Can you explain me why? (Uhm, I'm not sure how to post my code in here. )Zebulun
I think you might already know more about this than I do. GUI work is not usually my thing.Danziger
H
5

Doesn't that mean I will draw all the points(instead of one) everytime I press or drag the mouse?

Yes, but @Dave's approach is perfectly satisfactory for thousands of nodes, as may be seen in GraphPanel. Beyond that, consider the flyweight pattern, as used by JTable renderers and illustrated here.

Addendum: Focusing on your AWTPainting questions, the variation below may illustrate the difference between System- and App-triggered Painting. As the mouse is dragged, repaint() invokes update(), which calls paint(); this is app-triggered. As you resize the window, only paint() is called (no red numbers are drawn); this is system-triggered. Note that there is a flicker when the mouse is released after resizing.

Flickering typically occurs when the entire component's background is cleared and redrawn:

 4. If the component did not override update(), the default implementation of update() clears the component's background (if it's not a lightweight component) and simply calls paint().

AWTPainting

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Panel;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;

public class AWTPainting {

    public static void main(String args[]) {
        CustomPanel panel = new CustomPanel();
        Frame f = new Frame();
        f.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        f.add(panel);
        f.pack();
        f.setVisible(true);
    }
}

class CustomPanel extends Panel {

    public CustomPanel() {
        this.add(new CustomCanvas(new Dimension(320, 240)));
    }
}

class CustomCanvas extends Canvas {

    private MouseAdapter handler = new MouseHandler();
    private List<Point> locations = new ArrayList<Point>();
    private Point sentinel = new Point();
    private Dimension dimension;

    CustomCanvas(Dimension dimension) {
        this.dimension = dimension;
        this.setBackground(Color.white);
        this.addMouseListener(handler);
        this.addMouseMotionListener(handler);
        this.locations.add(sentinel);
    }

    @Override
    public void paint(Graphics g) {
        g.setColor(Color.blue);
        Point p1 = locations.get(0);
        for (Point p2 : locations.subList(1, locations.size())) {
            g.drawLine(p1.x, p1.y, p2.x, p2.y);
            p1 = p2;
        }
    }

    @Override
    public void update(Graphics g) {
        paint(g);
        g.clearRect(0, getHeight() - 24, 50, 20); // to background
        g.setColor(Color.red);
        g.drawString(String.valueOf(locations.size()), 8, getHeight() - 8);
    }

    private class MouseHandler extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            if (locations.get(0) == sentinel) { // reference identity
                locations.set(0, new Point(e.getX(), e.getY()));
            }
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            locations.add(new Point(e.getX(), e.getY()));
            repaint();
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return dimension;
    }
}
Hoyt answered 11/4, 2012 at 18:58 Comment(4)
Ok, I have to know: what the hell is this public class ![AWTPainting][7] {...?Danziger
Um, careless editing? I wondered where that first image went. :-) I have little AWT experience, so I'd welcome you critical review.Hoyt
I'm in the same boat with regards to AWT/Swing; I can get by, but I'm not really an experienced user. I asked about your typo because, years ago, I had experience in Java 1.2, but then ended up living face-down in a moon crater for quite some time. When I got back, Java had generics and other stuff that took me by surprise. I was concerned that maybe this had happened again and Oracle had done something strange to Java while I was looking the other way.Danziger
Ah, Swing enables double buffering by default, which avoids the problem. You can clear and draw anything within your frame budget, for example.Hoyt
Z
2

@Andrew, @Dave, @trashgod Hi, I did some research on this and, finally, this is what I've got. Please correct me if I'm wrong. You cannot override paint() so you call repaint() everytime you need to do app-triggered painting. Repaint() calls update() which its default behavior is to call paint(). update() is used for incremental painting; that explains the flickering screen when paint() was doing all the work, which practically means it was painting the whole image at every step. However, my question is if I add "locationsAdded = 0" in the update method that means everytime I drag the mouse I paint the whole image(like in paint), so why doesn't it blink like before? I've also read something about painting in swing and I didn't understand why update() is never invoked for swing. Can you explain me why?

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;

class CustomCanvas extends Canvas{ 
    ArrayList<Point> locations;        
    int locationsAdded;    
    Point oldLocation;
    Point location;
    Dimension dimension;
    CustomCanvas(Dimension dimension){  
        locations = new ArrayList<>();        
        this.dimension = dimension;   
        this.init();
        addListeners();
    }    
    private void init(){                          
        oldLocation= new Point(0, 0);
        location= new Point(0, 0);
    }
    public void paintLine(Graphics g, int x){
        Point p1 = (Point)locations.get(x);
        Point p2 = (Point)locations.get(x+1);
        g.drawLine(p1.x, p1.y, p2.x, p2.y);
        locationsAdded++;
    }
    @Override
    public void paint(Graphics g){          
        locationsAdded = 0;        
        g.setColor(Color.red);                  
        for(int i = locationsAdded; i < locations.size()-1; i++){
            paintLine(g, i);
        }             
    }
    public void update(Graphics g) {        
    //locationsAdded = 0;
        for (int i = locationsAdded; i < locations.size()-1; i++) {            
            paintLine(g, i);
        }
    }
    private void addListeners(){
        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent me){                                   
                oldLocation = location;
                location = new Point(me.getX(), me.getY());
                locations.add(location);
                repaint();
            }
        }); 
    }

    @Override
    public Dimension getMinimumSize() {
        return dimension; 
    }
    @Override
    public Dimension getPreferredSize() {
        return dimension;
    }
}
class CustomFrame extends Panel {
    Panel displayPanel = new Panel(new BorderLayout());
    CustomCanvas canvas = new CustomCanvas(new Dimension(700, 700));        
    public CustomFrame(String titlu) {            
        canvas.setBackground(Color.white);
        displayPanel.add(canvas, BorderLayout.CENTER);            
        this.add(displayPanel);
    }  
}
public class AWTPainting {
    public static void main(String args[]) {
        CustomFrame panel = new CustomFrame("Test Paint");
        Frame f = new Frame();
        f.add(panel);
        f.pack();        
        f.setSize(700,700);                    
        f.show();                
    }
}
Zebulun answered 13/4, 2012 at 21:55 Comment(1)
+1 for an sscce; I've elaborated here.Hoyt
M
0

set your layout to Null layout

Mundane answered 11/6, 2020 at 0:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.