不使用 JLayeredPane 将 jcomponent 向后移动

问题描述

我编写了一个 DragAndDrop MouseListener。

组件 A 是组件 B 后面的“背景”图像。两者都位于 JPanel 上。

我已使图像可拖动。但是,我希望图像在拖动时保持在组件 B 后面。

但是,每次我拖动图像时,我想 Java 都会给它焦点或其他东西,所以它会被带到最前面。

有没有一种方法可以在我拖动图像时将图像保留在后面?

我知道我可以使用 JlayeredPane 并在每次拖动时使用 movetoBack 方法,但我宁愿不使用 JlayeredPane 而只使用 JPanel。 JPanel 是否有 movetoBack 等价物?

或者有没有办法让组件保留当前层(可能“不获得焦点”),以便我可以将它拖到当前层内?

这是一个例子

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,100,100);
    top.setBounds(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);
}





}

解决方法

组件 A 是组件 B 后面的“背景”图像。两者都位于 JPanel 上。

Swing 组件是根据它们的 ZOrder 绘制的。首先绘制最高的 ZOrder

ZOrder 被分配为组件添加到面板中。因此,添加的第一个组件为 ZOrder 0,第二个组件为 ZOrder 1

因此,最后将您的“背景”图像添加到面板中,它始终具有最高的 ZOrder,这意味着它首先被绘制,因此其他组件将被绘制在它之上。

例如:

panel.add(someOtherComponent);
panel.add(background);

或者您可以使用:

Container.setComponentZOrder(...)

在添加组件后动态更改 ZOrder 的方法。

编辑:

我提供的答案是你是一个非此即彼的解决方案。您只需选择一种方法。

  1. 您正确实现了第一种方法,即最后添加“背景”组件。所以没有必要再玩 ZOrder。当 ZOrder 保持固定时,这种方法非常有效。

  2. 第二种方法是当您希望 ZOrder 动态更改时,例如当您有多个组件并且您希望在与每个组件交互时更改它时。但是,您错误地实施了这种方法。您为 ZOrder 使用了“0”,这意味着该组件将始终最后绘制,因此位于任何其他组件之上。

在您当前的示例中,无需动态更改 ZOrder,因此您可以删除所有相关代码。

如您所见,底部按钮一直被添加到最前面

Swing 绘画经过优化,假设组件不重叠。

但是,在 JButton 的情况下,当您将鼠标悬停在按钮上以重新绘制边框时,它具有自动重新绘制逻辑。所以当按钮被重新绘制时,它仍然会绘制在另一个组件的顶部。

因此您需要告诉 Swing 不要优化绘画。因此,当重绘一个组件时,它们将在重叠时全部重绘。您可以通过覆盖 isOptimizedDrawingEnabled(...) 方法来实现,如下所示:

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,100,100);
    top.setBounds(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);
}


}