JLayeredPanel布局管理器自由移动对象 [英] JLayeredPanel layout manager free moving objects

查看:193
本文介绍了JLayeredPanel布局管理器自由移动对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个游戏板,有8个我希望能够将它们移动到jpanel上的任何位置。目前,我只能进行流量或网格布局,但这不会产生预期的结果。长期目标是能够独立地点击一块并将其拖动到所需的位置/位置。 (包括在其他部分之上)

I have a game board, with 8 pieces that I would like to be able to move them anywhere on the jpanel. Currently, I can only do flow or grid layout, however this does not yield the desired results. The long term goal, is to be able to independently click on a piece and drag it to desired location/position. (including on top of other pieces)

任何建议或输入都是最受欢迎的...(感谢Hovercraft Full Of Eels,MadProgrammer,peeskillet,Andrew Thompson为他们的早先的建议)....

Any suggestions or input would be most welcome... (Thanks to Hovercraft Full Of Eels, MadProgrammer, peeskillet, Andrew Thompson for their earlier suggestions)....

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class FireflyGameBoard extends JFrame implements MouseListener,
    MouseMotionListener
{
  JLayeredPane layeredPane;
  JPanel gameBoard;
  // JPanel background;
  JLabel gamePiece;
  int xAdjustment;
  int yAdjustment;
  ImageIcon bgicon;
  Image bg;
  String Rimagepath = "/Users/yournamehere/Documents/workspace/MotionGraphicsTest/resources/";

  public FireflyGameBoard()
  {
    Dimension boardSize = new Dimension(1920, 1080);

    bgicon = new ImageIcon(Rimagepath + "Backdroptest.png");

    bg = bgicon.getImage();

    ImagePanel background = new ImagePanel(new ImageIcon(Rimagepath
        + "Backdroptest.png").getImage());

    // Use a Layered Pane for this this application
    layeredPane = new JLayeredPane();
    getContentPane().add(layeredPane);
    layeredPane.setPreferredSize(boardSize);
    layeredPane.addMouseListener(this);
    layeredPane.addMouseMotionListener(this);

    // Add a chess board to the Layered Pane

    gameBoard = new ImagePanel(bg);
    layeredPane.add(background, JLayeredPane.DEFAULT_LAYER);
    layeredPane.add(gameBoard, JLayeredPane.MODAL_LAYER);
    gameBoard.setLayout(new FlowLayout());
    gameBoard.setPreferredSize(boardSize);
    gameBoard.setBounds(0, 0, boardSize.width, boardSize.height);

    // for (int i = 0; i < 64; i++)
    // {
    // JPanel square = new JPanel(new BorderLayout());
    // gameBoard.add(square);
    //
    // // square.setBackground(null);
    // }

    // Add a few pieces to the board

    JLabel piece = new JLabel(new ImageIcon(Rimagepath + "alliance.png"));
    gameBoard.add(piece);
    piece = new JLabel(new ImageIcon(Rimagepath + "piece2.png"));
    gameBoard.add(piece);
    piece = new JLabel(new ImageIcon(Rimagepath + "piece3.png"));
    gameBoard.add(piece);
    piece = new JLabel(new ImageIcon(Rimagepath + "piece4.png"));
    gameBoard.add(piece);
    piece = new JLabel(new ImageIcon(Rimagepath + "reaper.png"));
    gameBoard.add(piece);
    piece = new JLabel(new ImageIcon(Rimagepath + "piece6.png"));
    gameBoard.add(piece);
    piece = new JLabel(new ImageIcon(Rimagepath + "piece7.png"));
    gameBoard.add(piece);
    piece = new JLabel(new ImageIcon(Rimagepath + "piece8.png"));
    gameBoard.add(piece);
  }

  @Override
  public void mousePressed(MouseEvent e)
  {
    gamePiece = null;
    Component c = gameBoard.findComponentAt(e.getX(), e.getY());

    if (c instanceof JPanel)
      return;

    Point parentLocation = c.getParent().getLocation();
    xAdjustment = parentLocation.x - e.getX();
    yAdjustment = parentLocation.y - e.getY();
    gamePiece = (JLabel) c;
    gamePiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
    gamePiece.setSize(gamePiece.getWidth(), gamePiece.getHeight());
    layeredPane.add(gamePiece, JLayeredPane.DRAG_LAYER);
  }

  // Move the chess piece around

  @Override
  public void mouseDragged(MouseEvent me)
  {
    if (gamePiece == null)
      return;
    gamePiece.setLocation(me.getX() + xAdjustment, me.getY() + yAdjustment);
  }

  // Drop the chess piece back onto the chess board

  @Override
  public void mouseReleased(MouseEvent e)
  {
    if (gamePiece == null)
      return;

    gamePiece.setVisible(false);
    Component c = gameBoard.findComponentAt(e.getX(), e.getY());

    if (c instanceof JLabel)
    {
      Container parent = c.getParent();
      parent.remove(0);
      parent.add(gamePiece);
    }
    else
    {
      Container parent = (Container) c;
      parent.add(gamePiece);
    }

    gamePiece.setVisible(true);
  }

  @Override
  public void mouseClicked(MouseEvent e)
  {

  }

  @Override
  public void mouseMoved(MouseEvent e)
  {
  }

  @Override
  public void mouseEntered(MouseEvent e)
  {

  }

  @Override
  public void mouseExited(MouseEvent e)
  {

  }

  public static void main(String[] args)
  {
    JFrame frame = new FireflyGameBoard();
    frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    frame.pack();
    frame.setResizable(true);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

P.S。这不是学校作业,我的g / f是一个狂热的棋盘游戏玩家,我想让她成为她最喜欢的一些数字版本....

P.S. This is not a school assignment, my g/f is an avid board game player, and I wanted to make her a digital version of some of her favorites....

推荐答案

Drag'n'Drop是一项严肃的工作。如果做得好,它可能真的很棒,但要为一些严肃的繁重和设计工作做好准备...

Drag'n'Drop is some serious work. When done right it can be really awesome, but be prepared for some serious heavy lifting and design work...

一种方法是尝试生成自包含的工作单元,即该部分负责管理它自己的拖动,并且单元格/网格负责管理掉落。

One approach is to try an generate self contained units of work, that is the piece is responsible for managing it's own drag and the cell/grid is responsible for managing the drop.

A Piece 是一个可移动的游戏片,可以拖到新的位置。

A Piece is a movable game piece, which can be dragged to a new location.

该片本身负责管理 DragGestureRecognizer ,用于初始化拖动过程......

The piece itself is responsible for managing the DragGestureRecognizer which is used to initialise the drag process...

因为我想在片中显示一个图标,所以我选择覆盖 JLabel ,因为它为此提供了核心功能......

Because I wanted to display an icon in the piece, I choose to override JLabel, as it provides the core functionality for this...

import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.image.BufferedImage;
import javax.swing.JLabel;

public class PieceLabel extends JLabel {
    private DragGestureHandler dragGestureHandler;
    private DragGestureRecognizer dgr;

    public PieceLabel() {
        setHorizontalAlignment(CENTER);
        setVerticalAlignment(CENTER);
    }

    @Override
    public void addNotify() {
        super.addNotify();
        if (dgr == null) {
            dragGestureHandler = new DragGestureHandler(this);
            dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, dragGestureHandler);
        }
    }

    @Override
    public void removeNotify() {
        if (dgr != null) {
            dgr.removeDragGestureListener(dragGestureHandler);
            dragGestureHandler = null;
        }
        dgr = null;
        super.removeNotify();
    }

    public static class DragGestureHandler implements DragGestureListener, DragSourceListener {

        private PieceLabel piece;
        private Container parent;

        public DragGestureHandler(PieceLabel child) {
            this.piece = child;
        }

        public PieceLabel getPiece() {
            return piece;
        }

        protected void setParent(Container parent) {
            this.parent = parent;
        }

        protected Container getParent() {
            return parent;
        }

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) {
            // When the drag begins, we need to grab a reference to the
            // parent container so we can return it if the drop
            // is rejected
            Container parent = getPiece().getParent();
            setParent(parent);
            // Remove the panel from the parent.  If we don't do this, it
            // can cause serialization issues.  We could over come this
            // by allowing the drop target to remove the component, but that's
            // an argument for another day
            parent.remove(getPiece());
            // Update the display
            parent.invalidate();
            parent.repaint();
            // Create our transferable wrapper
            Transferable transferable = new PieceTransferable(getPiece());
            // Start the "drag" process...
            DragSource ds = dge.getDragSource();
            //            ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), transferable, this);
            BufferedImage image = BoardDrag.createBufferedImage(piece.getIcon(), piece);
            Point pp = piece.getLocation();
            Point dp = dge.getDragOrigin();
            int x = image.getWidth() / 2;
            int y = image.getHeight() / 2;
            ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), image, new Point(x, y), transferable, this);
        }

        @Override
        public void dragEnter(DragSourceDragEvent dsde) {
        }

        @Override
        public void dragOver(DragSourceDragEvent dsde) {
        }

        @Override
        public void dropActionChanged(DragSourceDragEvent dsde) {
        }

        @Override
        public void dragExit(DragSourceEvent dse) {
        }

        @Override
        public void dragDropEnd(DragSourceDropEvent dsde) {
            // If the drop was not sucessful, we need to
            // return the component back to it's previous
            // parent
            if (!dsde.getDropSuccess()) {
                getParent().add(getPiece());
                getParent().invalidate();
                getParent().repaint();
            }
        }
    }

}



