50

I'm working on a project in which I am trying to make a paint program. So far I've used Netbeans to create a GUI and set up the program.

As of right now I am able to call all the coordinated necessary to draw inside it but I am very confused with how to actually paint inside it.

Towards the end of my code I have a failed attempt at drawing inside the panel.

Can anyone explain/show how to use graphics in a example like this?

All examples I have found make a class and extend it with JPanel but I don't know if I can do this since it was generated in netbeans.

I need to draw inside a JPanel, inside my JFrame. I don't know where to put the graphics class.

JavaPaintUI Class

package javapaint;

import java.awt.*;
import javax.swing.*;

public class JavaPaintUI extends javax.swing.JFrame {

public JavaPaintUI() {
    initComponents();
}


private void initComponents() {


    jPanel2 = new javax.swing.JPanel();

    jPanel2.setBackground(new java.awt.Color(255, 255, 255));
    jPanel2.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
    jPanel2.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mousePressed(java.awt.event.MouseEvent evt) {
            jPanel2MousePressed(evt);
        }
        public void mouseReleased(java.awt.event.MouseEvent evt) {
            jPanel2MouseReleased(evt);
        }
    });
    jPanel2.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
        public void mouseDragged(java.awt.event.MouseEvent evt) {
            jPanel2MouseDragged(evt);
        }
    });
    pack();
}// </editor-fold>                        

int currentX, currentY, oldX, oldY;

private void jPanel2MouseDragged(java.awt.event.MouseEvent evt) {                                     
    if (tool == 1) {
        currentX = evt.getX();
        currentY = evt.getY();
        oldX = currentX;
        oldY = currentY;
        System.out.println(currentX + " " + currentY);
        System.out.println("PEN!!!!");
    }

}                                    

private void jPanel2MousePressed(java.awt.event.MouseEvent evt) {                                     
    oldX = evt.getX();
    oldY = evt.getY();
    System.out.println(oldX + " " + oldY);
}                                    


//mouse released//
private void jPanel2MouseReleased(java.awt.event.MouseEvent evt) {                                      
    if (tool == 2) {
        currentX = evt.getX();
        currentY = evt.getY();
        System.out.println("line!!!! from" + oldX + "to" + currentX);
    }
}                                     

//set ui visible//
public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(new Runnable() {

        public void run() {
            new JavaPaintUI().setVisible(true);
        }
    });
}

// Variables declaration - do not modify                     
private javax.swing.JPanel jPanel2;
// End of variables declaration                   

class jPanel2 extends JPanel {

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.drawString("BLAH", 20, 20);
        g.drawRect(200, 200, 200, 200);
    }
}
}

Screen shot

The whole thing is a JFrame and the white section in the center is jPanel2 which is what I want to draw on. screen shot of some code that is not this

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Nick R
  • 543
  • 2
  • 6
  • 13
  • 7
    For a very simple example of painting in Java, please see my reply in this thread: [changing-jpanel-graphics-g-color-drawing-line](http://stackoverflow.com/questions/6105393/changing-jpanel-graphics-g-color-drawing-line/6105437#6105437). Also, don't futz with NetBeans-generated code as learning to first code Swing/graphics yourself will pay immediate and long-term dividends. – Hovercraft Full Of Eels May 25 '11 at 02:01
  • 4
    "all examples i have found make a class and extend it with jpanel but i dont know if i can do this since it was generated in netbeans." Well there's your basic problem, not understanding how to use your automagic IDE. In that case I recommend putting aside the IDE you do not understand, and which has seemingly grabbed you by the short groin hairs, and learn how to code Java. Maybe later, with the help of the F1 facility of the IDE, you'll figure how to work with custom components. But leave that for the moment, since it is an extra complication you don't need. – Andrew Thompson May 25 '11 at 02:12
  • 1
    for sure, i realized this on several sites now that cheaters don't win :) thanks guys alot further now than i was before – Nick R May 25 '11 at 04:46

4 Answers4

37

Note the extra comments.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

class JavaPaintUI extends JFrame {

    private int tool = 1;
    int currentX, currentY, oldX, oldY;

    public JavaPaintUI() {
        initComponents();
    }

    private void initComponents() {
        // we want a custom Panel2, not a generic JPanel!
        jPanel2 = new Panel2();

        jPanel2.setBackground(new java.awt.Color(255, 255, 255));
        jPanel2.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
        jPanel2.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent evt) {
                jPanel2MousePressed(evt);
            }
            public void mouseReleased(MouseEvent evt) {
                jPanel2MouseReleased(evt);
            }
        });
        jPanel2.addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseDragged(MouseEvent evt) {
                jPanel2MouseDragged(evt);
            }
        });

        // add the component to the frame to see it!
        this.setContentPane(jPanel2);
        // be nice to testers..
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
    }// </editor-fold>

    private void jPanel2MouseDragged(MouseEvent evt) {
        if (tool == 1) {
            currentX = evt.getX();
            currentY = evt.getY();
            oldX = currentX;
            oldY = currentY;
            System.out.println(currentX + " " + currentY);
            System.out.println("PEN!!!!");
        }
    }

    private void jPanel2MousePressed(MouseEvent evt) {
        oldX = evt.getX();
        oldY = evt.getY();
        System.out.println(oldX + " " + oldY);
    }


    //mouse released//
    private void jPanel2MouseReleased(MouseEvent evt) {
        if (tool == 2) {
            currentX = evt.getX();
            currentY = evt.getY();
            System.out.println("line!!!! from" + oldX + "to" + currentX);
        }
    }

    //set ui visible//
    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                new JavaPaintUI().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify
    private JPanel jPanel2;
    // End of variables declaration

    // This class name is very confusing, since it is also used as the
    // name of an attribute!
    //class jPanel2 extends JPanel {
    class Panel2 extends JPanel {

        Panel2() {
            // set a preferred size for the custom panel.
            setPreferredSize(new Dimension(420,420));
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            g.drawString("BLAH", 20, 20);
            g.drawRect(200, 200, 200, 200);
        }
    }
}

