DnD - 为啥拖动的标签显示在其他组件下方?

Posted

技术标签:

【中文标题】DnD - 为啥拖动的标签显示在其他组件下方?【英文标题】:DnD - Why is dragged label displayed below other components?DnD - 为什么拖动的标签显示在其他组件下方? 【发布时间】:2012-11-24 23:22:53 【问题描述】:

我编写了一个基本的 DnD 程序,其中有四个连续的 JLabel。我发觉到 当我向左拖动标签时,它会显示在下一个标签下方。然而, 当我将标签向右拖动时,它会显示在下一个标签的上方。 标签按从左到右的顺序添加。所以拖动的标签正在 显示在拖动标签之前添加的其他标签下方,并显示 在拖动标签之后添加的其他标签之上。 谁能解释为什么会这样?任何人都可以提供修复,以便拖动标签 显示在其他标签上方? 谢谢。

源代码:

public class LabelDnd extends JPanel 

    private JLabel[] labels;
    private Color[] colors =  Color.BLUE, Color.BLACK, Color.RED, Color.MAGENTA ;

    public LabelDnd() 
        super();
        this.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
        this.setBackground(Color.WHITE);
        this.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));

        JPanel basePanel = new JPanel();
        basePanel.setLayout(new GridLayout(1, 4, 4, 4));
        basePanel.setBackground(Color.CYAN);

        MouseAdapter listener = new MouseAdapter() 

            Point p = null;

            @Override
            public void mousePressed(MouseEvent e) 
              p = e.getLocationOnScreen();
            

            @Override
            public void mouseDragged(MouseEvent e) 
              JComponent c = (JComponent) e.getSource();
              Point l = c.getLocation();
              Point here = e.getLocationOnScreen();
              c.setLocation(l.x + here.x - p.x, l.y + here.y - p.y);
              p = here;
            
          ;

        this.labels = new JLabel[4];

        for (int i = 0; i < this.labels.length; i++) 
            this.labels[i] = new JLabel(String.valueOf(i), JLabel.CENTER);
            this.labels[i].setOpaque(true);
            this.labels[i].setPreferredSize(new Dimension(100, 100));
            this.labels[i].setBackground(this.colors[i]);
            this.labels[i].setForeground(Color.WHITE);
            this.labels[i].addMouseListener(listener);
            this.labels[i].addMouseMotionListener(listener);
            basePanel.add(this.labels[i]);
        

        this.add(basePanel);
    

    public static void main(String[] args) 
        SwingUtilities.invokeLater(new Runnable() 
            public void run() 
                JFrame frame = new JFrame("LabelDnd");
                frame.getContentPane().setLayout(new BorderLayout());

                LabelDnd panel = new LabelDnd();

                frame.getContentPane().add(panel, BorderLayout.CENTER);

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            
        );
    

【问题讨论】:

【参考方案1】:

您不会更改代码中添加或绘制标签的顺序。拖动时考虑将 JLabel 提升到玻璃窗格(在将其从主容器中移除之后),然后在释放鼠标时将其重新添加到主容器中。

例如:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.*;
import java.util.Comparator;
import java.util.PriorityQueue;

import javax.swing.*;

