java Swing:创建一个可拖动的组件......?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/874360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 14:10:32  来源:igfitidea点击:

Swing: Creating a draggable component...?

javaswingdrag-and-dropdraggable

提问by ivan_ivanovich_ivanoff

I searched the web for examples of draggable Swing components, but I found either incomplete or non-working examples.

我在网上搜索了可拖动 Swing 组件的示例,但我发现了不完整或无法工作的示例。

What I need is a Swing componentthat can be draggedby the mouse inside an other component. While being dragged, it should already changeits position, not just 'jump' to its destination.

我需要的是一个可以通过鼠标拖动到其他组件内的Swing组件。在被拖动时,它应该已经改变了它的位置,而不仅仅是“跳”到它的目的地。

I would appreciate examples which work without non-standard APIs.

我会欣赏没有非标准 API 的示例。

Thank you.

谢谢你。

回答by ivan_ivanovich_ivanoff

I propose a simple, but well-working solution, found out by myself ;)

我提出了一个简单但有效的解决方案,由我自己发现;)

What do I do?

我该怎么办?

  • When mouse is pressed, I record the cursor'sposition on screen, and the component'sposition.
  • When mouse is dragged, I calculate the differencebetween new and old cursor'sposition on screen, and move the component by this difference.
  • 按下鼠标时,我会记录光标在屏幕上位置以及组件的位置。
  • 当拖动鼠标,我计算出的差异新旧之间的光标的位置在屏幕上,并移动部件由这种差异

Tested with latest JDK 6 unter Linux (OpenSuse, KDE3),
but hey, it's Java Swing, should work equally everywhere.

在 Linux(OpenSuse、KDE3)下使用最新的 JDK 6 进行了测试,
但是嘿,它是 Java Swing,应该在任何地方都可以正常工作。

Here goes the code:

代码如下:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class MyDraggableComponent
    extends JComponent {

  private volatile int screenX = 0;
  private volatile int screenY = 0;
  private volatile int myX = 0;
  private volatile int myY = 0;

  public MyDraggableComponent() {
    setBorder(new LineBorder(Color.BLUE, 3));
    setBackground(Color.WHITE);
    setBounds(0, 0, 100, 100);
    setOpaque(false);

    addMouseListener(new MouseListener() {

      @Override
      public void mouseClicked(MouseEvent e) { }

      @Override
      public void mousePressed(MouseEvent e) {
        screenX = e.getXOnScreen();
        screenY = e.getYOnScreen();

        myX = getX();
        myY = getY();
      }

      @Override
      public void mouseReleased(MouseEvent e) { }

      @Override
      public void mouseEntered(MouseEvent e) { }

      @Override
      public void mouseExited(MouseEvent e) { }

    });
    addMouseMotionListener(new MouseMotionListener() {

      @Override
      public void mouseDragged(MouseEvent e) {
        int deltaX = e.getXOnScreen() - screenX;
        int deltaY = e.getYOnScreen() - screenY;

        setLocation(myX + deltaX, myY + deltaY);
      }

      @Override
      public void mouseMoved(MouseEvent e) { }

    });
  }

}

public class Main {

  public static void main(String[] args) {
    JFrame f = new JFrame("Swing Hello World");

    // by doing this, we prevent Swing from resizing
    // our nice component
    f.setLayout(null);

    MyDraggableComponent mc = new MyDraggableComponent();
    f.add(mc);

    f.setSize(500, 500);

    f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    f.setVisible(true);
  }

}

回答by Stefan

Here is one more approach you might want to try. I think its quite clean. Just copy the following class and use it like this:

这是您可能想尝试的另一种方法。我认为它很干净。只需复制以下类并像这样使用它:

Usage:

用法:

DragListener dl = new DragListener(componentOrWindowToBeMoved); dl.addHandle(componentToPickWithTheMouse);

DragListener dl = new DragListener(componentOrWindowToBeMoved); dl.addHandle(componentToPickWithTheMouse);

Class:

班级:

import java.awt.Component;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class DragListener extends MouseAdapter {

    private final Component COMPONENT_TO_DRAG;
    private final int MOUSE_BUTTON;
    private Point mousePosition;
    private Point sourceLocation;
    private Point locationOnScreen;
    private int buttonPressed;

    public DragListener(final Component componentToDrag) {
         this(componentToDrag, MouseEvent.BUTTON1);
    }

    public DragListener(final Component componentToDrag, final int mouseButton)                         {
        this.COMPONENT_TO_DRAG = componentToDrag;
        this.MOUSE_BUTTON = mouseButton;
    }

    @Override
    public void mousePressed(final MouseEvent e) {
        this.buttonPressed = e.getButton();
        this.mousePosition = MouseInfo.getPointerInfo().getLocation();
        this.sourceLocation = new Point();
    }

    @Override
    public void mouseDragged(final MouseEvent e) {
        if (this.buttonPressed == MOUSE_BUTTON) {
            this.locationOnScreen = e.getLocationOnScreen();
            this.sourceLocation = this.COMPONENT_TO_DRAG.getLocation(this.sourceLocation);
            this.sourceLocation.translate((int) (this.locationOnScreen.getX() - this.mousePosition.getX()), (int) (this.locationOnScreen.getY() - this.mousePosition.getY()));
            this.COMPONENT_TO_DRAG.setLocation(this.sourceLocation);
            this.mousePosition = MouseInfo.getPointerInfo().getLocation();
        }
    }

    public void addHandle(Component handle) {
        handle.addMouseListener(this);
        handle.addMouseMotionListener(this);
    }
}

回答by ivan_ivanovich_ivanoff

Also, I found out that one could create an JInternalFrameinside an JFrame, but the problem is: you get always an annoying window title bar.

此外,我发现可以在 JFrame 中创建JInternalFrame,但问题是:您总是会得到一个烦人的窗口标题栏

To disable the title bar, sadly, a dirty workaroundis necessary:

要禁用标题栏,遗憾的是,需要一个肮脏的解决方法

public class MyDraggableComponent extends JInternalFrame {

  public MyDraggableComponent() {
    InternalFrameUI thisUI = getUI();
    if (thisUI instanceof BasicInternalFrameUI) {
      ((BasicInternalFrameUI) thisUI).setNorthPane(null);
    }

}

I really miss a method like "someInternalFrame.setWindowTitleBar(false)"...
:'(

我真的很想念像“someInternalFrame.setWindowTitleBar(false)”这样的方法......
:'(