I have written a DragAndDrop MouseListener.
Component A is a "background" image that is behind Component B. Both are located on a JPanel.
I have made the image draggable. However, I want the image to remain behind component B as I drag it.
However, every time I drag the image, I suppose Java gives it focus or something, so it gets brought to the forefront.
Is there a method that can keep the image in the back even as I am dragging it?
I know I can use a JLayeredPane
and use the moveToBack
method every time I drag, but I would rather not use a JLayeredPane and just use a JPanel. Is there a moveToBack
equivalent for JPanel?
Or is there a way to make the component preserve the current layer (maybe "not gain focus") so that I can drag it within its current layer?
HERE IS AN EXAMPLE
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class OverlapTester extends JFrame {
public static final long serialVersionUID = 172L;
public static void main(String[] args) {
OverlapTester frame = new OverlapTester();
frame.initialize();
}
public void initialize() {
setLayout(null);
JButton bottom = new JButton("bottom");
JButton top = new JButton("top");
bottom.setBounds(0,0,100,100);
top.setBounds(0,0,50,50);
add(top);
add(bottom);
int bottomZOrder = 0;
bottom.addMouseListener(new MouseListener(){
@Override
public void mouseEntered(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseExited(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseReleased(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mousePressed(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseClicked(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
bottom.addMouseMotionListener(new MouseMotionListener(){
@Override
public void mouseDragged(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseMoved(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
}
}
Component A is a "background" image that is behind Component B. Both are located on a JPanel.
Swing components are painted based on their ZOrder
. The highest ZOrder
is painted first.
The ZOrder
is assigned as a component is added to the panel. So the first component added is given ZOrder 0
, and the second component ZOrder 1
.
So add your "background" image to the panel last and it will always have the highest ZOrder
which means it is painted first, so other components will be painted on top of it.
For example:
panel.add(someOtherComponent);
panel.add(background);
Or you can use the:
Container.setComponentZOrder(...)
method to change the ZOrder
dynamically after a component has been added.
Edit:
The answer I provided was you was an either/or solution. You only need to choose one approach.
You implemented the first approach correctly, which is to add the "background" component last. So there is no need to then play with the ZOrder. This approach works great when ZOrder will remain fixed.
The second approach is when you want the ZOrder to dynamically change, say when you have multiple components and you want to change it as you interact with each component. However you implemented this approach incorrectly. You used "0" for the ZOrder, which means the component will always be painted last and therefore on on top of any other component.
In your current example there is no need to change the ZOrder dynamically, so you can remove all related code.
As you can see the bottom button is consistently being added to the forefront
Swing painting is optimized to assume components don't overlap.
However, in the case of a JButton, it has automatic repaint logic when you hover over the button to repaint the Border. So when the button is repainted it still paints over top of the other component.
So you need to tell Swing to NOT optimize the painting. Therefore when one component is repainted they will all be repainted when they overlap. You do this by overriding the isOptimizedDrawingEnabled(...)
method as demonstrated below:
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.*;
public class OverlapTester extends JFrame {
public static void main(String[] args) {
OverlapTester frame = new OverlapTester();
frame.initialize();
}
public void initialize() {
JPanel contentPane = new JPanel(null)
{
@Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
};
add(contentPane);
// setLayout(null);
JButton bottom = new JButton("bottom");
JButton top = new JButton("top");
bottom.setBounds(0,0,100,100);
top.setBounds(0,0,50,50);
// add(top);
// add(bottom);
contentPane.add(top);
contentPane.add(bottom);
// int bottomZOrder = 0;
int bottomZOrder = 1;
bottom.addMouseListener(new MouseListener(){
@Override
public void mouseEntered(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseExited(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseReleased(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mousePressed(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseClicked(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
bottom.addMouseMotionListener(new MouseMotionListener(){
@Override
public void mouseDragged(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
@Override
public void mouseMoved(MouseEvent e) {
e.getComponent().getParent().setComponentZOrder(e.getComponent(), bottomZOrder);
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
}
}