Drop / Cell / Grid



单元/网格就是这样,它构成了整个网格/板上的单个元素。它可以包含一个 Piece (事实上,你可以很容易地配置它拒绝其他一切)

Drop/Cell/Grid

A cell/grid is just that, it makes up a single element within the over all grid/board. It can contain a Piece (and in fact, you could easily configure it do reject everything else)

这个管理 DropTarget ,负责检测什么东西掉在上面......

This manages the DropTarget, which is responsible for detecting when something is dropped onto it...

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import javax.swing.JComponent;
import javax.swing.JPanel;

public class Cell extends JPanel {
    private DropTarget dropTarget;
    private DropHandler dropHandler;

    public Cell() {
        setLayout(new BorderLayout());
    }

    @Override
    public void addNotify() {
        super.addNotify();
        if (dropHandler == null) {
            dropHandler = new DropHandler();
        }
        if (dropTarget == null) {
            dropTarget = new DropTarget(this, DnDConstants.ACTION_MOVE, dropHandler, true);
        }
    }

    @Override
    public void removeNotify() {
        if (dropTarget != null) {
            dropTarget.removeDropTargetListener(dropHandler);
        }
        dropTarget = null;
        dropHandler = null;
        super.removeNotify();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(48, 48);
    }

    public class DropHandler implements DropTargetListener {

