0

So I'm just doing some studying for finals here, and I'm having trouble getting this to work. What I have always done previously is call paintComponent(Graphics g) and things have worked just peachy. But now I'm getting a "paintComponent has protected access in JComponent" error when I go to compile. The only way I could get it to compile was to tell it to call paint. But even if it compiles, paint never gets called in the applet window. What am I not seeing here?

 public class LineDraw extends JComponent
 {
private final Point p1;
private final Point p2;
private final JApplet callingWindow;
private final ArrayList<Line2D.Double> lines;

public LineDraw(JApplet callingWindow)
{
    this.p1 = new Point();
    this.p2 = new Point();
    this.callingWindow = callingWindow;
    this.lines = new ArrayList<Line2D.Double>();
    MouseListener mouse = new MouseHandler();
    callingWindow.addMouseListener(mouse);
}

public class MouseHandler extends MouseAdapter
{
    private boolean firstClick;
    public MouseHandler()
    {
        firstClick = true;
    }
    public void mouseClicked(MouseEvent e)
    {
        if(firstClick)
        {
            p1.setLocation(e.getPoint());
            firstClick = false;
        }
        else    
        {
            p2.setLocation(e.getPoint());
            lines.add(new Line2D.Double(p1, p2));
            p2.setLocation(p1.getLocation());
            callingWindow.repaint();        
        }
    }

    public void paintComponent(Graphics2D g2)
    {
        for(Line2D.Double e: lines)
        {
            g2.draw(e);
        }
    }
}

}

And the applet class itself

    public class AppletWin extends JApplet
    {
private LineDraw lineDrawer;

public void init()
{
    setBackground(Color.white);
    lineDrawer = new LineDraw(this);
}

public void paint(Graphics h)
{
    Graphics2D g = (Graphics2D)h;
    //Clear background
    g.setColor(getBackground());
    g.fillRect(1, 1, getWidth(), getHeight());
    lineDrawer.paintComponent(g);  
}
    }
Scuba Steve
  • 1,541
  • 1
  • 19
  • 47

2 Answers2

1

LineDrawer is a component, you should add it to you applet and it will painted automatically.

You seem to have fallen into a naming clash problem.

JComponent defines two methods for painting (actually it defines a few more, but lets keep it simple), paint and paintComponent.

The problem you are having is to do with the fact that you've inherited from a JComponent but seem to be performing your own painting. This fine when you want to develop you own "painter" classes, but falls foul of Swing components. There is more to a Swing component then simply painting it.

The other problem you're have is you've defined a "paint" method in side MouseHandler method and seem to be assuming that it is callable from LineDraw class. The problem is, you're actually calliing the JComponent's paint method, not the one you want/think you are.

Try removing the extends JComponent and moving the paint method into the LineDraw class.

As MouseEvent has stated, under most circumstances, you should never call paint or paintComponent directly.

You may find:

Helpful

UPDATED

Painting as a Swing Component

public class AppletWin extends JApplet {

    private LineDraw lineDrawer;

    public void init() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                setBackground(Color.white);
                lineDrawer = new LineDraw(AppletWin.this);
                setLayout(new BorderLayout());
                add(lineDrawer);
            }
        });
    }

    public class LineDraw extends JComponent {

        private final Point p1;
        private final Point p2;
        private final JApplet callingWindow;
        private final ArrayList<Line2D.Double> lines;

        public LineDraw(JApplet callingWindow) {
            this.p1 = new Point();
            this.p2 = new Point();
            this.callingWindow = callingWindow;
            this.lines = new ArrayList<Line2D.Double>();
            MouseListener mouse = new MouseHandler();
            addMouseListener(mouse);
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.RED);
            for (Line2D.Double e : lines) {
                g2.draw(e);
            }
        }

        public class MouseHandler extends MouseAdapter {

            private boolean firstClick;

            public MouseHandler() {
                firstClick = true;
            }

            public void mouseClicked(MouseEvent e) {
                if (firstClick) {
                    p1.setLocation(e.getPoint());
                    firstClick = false;
                } else {
                    p2.setLocation(e.getPoint());
                    lines.add(new Line2D.Double(p1, p2));
                    p2.setLocation(p1.getLocation());
                }
                repaint();
            }
        }
    }
}

As Painter

public class AppletWin extends JApplet {

    private LineDraw lineDrawer;

    public void init() {
        setBackground(Color.white);
        lineDrawer = new LineDraw(this);
    }

    public void paint(Graphics h) {
        // The the applet clear the background
        super.paint(h);
        Graphics2D g = (Graphics2D) h;
        lineDrawer.paintComponent(g);
    }
    public class LineDraw {

