Solved

Creating a freehand drawing program with a menu bar...

Posted on 2002-07-03
12
890 Views
Last Modified: 2010-08-05
I'm trying to create a small application that allows me to draw freehand lines on a JPanel (which will reside in the Content Pane of a JFrame.  I also added a JMenuBar with a JMenu (containing a couple of JMenuItems) to the Root Pane using its setJMenuBar method.  

The problem is that if I don't call super.paintComponent(g) in the JPanel's paintComponent method, the background (red) doesn't get painted and there's a duplicate menubar painted on the Content Pane for some reason (?).  If I do call the super method, it repaints the background every time (which covers the duplicate menu bar but erases any previous lines that were drawn).  The call to super in paintComponent is commented out in the code below.



The code I've got so far is below:

-----------------------
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;


public class DrawTest
{


      /**
       *
       *  INNER class that represents the frame that contains the panel on which the drawing
       *  will be done (it is also the event listener).
       *
       */


    public class AFrame extends JFrame
    {

            private APanel mainPanel;
            private JMenuBar jmb;

        public AFrame()
        {

            super("DrawTest");
            setSize(500,500);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                  // Create Menu Bar

                  jmb = new JMenuBar();
                  JMenu jm = new JMenu("Draw");
                  JMenuItem jmi_1 = new JMenuItem("Freehand");
                  JMenuItem jmi_2 = new JMenuItem("Rectangle");
                  jm.add(jmi_1);
                  jm.add(jmi_2);
                  jmb.add(jm);
                  getRootPane().setJMenuBar(jmb);


                  // Create the panel on which the drawing will be done

                  mainPanel = new APanel();
                  mainPanel.setBounds(0,0,500,500);
                  mainPanel.setBackground(Color.RED);
                  mainPanel.setVisible(true);
                  mainPanel.setOpaque(true);

                  getContentPane().setLayout(null);
                  getContentPane().add(mainPanel);


                  // Register the Panel/EventListener with itself

            mainPanel.addMouseMotionListener(mainPanel);
            mainPanel.addMouseListener(mainPanel);

        } // end AFrame constructor


        public void paint(Graphics g)
        {
                  super.paint(g);

        } // end paint






    } // end AFrame INNER class



      /**
       *
       *  INNER class that represents the single panel on which the drawing will actually
       *  be done.
       *
       */

      public class APanel extends JPanel implements MouseMotionListener, MouseListener
      {


            private Point mCurr;      // refers to the current point to which the line is to be drawn
            private Point mLast;      // refers to the last point that the line left off at


            public void paintComponent(Graphics g)
            {
                       
                  //super.paintComponent(g);
                  Graphics2D g2 = (Graphics2D) g;



                  // Ensure that both references have a legitimate instance

                  if (getLast() != null && getCurr() != null)
                  {

                        Line2D.Double line = new Line2D.Double(this.getLast(), this.getCurr());

                        g2.setPaint(Color.YELLOW);
                        g2.draw(line);

                        // Connect to the current point on the next event

                        setLast(getCurr());

                  }




            } // end paintComponent


            public void initializePanel()
            {
                  super.paintComponent(getGraphics());
            }



            // Get and set methods

            public void setCurr(Point p) {      mCurr = p; };
            public void setLast(Point p) {      mLast = p; };
            public Point getCurr() { return mCurr; };
            public Point getLast() { return mLast; };

            public void mouseReleased(MouseEvent e)
            {
                  this.setLast(null);
            }


            public void mouseDragged(MouseEvent e)
            {

                  // Set the panel's current point to the point at which this event occurred

                  this.setCurr(e.getPoint());

                  if (this.getLast() == null)
                        this.setLast(this.getCurr());

                  this.repaint();

            } // end mouseDragged


            // Unused method from MouseMotionListener Interface
            public void mouseMoved(MouseEvent e){};

            // Unused methods from the MouseListener Interface
            public void mousePressed(MouseEvent e){};
            public void mouseEntered(MouseEvent e){};
            public void mouseExited(MouseEvent e){};
        public void mouseClicked(MouseEvent e){};


      } // end APanel INNER class



      /**
       *
       *  Constructor of OUTER class that instantiates frame object of
       *  inner class <code>AFrame</code>.
       *
       */

    public DrawTest()
    {

          AFrame aframe = new AFrame();
        aframe.setVisible(true);

    } // end DrawTest constructor



