Zachary Boński
Zachary Boński

Reputation: 3

JPanel shows only the first custom JComponent added

I wrote a program that draws triangles on the screen. However only the first triangle is shown. How can I make multiple custom JComponents visible?

I already tried to create something like a draw() method but then I can't perform any actions on this object like i. e. I would like the color of the triangle to change whenever I click on it. For this I would need a MouseListener but it won't work with the draw() method.

View.java file:

package test;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class View extends JPanel {

public View()
{


    setPreferredSize(new Dimension(300, 300));
    add(new Triangle(20, 50, Color.red)); //this one will react to mouseClicked
    add(new Triangle(100, 200, Color.pink)); //this one doesn't appear

}


public static void main(String []args)
{
    JFrame frame = new JFrame("Trianlge test");
    frame.add(new View());
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);

}


public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    Triangle p3 = new Triangle(60, 120, Color.blue); //this one won't react to mouseClicked()
    p3.draw(g);
}
}

Triangle.java file:

    package test;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.GeneralPath;

import javax.swing.JComponent;

public class Triangle extends JComponent implements MouseListener{

private int x,y;
private Color c;
public Triangle(int x, int y, Color c)
{
    this.x = x;
    this.y = y;
    this.c = c;
    setPreferredSize(new Dimension(100, 100));
    addMouseListener(this);
}

public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    GeneralPath path = new GeneralPath();

    g2d.setColor(c);
    path.moveTo(x, y);
    path.lineTo(x, y);
    path.lineTo(x+50, y);
    path.lineTo(x, y-50);
    path.closePath();
    g2d.fill(path);
    repaint();
}

public void draw(Graphics g)
{

    Graphics2D g2d = (Graphics2D) g;
    GeneralPath path = new GeneralPath();

    g2d.setColor(c);
    path.moveTo(x, y);
    path.lineTo(x, y);
    path.lineTo(x+50, y);
    path.lineTo(x, y-50);
    path.closePath();
    g2d.fill(path);
    repaint();
}

@Override
public void mouseClicked(MouseEvent e) {
    c = Color.cyan;
    repaint();

}

@Override
public void mousePressed(MouseEvent e) {
}

@Override
public void mouseReleased(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mouseEntered(MouseEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void mouseExited(MouseEvent e) {
    // TODO Auto-generated method stub

}

}

Upvotes: 0

Views: 324

Answers (2)

Prasad Karunagoda
Prasad Karunagoda

Reputation: 2148

Set a border to Triangle components like this:

public Triangle(int x, int y, Color c)
{
  this.x = x;
  this.y = y;
  this.c = c;
  setPreferredSize(new Dimension(100, 100));
  addMouseListener(this);
  // Set this border to see the boundaries of this component.
  // When you are done, you may remove this.
  setBorder(BorderFactory.createLineBorder(Color.black));
}

Then you can better understand the bounds of the components.

Pink triangle is not visible because it is outside component's boundary.

p3 triangle does not react to mouse clicks because it is just a drawing. Only components react to mouse and other events.

Notice that components are rectangle in shape. So, the mouse listener you have added works anywhere on the component; not only on the area of triangle.

You are drawing triangles in two ways in this program.
1. By adding Triangle components. (Like "add(new Triangle(20, 50, Color.red));")
2. By drawing p3 in paintComponent() method.

From software designing perspective, better to stick to one approach. Otherwise it can be confusing and error prone.

Upvotes: 0

camickr
camickr

Reputation: 324118

JFrame frame = new JFrame();

First of all that statement in your View class is completely unnecessary. You would not create a JFrame instance in the constructor of a component. Also your code never references the variable which is a good indication it is not needed.

However, the main problem is your concept of creating custom components is wrong:

setPreferredSize(new Dimension(100, 100));

You attempt to set the preferred size of the component.

add(new Triangle(100, 200, Color.pink)); //this one doesn't appear

But then you attempt to do you custom painting at (100, 200) which is outside the size of the component. So the painting logic clipped at the size of component so you don't see anything being painted.

Custom painting should be done relative to (0, 0) of the component, not relative to the parent component.

If you you want to randomly position components on the parent panel then you need to:

  1. set the parent panel to use a null layout
  2. set the location of each component you add to the panel
  3. set the size of each component you add to the panel.

basically you need to take over the functionality of the layout manager.

Other problems with your current painting code:

  1. Don't invoke repaint() in a painting method. This will essentially cause an infinite painting loop. If you need animation you use a Swing Timer to schedule the animation.

  2. Don't invoke paintComponent(...) directly. Swing will invoke paintComponent() when a component needs to be repainted.

However, I would suggest that if you want to paint Shapes on a panel, Then you forget about creating custom components. Instead you keep an ArrayList of the Shapes you want to paint and then in the paintComponent() method of the panel you iterate through the ArrayList to paint each shape.

For an example of this approach take a look at the Draw On Component example found in Custom Painting Approaches.

Note:

If you really want to be able to handle mouse events then you need to use a Shape object to represent your shapes to do proper hit detection. If you just display your shape as a component, then the mouse hit will be detected if you click anywhere in the rectangular area of the component, not just the triangle part that you actually paint. The Shape class has a contains(...) method you can use to determine if you actually click in the Shape or not.

Check out Playing With Shapes for more information on this concept.

Upvotes: 1

Related Questions