1

我是一位经验丰富的 Java 开发人员,但我是用户界面开发的新手,我一直在使用Java AWT开发应用程序。我想将拖动添加到用户界面中。

令我惊讶的是,ajava.awt.Shape不是 a java.awt.Component,因此我看不到将可拖动钩子添加到我的特定形状的“一流”方式。相反,我使用应用程序窗口的鼠标事件挂钩,然后在处理任何拖动之前简单地检测鼠标光标何时位于感兴趣的形状上。

由于我的应用程序不需要复杂的 Z 轴图层,因此我限制任何拖动的形状与另一个可拖动的形状相交。如果用户将一个拖到另一个上,它只会“碰撞”并保持在那里。然后,用户首先将其从碰撞中解放出来,然后将其放置在其他地方。您会注意到,在我为 Stackoverflow 留下评论的 paint() 方法中检测到了交集。

用户界面有效。除此之外,很容易产生一种竞争条件,如果我非常快速地拖动形状,它可能会深深地嵌套到另一个形状中并卡住。

问题:我是否以完全错误的方式来解决这个问题?是否有一种优雅的最佳实践可以在进一步事件发生之前“及时”检测到碰撞?

package test;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.ScrollPane;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.RoundRectangle2D;

/**
 * Simple graphical application with two white rectangles that can be dragged around the application window
 * */
final class DraggableTest extends Component implements MouseListener, MouseMotionListener {

    public static void main( String[] args ) {
        
        Frame window = new Frame( "Test Dragging Things" );
        ScrollPane scrollPane = new ScrollPane();
        DraggableTest app = new DraggableTest();
        scrollPane.add( app );
        window.add( scrollPane );
        
        app.setSize( new Dimension( 400, 400 ) );
        app.setPreferredSize( new Dimension( 400, 400 ) );
        window.setSize( 200, 200 );
        window.setVisible( true );

        window.addWindowListener( new WindowAdapter() {
            @Override
            public void windowClosing( WindowEvent e ) {
                window.dispose();
            }
        } );
        app.addMouseMotionListener( app );
        app.addMouseListener( app );
        
    }
    
    private static final long serialVersionUID = 2673458042104218875L;
    
    @Override
    public void paint( Graphics g ) {
        Graphics2D g2 = ( Graphics2D ) g;

        // redraw the draggable shapes in their last dragged position
        draggableShape1 = new RoundRectangle2D.Double( lastDragged1.x, lastDragged1.y, 40, 20, 10, 10 );
        g2.setPaint( Color.WHITE );
        g2.fill( draggableShape1 );
        g2.setPaint( Color.BLACK );
        g2.setStroke( new BasicStroke() );
        g2.draw( draggableShape1 );
        
        draggableShape2 = new RoundRectangle2D.Double( lastDragged2.x, lastDragged2.y, 40, 20, 10, 10 );
        g2.setPaint( Color.WHITE );
        g2.fill( draggableShape2 );
        g2.setPaint( Color.BLACK );
        g2.setStroke( new BasicStroke( 2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 20.0f, new float[] { 10.0f / 2.0f }, 0.0f ) );
        g2.draw( draggableShape2 );
        
        // HERE IS WHERE MY STACKOVERFLOW QUESTION LIES
        // Cancel any drag operation if a dragged object begins to intersect another. 
        // Because then we would have a requirement for differentiating and specifying which object to drag.
        synchronized (this) {
            if ( draggableShape1.getBounds2D().intersects( draggableShape2.getBounds2D() ) ) {
                dragging1 = false;
                dragging2 = false;
                Toolkit.getDefaultToolkit().beep(); // alert the user to termination of the drag
            }
        }
    }

    // Two shapes drawn in method paint()
    private Shape draggableShape1 = null;
    private Shape draggableShape2 = null;
    
    // initial locations of draggable shapes
    private Point initialPosition1 = new Point( 0, 0 );
    private Point initialPosition2 = new Point( 100, 100 );
    
