0

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);
}





}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
ThePrince
  • 818
  • 2
  • 12
  • 26
  • See: https://stackoverflow.com/questions/67630144/java-get-rgb-value-of-a-pixel-in-icon/67630212#67630212. You got an answer to your last question and apparently didn't read the suggestion since you still haven't "accepted" the answer or commented why the suggestion didn't help. So I'll skip this one. I suspect the solution would be a single statement but you need to post a [mre] to demonstrate the problem. – camickr May 22 '21 at 04:30

1 Answers1

1

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.

  1. 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.

  2. 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);
}


}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • When I click and drag the image it gets brought up to the front. I want to keep it in the back while I manipulate it - but Swing always moves it to the front. Same happens with another component - as soon as I start manipulating it, it gets brought to the front and I don't want it to do that. I want to programatically keep it at the back to avoid this happening. – ThePrince May 22 '21 at 23:29
  • Hello, I tried out the answer, and while intuitive it is not working for me. I have edited my original question to include a runnable example. As you can see the bottom button is consistently being added to the forefront despite trying to keep it at the bottom. – ThePrince May 23 '21 at 03:29
  • @ThePrince, This is the reason why an [mre] should be provided with every question. You stated you were trying to display a "background image". Typically people use a JLabel to display a background image. A JButton behaves differently that a JLabel because of the rollover effects of the button, so you need to add additional logic. See edit. – camickr May 23 '21 at 14:06
  • Yes, I was working with a JLabel with an image. It contained a DragAndDrop functionality which was a bit longer in the code. I incorrectly assumed that it would behave the same way, since I had a whole custom class managing the drag and drop functionality. Thank you for the example, I will test this. – ThePrince May 23 '21 at 19:05