3

I have not used Swing/G2D much, so please be patient. I have the following class which is a component on my GUI (meant to be a kind of Canvas to draw on):

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

public class DrawPanel extends JComponent{
public void paintComponent(Graphics g){
    Graphics2D g2 = (Graphics2D) g;

    g2.setPaint(Color.black);
    g2.fillRect(0, 0, getWidth(), getHeight());

    BrushStroke bs = new BrushStroke();     
    add(bs);
}
}

I have been trying to add the following to the above JComponent:

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

public class BrushStroke extends JComponent{
public void paintComponent(Graphics g){
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                RenderingHints.VALUE_ANTIALIAS_ON);

    g2.setPaint(Color.red);
    g2.fillOval(0, 0, 10, 10);          
}
}

The BrushStroke does not show on the DrawPanel.

I have been searching forever for an answer, and each example I look at seems to be contradictory.

If anybody has attempted what I am, then help would be greatly appreciated. Also, if I am taking the completely wrong approach, please do say.

mre
  • 43,520
  • 33
  • 120
  • 170
Jack H
  • 2,440
  • 4
  • 40
  • 63
  • You shouldn't be using a `JComponent` for a brush stroke. – Chris Dennett Nov 28 '11 at 20:12
  • @ChrisDennett, there is nothing wrong with using JComponent for custom painting. By default the component will be transparent. If you want an opaque component then, yes, you would probably extend JPanel so you don't have to worry about the background painting. – camickr Nov 28 '11 at 22:24

2 Answers2

3
  1. You should never add a component to a panel in any painting method. The painting methods are invoked whenever Swing determines a component needs to be painted. Therefore you would be adding the component to the panel multiple times.

  2. When you do custom painting you are responsible for overriding the getPreferredSize() method to give the size of the component. This way the layout managers can position the components properly. If you don't do this then the preferred size is 0, so there is nothing to paint.

Read the section from the Swing tutorial on Custom Painting for more information and examples.

camickr
  • 321,443
  • 19
  • 166
  • 288
2

On the JComponent.add method, the documentation says:

Note: If a component has been added to a container that has been displayed, validate must be called on that container to display the new component. If multiple components are being added, you can improve efficiency by calling validate only once, after all the components have been added.

You should refresh your DrawPanel after adding an element to it. Watch out not to do it in the painComponent method, you will end up in an infinite recursion.

Do the following instead:

DrawPanel drawPanel = new DrawPanel();
drawPanel.add(new BrushStroke());
drawPanel.repaint();

EDIT Here is a fully working solution (extending JPanels instead of JComponent)

public static void main(String[] args){
   JFrame frame = new JFrame();
   DrawPanel drawPanel = new DrawPanel();
   drawPanel.add(new BrushStroke());
   frame.getContentPane().add(drawPanel);
   frame.pack();
   frame.setVisible(true);
}
class DrawPanel extends JPanel{
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setPaint(Color.black);
        g2.fillRect(0, 0, getWidth(), getHeight());
    }
    @Override
    public  Dimension getPreferredSize(){
        return new Dimension(100, 100);
    }
}
class BrushStroke extends JPanel{
    public void paintComponent(Graphics g){
        this.setOpaque(false);
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setPaint(Color.red);
        g2.fillOval(0, 0, 10, 10); 
    }
    @Override
    public  Dimension getPreferredSize(){
        return new Dimension(10, 10);
    }
}

The output gives the following:

enter image description here

GETah
  • 20,922
  • 7
  • 61
  • 103
  • Hi, thanks for the response. I shall try this. I do have one question though. I did my original implementation with dumping the DrawPanel to a buffer in mind. With this new schematic, how would I achieve this? Thanks. – Jack H Nov 28 '11 at 20:31
  • No problem. What do you mean by dumping DrawPanel to a buffer? – GETah Nov 28 '11 at 20:42
  • -1, No you should not do a super.paint() at the end of paintComponent(). This will cause an infinite loop because paint() invokes paintComponent(). Also, you should not invoke paint() directly on a component. You should use repaint() to tell Swing that a component needs repainting. – camickr Nov 28 '11 at 22:19
  • @GETah So I can make use of offscreen imaging. – Jack H Nov 29 '11 at 14:24
  • Hi, I have been trying this, but it is not working. Any other ideas? – Jack H Nov 29 '11 at 23:08
  • @VisionIncision Same problem as before? – GETah Nov 29 '11 at 23:10
  • @GETah Yeah, I really am scratching my head. Maybe I haven't explained myself properly. I am trying to simply create my own classes to represent shapes or brush strokes, and add these to my 'canvas'. Thankyou, I know this must be a little frustrating. – Jack H Nov 29 '11 at 23:14
  • 1
    @VisionIncision Let me try your code tomorrow and see :) I will keep you posted – GETah Nov 29 '11 at 23:30
  • @GETah Thank you, great help. – Jack H Nov 29 '11 at 23:37
  • @GETah Thanks so much for this, I was really scratching my head with it. Just so I know for future reference, why was my implementation not working? Thanks again. – Jack H Dec 01 '11 at 12:17
  • 1
    @VisionIncision Very good question. There might be something that JPanel implements but not JComponent (I am not really used to JComponents :)) – GETah Dec 01 '11 at 13:04