@SuppressWarnings("serial")
public class LabelDnd extends JPanel 
   private static final String X_POS = "x position";
   private JLabel[] labels;
   private Color[] colors =  Color.BLUE, Color.BLACK, Color.RED, Color.MAGENTA ;

   public LabelDnd() 
      super();
      // this.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
      this.setBackground(Color.WHITE);
      this.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));

      JPanel basePanel = new JPanel();
      basePanel.setLayout(new GridLayout(1, 4, 4, 4));
      basePanel.setBackground(Color.CYAN);

      MouseAdapter listener = new MouseAdapter() 
         Point loc = null;
         Container parentContainer = null;
         Container glasspane = null;
         JLabel placeHolder = new JLabel("");
         private Point glassLocOnScn;

         @Override
         public void mousePressed(MouseEvent e) 
            JComponent selectedLabel = (JComponent) e.getSource();

            loc = e.getPoint();
            Point currLocOnScn = e.getLocationOnScreen();

            parentContainer = selectedLabel.getParent();

            JRootPane rootPane = SwingUtilities.getRootPane(selectedLabel);
            glasspane = (Container) rootPane.getGlassPane();
            glasspane.setVisible(true);
            glasspane.setLayout(null);
            glassLocOnScn = glasspane.getLocationOnScreen();

            Component[] comps = parentContainer.getComponents();

            // remove all labels from parent
            parentContainer.removeAll();

            // add labels back except for selected one
            for (Component comp : comps) 
               if (comp != selectedLabel) 
                  parentContainer.add(comp);
                else 
                  // add placeholder in place of selected component
                  parentContainer.add(placeHolder);
               
            

            selectedLabel.setLocation(currLocOnScn.x - loc.x - glassLocOnScn.x,
                  currLocOnScn.y - loc.y - glassLocOnScn.y);
            glasspane.add(selectedLabel);
            glasspane.setVisible(true);

            parentContainer.revalidate();
            parentContainer.repaint();

         

         @Override
         public void mouseDragged(MouseEvent e) 
            JComponent selectedLabel = (JComponent) e.getSource();

            glassLocOnScn = glasspane.getLocationOnScreen();
            Point currLocOnScn = e.getLocationOnScreen();
            selectedLabel.setLocation(currLocOnScn.x - loc.x - glassLocOnScn.x,
                  currLocOnScn.y - loc.y - glassLocOnScn.y);

            glasspane.repaint();
         

         @Override
         public void mouseReleased(MouseEvent e) 
            JComponent selectedLabel = (JComponent) e.getSource();

            if (parentContainer == null || glasspane == null) 
               return;
            

            // sort the labels based on their x position on screen
            PriorityQueue<JComponent> compQueue = new PriorityQueue<JComponent>(
                  4, new Comparator<JComponent>() 

                     @Override
                     public int compare(JComponent o1, JComponent o2) 
                        // sort of a kludge -- checking a client property that
                        // holds the x-position 
                        Integer i1 = (Integer) o1.getClientProperty(X_POS);
                        Integer i2 = (Integer) o2.getClientProperty(X_POS);
                        return i1.compareTo(i2);
                     
                  );

            // sort of a kludge -- putting x position before removing component
            // into a client property to associate it with the JLabel
            selectedLabel.putClientProperty(X_POS, selectedLabel.getLocationOnScreen().x);
            glasspane.remove(selectedLabel);
            compQueue.add(selectedLabel);
            Component[] comps = parentContainer.getComponents();
            for (Component comp : comps) 
               JLabel label = (JLabel) comp;
               if (!label.getText().trim().isEmpty())  // if placeholder!
                  label.putClientProperty(X_POS, label.getLocationOnScreen().x);
                  compQueue.add(label); // add to queue
               
            
            parentContainer.removeAll();

            // add back labels sorted by x-position on screen
            while (compQueue.size() > 0) 
               parentContainer.add(compQueue.remove());
            

            parentContainer.revalidate();
            parentContainer.repaint();
            glasspane.repaint();

         
      ;

      this.labels = new JLabel[4];

      for (int i = 0; i < this.labels.length; i++) 
         this.labels[i] = new JLabel(String.valueOf(i), JLabel.CENTER);
         this.labels[i].setOpaque(true);
         this.labels[i].setPreferredSize(new Dimension(100, 100));
         this.labels[i].setBackground(this.colors[i]);
         this.labels[i].setForeground(Color.WHITE);
         this.labels[i].addMouseListener(listener);
         this.labels[i].addMouseMotionListener(listener);
         basePanel.add(this.labels[i]);
      

      this.add(basePanel);
   

   public static void main(String[] args) 
      SwingUtilities.invokeLater(new Runnable() 
         public void run() 
            JFrame frame = new JFrame("LabelDnd");
            frame.getContentPane().setLayout(new BorderLayout());

            LabelDnd panel = new LabelDnd();

            frame.getContentPane().add(panel, BorderLayout.CENTER);

            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
         
      );
   

【讨论】:

【参考方案2】:

因为你的 Swing 组件的z-index。您应该在拖动后使用setComponentZOrder 更改组件的z-index 值。

请查看this链接了解更多详情

【讨论】:

以上是关于DnD - 为啥拖动的标签显示在其他组件下方?的主要内容,如果未能解决你的问题,请参考以下文章

react-dnd使用介绍

react-dnd 使用

react-beautiful-dnd:当存在多个列表时,在一行中拖动内容会从所有行中拖动相同的索引

自定义组件和标签顺序

React美丽的DnD拖出位置问题

HTML5 DnD - 为啥在拖放元素后重影图像只能按预期工作?