Screen Shot

enter image description here

Other examples - more tailored to multiple lines & multiple line segments

HFOE put a good link as the first comment on this thread. Camickr also has a description of active painting vs. drawing to a BufferedImage in the Custom Painting Approaches article.

See also this approach using painting in a BufferedImage.

Community
  • 1
  • 1
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 1
    @Andrew, the only thing you answer does is rename the inner class... my answer gives a viable solution on how to code a drawing program. I'm not attacking your answer. I'm saying it could be a one liner comment and would provide the same impact. The question is "can anyone explain/show how to use graphics in a example like this?" Now, does your answer answers it? – Yanick Rochon May 25 '11 at 02:52
  • @Yanick: "the only thing you answer does is rename the inner class.." Check the comments starting with the following to see the **5** noteworthy changes to the source. // we want a custom Panel2, not a generic JPanel! // add the component to the frame to see it! // be nice to testers.. // This class name is very confusing, since it is also used as the // name of an attribute! // set a preferred size for the custom panel. – Andrew Thompson May 25 '11 at 02:56
  • I've read your "modifications" (read the update on my last comment); it still does not answer the question. I'm actually surprised you have an upvote. – Yanick Rochon May 25 '11 at 02:58
  • 6
    +1 for answering the question with working code by tweaking the original code. – camickr May 25 '11 at 03:08
  • Thanks for answer, However when i do these changes it overides all my other UI's and swing components (possibly all the netbeans stuff). okay now to make it paint when you move the mouse im using g.drawLine(oldX, oldY, currentX, currentY); but if i put that in my paintcomponent followed by repaint() it only draws the line temporary. I want it do draw lines similar to how i have it output (when pen is selected, every movement paints a line from new and previous cords and when the line is selected it paints a line when released from the first point to the second point) – Nick R May 25 '11 at 03:54
  • @Nick: I have to admit I neglected to check the screen shot (which is now embedded in your question). Where the heck did those other controls come from? As to drawing multiple lines, either you need to store multiple co-ordinates or better multiple `PaintedElement`s (which encapsulate co-ords, stroke size, pen/line & stroke size) in an expandable list and paint them all in the `paintComponent()`, or write each one in turn to a `BufferedImage`. If the user chooses to `Clear` the current image, either empty the co-ord/`PaintedElement` lists or refresh the `BufferedImage`. – Andrew Thompson May 25 '11 at 04:15
  • haha i eliminated it as i thought it was irrelevant at the time ( should i update full code? ) any idea of why the rest disappeared? i'm not really concerned with those options thought at the moment. I got it to draw lines in the panel when you click using redraw, but now im working on them staying there. i use buffered images to do this right? – Nick R May 25 '11 at 04:17
  • @Nick: "( should i update full code? )" No. I suggest you prepare an [SSCCE](http://pscode.org/sscce.html) (my example is an SSCCE). "i use buffered images to do this right?" Either a `BufferedImage` or storing the (hypothetical) `PaintedElement`s yes. One advantage of using a `BufferedImage` is that you can set it as the `ImageIcon` of a `JLabel` then add the label to a panel. There would be no need to override/extend anything in that case. – Andrew Thompson May 25 '11 at 04:30
  • thanks a ton by the way. Okay well then my only task as of now are to figure out how to buffer and save the image, and restore my missing gui :/ lol – Nick R May 25 '11 at 04:34
  • @Nick: See the examples mentioned in my latest edit. Have not delved too deeply into HFOE's e.g., but camickr's definitely covers most aspects of what you are asking. – Andrew Thompson May 25 '11 at 04:41
  • @Yanick you should avoid bashing other answers like that (I think your 1st comment was just enough, no need to insist further). As far as I am concerned, this answer looks fine to me too, it shows why the original code doesn't paint what it is supposed to. As far as I'm concerned, both answers are a step in solving the OP's problem. – jfpoilpret May 25 '11 at 05:39
  • @jfpoilpret: "(I think your 1st comment was just enough, no need to insist further)." Note that I made a (rather acerbic) reply to the initial comment, that prompted the further comments/clarification from Yanick. My 1st reply did not seem that important in the larger scheme of things, so I deleted it. Sorry for any confusion that caused. :( – Andrew Thompson May 25 '11 at 07:07
  • @jfpoilpret, I try not to bash on answers, and I would say that I often tell people myself not to do so. At least I never insult anyone. However, I like when SO's answers are complete and address the problem directly. The two questions asked here are : 1) "How to draw in jPanel?" and 2) "Can anyone explain/show how to use graphics in a example like this?"; in that sense, this answer only correct a class naming issue, and to me does not answer any of those questions. ... or I may have totally misread what was the question, because there are no other question mark elsewhere... – Yanick Rochon May 25 '11 at 12:43
  • @Yanick I generally think that any answer or answer step that can help on the reply (or part of it) is worth being given as an answer and not just a comment. Then it is up to people reading the question and its various answers to judge how relevant each answer is. Note that for my part, I have upvoted both answers because both seemed a right step in answering the original question. If you try to get only one answer for each question, then you will pass through a lot of useful answers or advices and that would be a pity. – jfpoilpret May 25 '11 at 12:55
  • i just needed to remove the this.setContentPane(jPanel2); and now my gui is all back. I think i'm going to rework how i get the coordinates and store them in a array. – Nick R May 25 '11 at 20:37
16

When working with graphical user interfaces, you need to remember that drawing on a pane is done in the Java AWT/Swing event queue. You can't just use the Graphics object outside the paint()/paintComponent()/etc. methods.

However, you can use a technique called "Frame buffering". Basically, you need to have a BufferedImage and draw directly on it (see it's createGraphics() method; that graphics context you can keep and reuse for multiple operations on a same BufferedImage instance, no need to recreate it all the time, only when creating a new instance). Then, in your JPanel's paintComponent(), you simply need to draw the BufferedImage instance unto the JPanel. Using this technique, you can perform zoom, translation and rotation operations quite easily through affine transformations.

Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • Thanks for the answer, ive seen this used in a few examples but was unsure of what it did. ill try it out. – Nick R May 25 '11 at 03:32
  • I think i have bit off too much for me to chew on this one, havent learned nearly enough of these concepts. – Nick R May 25 '11 at 03:50
  • 1
    @Nick, see a buffered image like a glorified 2d array of pixel values with convenient drawing methods, etc. Your application draws unto the buffered image--via the buffered image's `Graphics` context--and your JPanel use that image to update itself when it is ready to be updated (when the OS redraws it). This the most simple solution in your case; for **raster graphics**. A yet more complex solution would be to keep an array of drawing elements like suggested by Andrew, instead of a buffered image, and draw those elements in the `paintComponent` method; for a **vector graphics** solution. – Yanick Rochon May 25 '11 at 13:08
11

Here is a simple example. I suppose it will be easy to understand:

import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Graph extends JFrame {
JFrame f = new JFrame();
JPanel jp;


public Graph() {
    f.setTitle("Simple Drawing");
    f.setSize(300, 300);
    f.setDefaultCloseOperation(EXIT_ON_CLOSE);

    jp = new GPanel();
    f.add(jp);
    f.setVisible(true);
}

public static void main(String[] args) {
    Graph g1 = new Graph();
    g1.setVisible(true);
}

class GPanel extends JPanel {
    public GPanel() {
        f.setPreferredSize(new Dimension(300, 300));
    }

    @Override
    public void paintComponent(Graphics g) {
        //rectangle originates at 10,10 and ends at 240,240
        g.drawRect(10, 10, 240, 240);
        //filled Rectangle with rounded corners.    
        g.fillRoundRect(50, 50, 100, 100, 80, 80);
    }
}

}

And the output looks like this:

Output

Community
  • 1
  • 1
Bijaya Bidari
  • 331
  • 2
  • 12
  • 3
    You either `extends JFrame` or use `JFrame f = new JFrame();`. By doing both you create 2 `JFrame` objects. Also you can omit `g.drawRect(10, 10, 240, 240);` – c0der May 11 '17 at 05:36
0

Variation of the code by Bijaya Bidari that is accepted by Java 8 without warnings in regard with overridable method calls in constructor:

public class Graph extends JFrame {
    JPanel jp;

    public Graph() {
        super("Simple Drawing");
        super.setSize(300, 300);
        super.setDefaultCloseOperation(EXIT_ON_CLOSE);

        jp = new GPanel();
        super.add(jp);
    }

    public static void main(String[] args) {
        Graph g1 = new Graph();
        g1.setVisible(true);
    }

    class GPanel extends JPanel {
        public GPanel() {
            super.setPreferredSize(new Dimension(300, 300));
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            //rectangle originated at 10,10 and end at 240,240
            g.drawRect(10, 10, 240, 240);
                    //filled Rectangle with rounded corners.    
            g.fillRoundRect(50, 50, 100, 100, 80, 80);
        }
    }
}
joser
  • 113
  • 7