    public static void main(String[] args)
    {

          DrawTest app = new DrawTest();

    } // end main




} // end DrawTest class

-----------------------


I'm not sure if I'm going about things the right way or not.  Any ideas would be much appreciated.
0
Comment
Question by:moshecristel
  • 7
  • 4
12 Comments
 
LVL 92

Expert Comment

by:objects
ID: 7128342
You should not do this:

          public void initializePanel()
          {
               super.paintComponent(getGraphics());
          }

0
 
LVL 92

Expert Comment

by:objects
ID: 7128350
You only paint a line between the last two points.
So whenever your panel gets repainted that is what you are going to see.

You need to paint lines between *all* points.
0
 
LVL 92

Expert Comment

by:objects
ID: 7128398
This is unnecessary:

      public void paint(Graphics g)
       {
               super.paint(g);

       } // end paint

0
 

Author Comment

by:moshecristel
ID: 7128706
objects:

I've tried storing all lines in a Vector and then just repainting them all each time the paintComponent method of the panel is called but as the size of the drawing grows the repaint becomes slower and slower and the events seem to fire less often (or something ?) because the line is drawn more jagged.

By just drawing one line each time an event fires (and not erasing the panel) I was hoping that things would be more efficient.

I could get rid of the initializePanel method and put a boolean in the panel called clear (that was initialized to true) so in paintComponent I would start out with something like:

...
if (clear)
{
    super.paintComponent(g)
    clear = false;
}
...

This way it would only call the super the first time through and paint the background.

There would still be the problem of the duplicate menu bar (which has been haunting me).  It's still there, just covered.  Any ideas of why that second menu bar might be showing up?
0
 
LVL 92

Expert Comment

by:objects
ID: 7128749
Your misunderstanding how the painting works.
paintComponent is called whenever your component needs repainting and is responsible for painting the entire component. You cannot simply paint new stuff.
ie. everytime it is called it needs to paint the background, and all the lines.
Try dragging another window over your window and you will see your painting disappear becuase you do not repaint it.

The duplicate menu bar is a similiar problem, it's not actually a duplicate, you're just not repainting what needs to be underneath it.

If the performance of painting all the lines is a problem as I would expect it to be, then what you need to do is use an offscreen image and paint your lines to it. Then in the paintComponent method just paint this image to the screen.
0
 

Author Comment

by:moshecristel
ID: 7128873
objects:

This makes sense but I'm not sure what you mean by "use an offscreen image and paint your lines to it".  

How would you go about doing this?
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 92

Accepted Solution

by:
objects earned 150 total points
ID: 7128888
Create an image the same size as your component.
Then get a graphics context for this image and perform your painting to this, instead of to the components graphic context:

Graphics g = image.getGraphics();
g.drawLine(....

Then in your paintComponent method simply paint this image to screen:

g.drawImage(image, 0, 0, this);
0
 

Author Comment

by:moshecristel
ID: 7128983
objects:

Excellent!  It worked beautifully.  Very fast and no more problems with needing to paint over the menu bar mirage :)

Thanks.
0
 
LVL 92

Expert Comment

by:objects
ID: 7129002
No worries, happy to help :-)
0
 
LVL 35

Expert Comment

by:girionis
ID: 8893899
No comment has been added lately, so it's time to clean up this TA.

I will leave a recommendation in the Cleanup topic area that this question is:

- points to objects

Please leave any comments here within the
next seven days.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER !

girionis
Cleanup Volunteer
0
 

Author Comment

by:moshecristel
ID: 8894015
objects:

Sorry, didn't realize I hadn't accepted your answer :(
0
 
LVL 92

Expert Comment

by:objects
ID: 8897942
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

After being asked a question last year, I went into one of my moods where I did some research and code just for the fun and learning of it all.  Subsequently, from this journey, I put together this article on "Range Searching Using Visual Basic.NET …
Java functions are among the best things for programmers to work with as Java sites can be very easy to read and prepare. Java especially simplifies many processes in the coding industry as it helps integrate many forms of technology and different d…
Viewers will learn about the regular for loop in Java and how to use it. Definition: Break the for loop down into 3 parts: Syntax when using for loops: Example using a for loop:
This theoretical tutorial explains exceptions, reasons for exceptions, different categories of exception and exception hierarchy.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now