        private final Point p1;
        private final Point p2;
        private final JApplet callingWindow;
        private final ArrayList<Line2D.Double> lines;

        public LineDraw(JApplet callingWindow) {
            this.p1 = new Point();
            this.p2 = new Point();
            this.callingWindow = callingWindow;
            this.lines = new ArrayList<Line2D.Double>();
            MouseListener mouse = new MouseHandler();
            callingWindow.addMouseListener(mouse);
        }

        public void paintComponent(Graphics2D g2) {
            System.out.println("...");
            for (Line2D.Double e : lines) {
                g2.draw(e);
            }
        }

        public class MouseHandler extends MouseAdapter {

            private boolean firstClick;

            public MouseHandler() {
                firstClick = true;
            }

            public void mouseClicked(MouseEvent e) {
                if (firstClick) {
                    p1.setLocation(e.getPoint());
                    firstClick = false;
                } else {
                    p2.setLocation(e.getPoint());
                    lines.add(new Line2D.Double(p1, p2));
                    p2.setLocation(p1.getLocation());
                }
                callingWindow.repaint();
            }
        }
    }
}

You really should avoid overriding the paint method of top level containers. The paint method is a very complex method and circumventing it is just going to cause you problems in the long run, besides, it's not double buffered ;)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • You're right in that I had defined paintcomponent in the wrong scope. Inheriting from JComponent is all fine if you overwrite the paintComponent method properly, as it will be called in the java painting process when repaint() is called. – Scuba Steve Dec 05 '12 at 05:53
  • I did not overwrite the method properly however hehe. – Scuba Steve Dec 05 '12 at 05:53
  • @ScubaSteve Yes, `paintComponent` *would* be called by the Swing paint sub system if 1- The component was added to a container and 2- It had the right signature (`JComponent` does not have `paintComponent(Graphics2D)` method ;)) - see updates – MadProgrammer Dec 05 '12 at 06:03
  • The JComponent doesn't have to be added to a container, but some paintable thing, like a JFrame or JApplet.. But yes, correct signature helps. – Scuba Steve Dec 08 '12 at 22:17
  • I know this because I've written entire guis without using Container – Scuba Steve Dec 08 '12 at 22:18
  • At some point, you will need a container that the paint sub system will pass a valid graphs context to in order to paint to the screen. – MadProgrammer Dec 08 '12 at 22:20
  • JFrame and JApplet can also be valid graphics contexts :D Container is great for managing your gui stuff, and I think it's needed if you want to do the more advanced gui layout stuff, but it's not needed just to draw something. – Scuba Steve Dec 09 '12 at 21:35
  • All windows (`Window`, `Frame`, `Dialog`, `JWindow`, `JFrame`, `JDialog`, `Applet`, `JApplet`) ARE containers. Top level containers are also not double buffered, reducing their efficiency. The paint mechanism is a complex beast, even for just simple drawing programs, you should avoid overriding the paint methods of top level containers, deferring to some thing `JPanel`, it also makes the component reusable, as you are not tying you're self to a top level container of the bat. – MadProgrammer Dec 09 '12 at 22:10
0

Problem with this code is that the paintComponent method in LineDraw is inside the inner class MouseHandler.

Scuba Steve
  • 1,541
  • 1
  • 19
  • 47
  • `JComponent` doesn't have a `paintComponent(Graphics2D)` method ;) – MadProgrammer Dec 05 '12 at 06:16
  • Sorry, but you are correct about it begin declared in the wrong place ;) – MadProgrammer Dec 05 '12 at 06:23
  • JComponent absolutely has a paintComponent method http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics) – Scuba Steve Dec 08 '12 at 22:15
  • check the parameters, you will find JComponent#paintComponent takes a Graphics object, not a Graphics2D object. The supplied method signature will never be called by the paint sub system – MadProgrammer Dec 08 '12 at 22:17
  • Graphics and Graphics2D actually interchangeable in this case. Polymorphism ftw. The system is actually passing around a Graphics2D object anyway. – Scuba Steve Dec 09 '12 at 21:32
  • Not according to my tests. Yes, Swing has been using `Graphics2D` since (I think 1.4, maybe 1.3), but the method signature requires a `Graphics` object, otherwise you not overriding the correct method, which means that the Swing paint subs system can't call it. – MadProgrammer Dec 09 '12 at 22:04
  • `Graphics` and `Graphics2D` are not interchangeable. `Graphics2D` extends from `Graphics`, meaning the `Graphics2D` can be past to methods to require a `Graphics` parameter, but not the other way round. – MadProgrammer Dec 09 '12 at 22:11