        @Override
        public void dragEnter(DropTargetDragEvent dtde) {
            // Determine if can actual process the contents comming in.
            // You could try and inspect the transferable as well, but
            // There is an issue on the MacOS under some circumstances
            // where it does not actually bundle the data until you accept the
            // drop.
            if (dtde.isDataFlavorSupported(PieceDataFlavor.SHARED_INSTANCE)) {
                dtde.acceptDrag(DnDConstants.ACTION_MOVE);
            } else {
                dtde.rejectDrag();
            }
        }

        @Override
        public void dragOver(DropTargetDragEvent dtde) {
        }

        @Override
        public void dropActionChanged(DropTargetDragEvent dtde) {
        }

        @Override
        public void dragExit(DropTargetEvent dte) {
        }

        @Override
        public void drop(DropTargetDropEvent dtde) {
            boolean success = false;
            // Basically, we want to unwrap the present...
            if (dtde.isDataFlavorSupported(PieceDataFlavor.SHARED_INSTANCE)) {
                Transferable transferable = dtde.getTransferable();
                try {
                    Object data = transferable.getTransferData(PieceDataFlavor.SHARED_INSTANCE);
                    if (data instanceof PieceLabel) {
                        PieceLabel piece = (PieceLabel) data;
                        DropTargetContext dtc = dtde.getDropTargetContext();
                        Component component = dtc.getComponent();
                        if (component instanceof JComponent) {
                            Container parent = piece.getParent();
                            if (parent != null) {
                                parent.remove(piece);
                            }
                            ((JComponent) component).add(piece);
                            success = true;
                            dtde.acceptDrop(DnDConstants.ACTION_MOVE);
                            invalidate();
                            repaint();
                        } else {
                            success = false;
                            dtde.rejectDrop();
                        }
                    } else {
                        success = false;
                        dtde.rejectDrop();
                    }
                } catch (Exception exp) {
                    success = false;
                    dtde.rejectDrop();
                    exp.printStackTrace();
                }
            } else {
                success = false;
                dtde.rejectDrop();
            }
            dtde.dropComplete(success);
        }
    }

}



