Drawing in Jlayeredpane Over Exising JPAnels

Drawing to JPanel on Layered Pane

I would guess the main issue is that your custom component doesn't have a "size" so there is nothing to paint.

When you add a component to a JFrame, the layout manager will give the components a size. A JLayeredPane doesn't use layout managers, so it is now your responsibility to give the component a size and location.

Read the section from the Swing tutorial on How to Use Layered Panes for a working example.

Also, don't use the POPUP_LAYER. That is meant to be used for temporary display of a component only.

Getting a panel within a JLayeredPane to stretch in a BorderLayout

I tried setting a BorderLayout on the JLayeredPane, but it fails with...:

Add the component normally and then set the layer:

  private Component getMiddlePanel(JPanel wrappedPanel)
{
JLayeredPane middlePanel = new JLayeredPane();
//middlePanel.add(wrappedPanel, JLayeredPane.DEFAULT_LAYER);
middlePanel.setLayout(new BorderLayout() );
middlePanel.add(wrappedPanel, BorderLayout.CENTER);
middlePanel.setLayer(wrappedPanel, JLayeredPane.DEFAULT_LAYER);
return middlePanel;
}

Using this approach there is no need for the ComponentListener.

trying to drag something using a JLayeredPane

The original code was written assuming you were clicking on a chess piece.

Now you want to click on an empty cell which will require the following changes.

  1. The chess board consists of JPanels in each cell. Some cells will contain a JLabel represent a chess piece. The current logic in the mousePressed event is expecting you to click on a JLabel otherwise some processing is skipped.

You need to remove:

//if (c instanceof JPanel) return;

  1. By default a Swing component has a 0 size when it is created.

You need to give it a size:

chessPiece.setSize( chessPiece.getPreferredSize() );

  1. The positioning logic for the label is based on finding the location of the clicked component relative to the parent. Since there is no label, this logic is now based on the panel relative to the layered pane.

You need to adjust this logic to make it relative to parent panel again:

//Point parentLocation = c.getParent().getLocation();
Point parentLocation = c.getLocation();

My updated mousePressed method looks like:

public void mousePressed(MouseEvent e)
{
// get the component where the user pressed; iff that's not a panel,
// we'll put it on the dragging layer.
//chessPiece = null; // change1 swap the change1 lines
chessPiece = new JLabel(new ImageIcon("dukewavered.gif")); // change1
chessPiece.setSize( chessPiece.getPreferredSize() );

Component c = chessBoard.findComponentAt(e.getX(), e.getY());

//if (c instanceof JPanel) return;

// get the location of the panel containing the image panel, i.e.,
// the square's panel. we adjust the location to which we move the
// piece by this amount so the piece doesn't 'snap to' the cursor
// location.
//Point parentLocation = c.getParent().getLocation();
Point parentLocation = c.getLocation();
xAdjustment = parentLocation.x - e.getX();
yAdjustment = parentLocation.y - e.getY();
//chessPiece = (JLabel)c; // change2 - comment out
chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);

layeredPane.add(chessPiece, JLayeredPane.DRAG_LAYER); // evidently this removes it from the default layer also.
layeredPane.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}

Note the above changes will break the old functionality of being able to drag an existing label. If you need functionality for both then your logic will be determined on whether you click on a JLabel (in which case you use the old logic) or click on a JPanel (in which case you use the newer logic).

Adding a JLayeredPane to a JPanel

I insert the JLayeredPane to the JPanel and then insert the JPanel to the frame It doesn't display the JLayeredPane.

A JPanel uses a FlowLayout which respects the preferred size of any component added to it.

A JLayeredPane does not use a layout manager and therefore does not have a preferred size so it is not displayed on the panel.

the components show and everything is okay,

The frame which uses a BorderLayout. The default location is the "CENTER" and the center will size the layered pane based on the space available in the frame.

There is no need for the extra overhead of adding the layered pane to the panel and then the panel to the frame.

Read the section from the Swing tutorial on How to Use Layered Panes for more information and working examples.

Drawing on a panel but when adding it through another frame, nothing draws

There are 2 separate issues here.

(1) Simplest answer is - you need to call 'getGraphics' from within your action method. Not from the constructor. E.g.

