Swing example. I made the letters as separate components and used the JLayeredPane
to draw the draggable letter on top of all the other components. The main work is done by the class DragAdapter
.
import java.awt.*; import java.awt.event.*; import java.awt.geom.Rectangle2D; import javax.swing.*; public class LetterDrop { static class GamePanel { JLayeredPane pane; /** ΠΏΠ°Π½Π΅Π»Ρ Π΄Π»Ρ Π±Π°Π·ΠΎΠ²ΠΎΠ³ΠΎ ΡΠ»ΠΎΡ */ JPanel baseLayer; /** ΠΏΠΎΠ»Π΅ */ Field field; /** ΠΏΠ°Π½Π΅Π»Ρ Ρ Π±ΡΠΊΠ²Π°ΠΌΠΈ Π΄Π»Ρ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°Π½ΠΈΡ */ JPanel letterBank; public GamePanel() { // ΡΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΈ ΡΠ°Π·ΠΌΠ΅ΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² field = createField(); letterBank = createLetterBank(); baseLayer = new JPanel( new GridBagLayout() ); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.CENTER; baseLayer.add( field, gbc ); gbc.gridx = 1; gbc.weightx = 0; gbc.fill = GridBagConstraints.NONE; gbc.anchor = GridBagConstraints.NORTHWEST; baseLayer.add( letterBank, gbc ); pane = new JLayeredPane(); // Π±Π°Π·ΠΎΠ²ΡΠΉ ΡΠ»ΠΎΠΉ ΡΠ°Π·ΠΌΠ΅ΡΠ°Π΅ΡΡΡ Π½Π° Π³Π»ΡΠ±ΠΈΠ½Π΅ DEFAULT_LAYER pane.add( baseLayer, JLayeredPane.DEFAULT_LAYER ); pane.addComponentListener( new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { baseLayer.setSize( e.getComponent().getSize() ); } }); } /** * ΠΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ, ΡΠΈΡΡΡΡΠΈΠΉ ΠΈΠ³ΡΠΎΠ²ΠΎΠ΅ ΠΏΠΎΠ»Π΅ * */ static class Field extends JPanel { static final int CELL_SIZE = 50; static final int SIZE = 10; String[][] letters = new String[SIZE][SIZE]; @Override public void paintComponent( Graphics g ) { g.setColor( Color.WHITE ); g.fillRect( 0, 0, getWidth(), getHeight() ); g.setColor( Color.BLACK ); FontMetrics metrics = g.getFontMetrics(); // ΠΎΡΡΠΈΡΠΎΠ²ΠΊΠ° ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½Π½ΡΡ
Π±ΡΠΊΠ² for ( int row = 0; row < SIZE; row += 1 ) { for ( int col = 0; col < SIZE; col += 1 ) { if ( letters[row][col] != null ) { Rectangle2D bounds = metrics.getStringBounds( letters[row][col], g ); g.drawString( letters[row][col], col * CELL_SIZE + (int)(CELL_SIZE - bounds.getWidth()) / 2, row * CELL_SIZE + metrics.getAscent() + (int)(CELL_SIZE - bounds.getHeight()) / 2 ); } } } // ΠΎΡΡΠΈΡΠΎΠ²ΠΊΠ° Π»ΠΈΠ½ΠΈΠΉ ΡΠ΅ΡΠΊΠΈ for ( int row = 1; row < SIZE; row += 1 ) { g.drawLine( 0, row * CELL_SIZE, getWidth(), row * CELL_SIZE ); } for ( int col = 1; col < SIZE; col += 1 ) { g.drawLine( col * CELL_SIZE, 0, col * CELL_SIZE, getHeight() ); } } @Override public Dimension getPreferredSize() { return new Dimension( SIZE * CELL_SIZE, SIZE * CELL_SIZE ); } @Override public Dimension getMinimumSize() { return getPreferredSize(); } public void letterDropped( Letter letter, Point dropLocation ) { if ( new Rectangle( getSize() ).contains( dropLocation ) ) { letters[dropLocation.y / CELL_SIZE][dropLocation.x / CELL_SIZE] = letter.letter; repaint(); } } } private Field createField() { return new Field(); } /** * ΠΠ»Π°ΡΡ, ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΠΈΠΉ ΡΠΎΠ±ΡΡΠΈΡ ΠΌΡΡΠΈ Π½Π° ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°Ρ
Ρ Π±ΡΠΊΠ²Π°ΠΌΠΈ */ private final class DragAdapter extends MouseAdapter { Letter letterToDrag; Point clickLocation; Point baseLocation; @Override public void mousePressed( MouseEvent event ) { Letter source = (Letter)event.getSource(); // ΠΏΡΠΈ Π½Π°ΠΆΠ°ΡΠΈΠΈ ΡΠΎΠ·Π΄Π°Π΅ΠΌ Π½ΠΎΠ²ΡΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ, ΠΊΠΎΡΠΎΡΡΠΉ Π±ΡΠ΄Π΅Ρ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°ΡΡΡΡ letterToDrag = new Letter( source.letter, true ); // ΠΏΠ΅ΡΠ΅ΡΡΠΈΡΡΠ²Π°Π΅ΠΌ Π΅Π³ΠΎ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΈΠ· ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²Π° ΠΏΠ°Π½Π΅Π»ΠΈ Ρ Π±ΡΠΊΠ²Π°ΠΌΠΈ (source.getParent()) // Π² ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²ΠΎ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΏΠ°Π½Π΅Π»ΠΈ pane letterToDrag.setBounds( SwingUtilities.convertRectangle( source.getParent(), source.getBounds(), pane ) ); // Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°Π΅ΠΌ, Π² ΠΊΠ°ΠΊΠΎΠΉ ΡΠΎΡΠΊΠ΅ (Π² ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΠ°Ρ
Π±ΡΠΊΠ²Ρ) Π½Π°ΠΆΠ°ΡΠ° ΠΌΡΡΡ clickLocation = event.getPoint(); // Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°Π΅ΠΌ ΡΡΠ°ΡΡΠΎΠ²ΡΠ΅ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°Π΅ΠΌΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ° baseLocation = letterToDrag.getLocation(); // Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ Π±ΡΠΊΠ²Ρ Π΄Π»Ρ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°Π½ΠΈΡ Π½Π° ΠΎΡΠ½ΠΎΠ²Π½ΡΡ ΠΏΠ°Π½Π΅Π»Ρ Π½Π° ΡΠ»ΠΎΠΉ DRAG_LAYER (Π²ΡΡΠ΅ DEFAULT_LAYER) pane.add( letterToDrag, JLayeredPane.DRAG_LAYER ); pane.setCursor( Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ) ); } @Override public void mouseReleased( MouseEvent event ) { Letter source = (Letter)event.getSource(); // ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ Π² ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²Π΅ Π±ΡΠΊΠ²Ρ Point dropPoint = event.getPoint(); System.out.println( "drop at: " + dropPoint ); // ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄ΠΈΠΌ Π² ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²ΠΎ ΠΈΠ³ΡΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΠ»Ρ Point pointInFieldCoords = SwingUtilities.convertPoint( source, dropPoint, field ); // Π²ΡΠ΅ ΠΏΡΡΡΠ΅ΠΌ pane.setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); letterToDrag.setVisible( false ); pane.remove( letterToDrag ); letterToDrag = null; // ΡΠΎΠΎΠ±ΡΠ°Π΅ΠΌ ΠΏΠΎΠ»Ρ, ΡΡΠΎ Π±ΡΠΊΠ²Π° ΡΠ±ΡΠΎΡΠ΅Π½Π° field.letterDropped( source, pointInFieldCoords ); } @Override public void mouseDragged( MouseEvent event ) { // ΠΏΡΠΈ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°Π½ΠΈΠΈ ΠΌΠ΅Π½ΡΠ΅ΠΌ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°Π΅ΠΌΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ° // clickLocation.x - event.getX - ΡΠ°Π·Π½ΠΈΡΠ° ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΠΌ ΠΌΡΡΠΈ // ΠΏΡΠΈ Π½Π°ΠΆΠ°ΡΠΈΠΈ, ΠΈ ΡΠ΅ΠΊΡΡΠ΅ΠΌ letterToDrag.setLocation( baseLocation.x - clickLocation.x + event.getX(), baseLocation.y - clickLocation.y + event.getY() ); } } private JPanel createLetterBank() { JPanel result = new JPanel( new GridLayout( 0, 3 ) ); DragAdapter dragAdapter = new DragAdapter(); for ( String letter : new String[] { "A", "B", "C", "D", "E", "F", "G", "H" } ) { Letter letterComponent = new Letter( letter ); letterComponent.addMouseMotionListener( dragAdapter ); letterComponent.addMouseListener( dragAdapter ); result.add( letterComponent ); } return result; } /** * ΠΠ»Π°ΡΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ° Π±ΡΠΊΠ²Ρ */ static class Letter extends JComponent { static final int SIZE = 30; String letter; boolean dragged; public Letter( String letter ) { this( letter, false ); } public Letter( String letter, boolean dragged ) { this.letter = letter; this.dragged = dragged; } @Override public void paintComponent( Graphics g ) { g.setColor( dragged ? Color.RED : Color.BLACK ); g.fillRect( 0, 0, getWidth(), getHeight() ); FontMetrics metrics = g.getFontMetrics(); Rectangle2D bounds = metrics.getStringBounds( letter, g ); g.setColor( Color.WHITE ); g.drawString( letter, (int)(SIZE - bounds.getWidth()) / 2, metrics.getAscent() + (int)(SIZE - bounds.getHeight()) / 2 ); } @Override public Dimension getPreferredSize() { return new Dimension( SIZE, SIZE ); } } } static void initUi() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE ); final GamePanel gamePanel = new GamePanel(); frame.add( gamePanel.pane ); frame.setSize( 800, 600 ); frame.setVisible( true ); } public static void main(String[] args) { EventQueue.invokeLater( LetterDrop::initUi ); } }