    // position shape last dropped to
    private Point lastDragged1 = initialPosition1;
    private Point lastDragged2 = initialPosition2;
    
    // is the shape being dragged now?
    boolean dragging1 = false;
    boolean dragging2 = false;
    
    // During drag, offset between mouse click position and the object being dragged
    int dragMouseOffsetX = 0;
    int dragMouseOffsetY = 0;
    
    // Drag event sequence: 
    //      Mouse button down.
    //      Mouse dragged to 1204, 452
    //      (repaint called from method mouseDragged )
    //      Mouse button released.

    @Override 
    public void mousePressed(MouseEvent e) {
        System.err.println( "Mouse button down." );
        Point mouseDownPosition = e.getPoint();
        
        // if mouse down in a draggable shape, assume we are starting a drag
        if ( draggableShape1.contains( e.getPoint() ) ) { 
            dragging1 = true;
            // Detect the offset between the mouse position and the shape's top left point
            dragMouseOffsetX = draggableShape1.getBounds().x - mouseDownPosition.x; // negative is left
            dragMouseOffsetY = draggableShape1.getBounds().y - mouseDownPosition.y; // negative is up
        }
        
        if ( draggableShape2.contains( e.getPoint() ) ) {
            dragging2 = true;
            // Detect the offset between the mouse position and the shape's top left point
            dragMouseOffsetX = draggableShape2.getBounds().x - mouseDownPosition.x; // negative is left
            dragMouseOffsetY = draggableShape2.getBounds().y - mouseDownPosition.y; // negative is up
        }
    }
    
    /**
     * The mouse button is down and is being moved.
     * This has an effect IFF one of the draggable items was under the pointer when the button was pressed
     * */
    @Override
    public void mouseDragged( MouseEvent e ) {
        Point mouseDragPosition = e.getPoint();
        
        if ( dragging1 || dragging2 ) {
            System.err.println( "Mouse dragged to " + mouseDragPosition.x + ", " + mouseDragPosition.y );
            System.err.println( "Shape dragged to " + ( mouseDragPosition.x + dragMouseOffsetX ) + ", " + ( mouseDragPosition.y + dragMouseOffsetY ) );
        }
        
        if ( dragging1 ) { // if we previously clicked on the shape
            if ( ! mouseDragPosition.equals( lastDragged1 ) ) { // Ignore drag of less than one pixel (measured in granularity of int)
                Point newDragPoint = new Point( mouseDragPosition.x + dragMouseOffsetX, mouseDragPosition.y + dragMouseOffsetY );
                lastDragged1 = newDragPoint;
                repaint();
            }
        }
        
        if ( dragging2 ) { // if we previously clicked on the shape
            if ( ! mouseDragPosition.equals( lastDragged2 ) ) { // Ignore drag of less than one pixel (measured in granularity of int)
                lastDragged2 = new Point( mouseDragPosition.x + dragMouseOffsetX, mouseDragPosition.y + dragMouseOffsetY );
                repaint();
            }
        }
    }
    
    /**
     * Any time the mouse button comes up, any dragging state is concluded
     * */
    @Override 
    public void mouseReleased(MouseEvent e) {
        System.err.println( "Mouse button released." );
        dragging1 = false;
        dragging2 = false;
    }

    @Override
    public void mouseMoved( MouseEvent e ) {
        // nothing to do
    }
    
    /**
     * The mouse enters or re-enters the application window
     * */
    @Override 
    public void mouseEntered(MouseEvent e) {
        System.err.println( "Mouse entered." );
    }
    
    /**
     * The mouse leaves the application window
     * */
    @Override 
    public void mouseExited(MouseEvent e) {
        System.err.println( "Mouse left." );
    }
     
    /**
     * A combination of mouse down and mouse up has been detected as a click.
     * Not implemented.
     * */
    @Override 
    public void mouseClicked(MouseEvent e) {
        //  Event sequence: 
        //      Mouse button down.
        //      Mouse button released.
        //      Click.
        System.err.println( "Click." );
    }
}
4

0 回答 0