public void jButton1ActionPerformed(java.awt.event.ActionEvent evt)  {                                         
Graphics2D doo = (Graphics2D) jPanel2.getGraphics();
...
doo.setFont(...);
doo.drawString(...);
}

(2) This would yield visible drawings, but they'll disappear whenever java decides to repaint - e.g. if you minimize the frame. This can be solved by paintComponent() as mentioned in the remarks. The basic idea is that your component (eg jPanel2) would hold a data structure of eveything it needs to paint - Strings, edges, vertexes etc. In paintComponent you draw them all. In actionPerformed() you change the datastructure and invoke 'repaint'. A sketch of this approach:

class MyPanel extends JPanel{
private String text;
private Point[] vertextes;
public void addVertext(..)
public void paintComponent(Graphics g){
... use g to drawString, drawOval... according to 'text' and 'vertexes'
}
}
// Then in your JFrame:
private MyPanel p;
...
actionPerfomred(...){
p.addVertext(..)
p.repaint();
}

Java Swing - how to show a panel on top of another panel?

I think LayeredPane is your best bet here. You would need a third panel though to contain A and B. This third panel would be the layeredPane and then panel A and B could still have a nice LayoutManagers. All you would have to do is center B over A and there is quite a lot of examples in the Swing trail on how to do this. Tutorial for positioning without a LayoutManager.

public class Main {
private JFrame frame = new JFrame();
private JLayeredPane lpane = new JLayeredPane();
private JPanel panelBlue = new JPanel();
private JPanel panelGreen = new JPanel();
public Main()
{
frame.setPreferredSize(new Dimension(600, 400));
frame.setLayout(new BorderLayout());
frame.add(lpane, BorderLayout.CENTER);
lpane.setBounds(0, 0, 600, 400);
panelBlue.setBackground(Color.BLUE);
panelBlue.setBounds(0, 0, 600, 400);
panelBlue.setOpaque(true);
panelGreen.setBackground(Color.GREEN);
panelGreen.setBounds(200, 100, 100, 100);
panelGreen.setOpaque(true);
lpane.add(panelBlue, new Integer(0), 0);
lpane.add(panelGreen, new Integer(1), 0);
frame.pack();
frame.setVisible(true);
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
new Main();
}

}

You use setBounds to position the panels inside the layered pane and also to set their sizes.

Edit to reflect changes to original post
You will need to add component listeners that detect when the parent container is being resized and then dynamically change the bounds of panel A and B.

how to draw line in jlayeredpane Java

A JLayeredPane uses a null layout. So when you add your custom painting panel to the layered pane you need to give the panel a size otherwise the size is (0, 0) so there is nothing to paint.

So the code should be something like:

JPanel panel = new CustomPaintingPanel();
panel.setSize(300, 300);
layeredPane.add(panel, ...);
frame.add(layeredPane);

Painting over the top of components in Swing?

This is what I'm already doing (on a much simpler level obviously), and Swing paints the rectangle underneath the components added to it.

This is one case where you should override the paint() method of the panel and not the paintComponent() method. Then the custom painting will be done AFTER all the child components have been painted.

JLayeredPane: How to check if component has another drawn under it

Basically two tasks:

  • visit the components in the same layer that are below the one that receives the mouseEvent and find the first that contains the mouse location
  • if found, manually dispatch the event (and let it handle further dispatches, if necessary)

JLayeredPane api helps with the first, and Component has a method to do the second - the only thingy that needs attention is coordinate transformation where SwingUtilities' helper methods come into play

@Override
public void mouseClicked(MouseEvent evt) {
if (!SwingUtilities.isLeftMouseButton(evt)) return;
Component[] siblings = layeredPane.getComponentsInLayer(JLayeredPane.getLayer(this));
int pos = layeredPane.getPosition(this);
for (Component sibling : siblings) {
// interested in siblings below
if (layeredPane.getPosition(sibling) > pos) {
// convert coordinates
Point p = SwingUtilities.convertPoint(this, evt.getX(), evt.getY(), sibling);
if (sibling.contains(p)) {
// convert event
MouseEvent dispatch = SwingUtilities.convertMouseEvent(
this, evt, sibling);
// manually dispatch the event
sibling.dispatchEvent(dispatch);
break;
}
}

}
// do my own work
System.out.println(getName() + evt);
}


Related Topics



Leave a reply



Submit