胶水



在Drag'n'Drop中,有两个特殊的类将拖拽粘合到拖放....

The Glue

In Drag'n'Drop, there are two special classes which glue the drag to the drop....

DataFlavor 负责提供一种方法,通过这种方法,断开连接的元素不仅可以确定转移的内容,还可以确定如何重构数据...

The DataFlavor is responsible for providing a means by which disconnected elements can determine not only what is been transferred, but how that data should be reconstituted...

为简单起见,我只是 PieceLabel.class

import java.awt.datatransfer.DataFlavor;

public class PieceDataFlavor extends DataFlavor {
    public static final PieceDataFlavor SHARED_INSTANCE = new PieceDataFlavor();

    public PieceDataFlavor() {
        super(PieceLabel.class, null);
    }

}



可转让



可转让是一个包装类,它允许从一个数据移动例如,位置到另一个,比如剪贴板。

The Transferable

The Transferable is a wrapper class which allows data to be moved from one location to another, like the clipboard, for example.

这个例子相当简单,但你可以想象一个 Transferable 可能包含多个 DataFlavor s,具体取决于您想要的 DataFlavor 可以更改类型(或其中的方式)你得到的数据。

This example is reasonably simple, but you could imagine that a Transferable might contain multiple DataFlavors, depending on which DataFlavor you want could change the type (or the manner in which you get) the data.

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

public class PieceTransferable implements Transferable {
    private final DataFlavor[] flavors = new DataFlavor[]{PieceDataFlavor.SHARED_INSTANCE};
    private final PieceLabel piece;

    public PieceTransferable(PieceLabel piece) {
        this.piece = piece;
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return flavors;
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        // Okay, for this example, this is over kill, but makes it easier
        // to add new flavor support by subclassing
        boolean supported = false;
        for (DataFlavor mine : getTransferDataFlavors()) {
            if (mine.equals(flavor)) {
                supported = true;
                break;
            }
        }
        return supported;
    }

    public PieceLabel getPanel() {
        return piece;
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        Object data = null;
        if (isDataFlavorSupported(flavor)) {
            data = getPanel();
        } else {
            throw new UnsupportedFlavorException(flavor);
        }
        return data;
    }

}



全部放在一起



因为组件是自包含的,所以把它放在一起实际上非常简单......它们基本上都是自己照顾...

Putting it all together

Because the components are self contained, putting it together is actually really easy...they basically take care of themselves...

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

public class BoardPane extends JPanel {

    public BoardPane() {
        setLayout(new GridLayout(8, 8));
        int index = 0;
        for (int row = 0; row < 8; row++) {
            for (int col = 0; col < 8; col++) {
                Cell cell = new Cell();
                if (index % 2 == 0) {
                    cell.setBackground(Color.WHITE);
                } else {
                    cell.setBackground(Color.BLACK);
                }
                add(cell);
                index++;
            }
            index++;
        }
        try {
            PieceLabel label = new PieceLabel();
            BufferedImage image = ImageIO.read(getClass().getResource("/Piece01.png"));
            label.setIcon(new ImageIcon(image));
            setCellPiece(label, 0, 0);
        } catch (IOException ex) {
            Logger.getLogger(BoardDrag.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void setCellPiece(PieceLabel label, int row, int col) {
        int index = (row * 8) + col;
        Cell cell = (Cell) getComponent(index);
        cell.removeAll();
        cell.add(label);
    }

}



有什么收获?



这是对的,你没有得到任何免费的东西。

What's the catch?

That's right, you don't get everything of free.

你必须实现确定是否所需的逻辑移动是否有效。逻辑应该在主动拒绝阻力的情况下实施。这可能需要您向 Transferable 添加更多信息,以便您可以确定起始单元格。

You will have to implement the logic required to determine if a move is valid or not. The logic should be implemented in such away as to actively reject the drag. This might require you to add more information to the Transferable so you can determine the start cell, for example.

我个人希望实现某种规则引擎,你的DnD API可以使用它来使它变得可插拔

I'd personally be looking to implement some kind of "rules" engine, which can be used by your DnD API so that it becomes pluggable

这篇关于JLayeredPanel布局管理器自由移动对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