#java #swing #jpanel #jlabel #chess
#java #swing #jpanel #jlabel #шахматы
Вопрос:
Я пытаюсь разработать шахматный движок Java, используя Swing для графического интерфейса, и за последние пару дней я обнаружил некоторые неудачи. Движок уже полностью функционален и отлично работает с перемещениями по щелчку (1 щелчок по плитке-JPanel с фигурой, которую вы хотите переместить, еще один щелчок по плитке-JPanel, где вы хотите разместить фигуру). Теперь я готов сделать еще один шаг и решил реализовать перетаскивание (щелкните и удерживайте мышь, перетащите и отпустите нужную JPanel).
Вот где я нахожу свои проблемы. Мой класс move имеет следующие аргументы: конфигурация шахматной доски, начальная плитка для перемещения и конечная плитка. При нажатии мыши он принимает идентификатор исходной плитки, но когда я отпускаю его, то, что будет идентификатором целевой плитки, вместо этого также является идентификатором исходной плитки. Это заставляет мой движок думать, что я пытаюсь переместить фрагменты на одну и ту же плитку, что приводит к незаконному перемещению. Я попытался использовать destinationTile = chessBoard.getTile(tileId)
ту же концепцию, которую я использовал для перемещений по щелчку, но вместо этого он использует исходную плитку. Я пробовал использовать TilePanel obj = (TilePanel)e.getSource(); destinationTile = chessBoard.getTile(obj.getTileId());
и при этом как исходная, так и целевая плитка получают значение идентификатора элемента? Поэтому, если я попытаюсь перетащить пешку, они оба получат «P» в качестве значения, если я попытаюсь переместить ферзя, они получат «Q». Так что да, я полностью потерян.
Вторая проблема, с которой я сталкиваюсь, заключается в том, что при перетаскивании фрагмента-JLabel он ограничен границами фрагмента-JPanel, в котором он находится. Этого следовало ожидать, поэтому я попытался использовать JLayeredPane. Мой подход заключался в том, чтобы клонировать перемещенную часть-JLabel, установить исходную невидимой. Добавьте клонированный в JLayeredPane.DRAG_LAYER
, перетащите его, следуя указаниям мыши. И при выпуске, если данное перемещение было законным, поместите эту JLabel на целевую плитку JPanel и удалите невидимую JLabel на исходной плитке. Это также не увенчалось успехом, поскольку, как бы я ни пытался добавить JLabel в DRAG_LAYER, я не мог заставить его работать. Что бы я ни пробовал, я не мог заставить изображение части следовать за моим курсором по доске.
Вот большая часть моего класса Tile со всеми обработчиками MouseEvent:
private class TilePanel extends JPanel {
private final int tileId;
public int getTileId() {
return tileId;
}
TilePanel(final BoardPanel boardPanel, final int tileId) throws IOException {
super(new GridBagLayout());
this.tileId = tileId;
setPreferredSize(TILE_PANEL_DIMENSION);
assignTileColor();
assignTilePieceIcon(chessBoard);
highlightTileBorder(chessBoard);
addMouseListener(new MouseListener() {
@Override
public void mouseClicked(final MouseEvent e) {
if(isRightMouseButton(e)) {
sourceTile = null;
destinationTile = null;
humanMovedPiece = null;
try {
boardPanel.drawBoard(chessBoard);
} catch (IOException ioException) {
ioException.printStackTrace();
}
System.out.println("piece deselected");
}
else if (isLeftMouseButton(e)) {
if(sourceTile == null) {
//first click
sourceTile = chessBoard.getTile(tileId);
humanMovedPiece = sourceTile.getPiece();
if (humanMovedPiece == null) {
sourceTile = null;
}
System.out.println("piece selected");
}
else {
//second click
destinationTile = chessBoard.getTile(tileId);
final Move move = Move.MoveFactory.createMove(chessBoard, sourceTile.getTileCoordinate(), destinationTile.getTileCoordinate());
final MoveTransition transition = chessBoard.getCurrentPlayer().makeMove(move);
if (transition.getMoveStatus() == MoveStatus.DONE) {
chessBoard = transition.getTransitionBoard();
moveLog.addMove(move);
SFXUtils.playSound("move");
System.out.println(chessBoard);
System.out.println("█████ move done: " move.toString() " █████");
}
sourceTile = null;
destinationTile = null;
humanMovedPiece = null;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
gameHistoryPanel.redo(chessBoard, moveLog);
takenPiecesPanel.redo(moveLog);
boardPanel.drawBoard(chessBoard);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
});
}
}
@Override
public void mousePressed(final MouseEvent e) {
if(sourceTile == null) {
//first click
sourceTile = chessBoard.getTile(tileId);
System.out.println(tileId);
System.out.println(sourceTile.getTileCoordinate());
humanMovedPiece = sourceTile.getPiece();
if (humanMovedPiece == null) {
sourceTile = null;
}
System.out.println("piece selected");
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
gameHistoryPanel.redo(chessBoard, moveLog);
takenPiecesPanel.redo(moveLog);
boardPanel.drawBoard(chessBoard);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
});
}
@Override
public void mouseReleased(final MouseEvent e) {
/* TilePanel obj = (TilePanel)e.getSource();
destinationTile = chessBoard.getTile(obj.getTileId());*/
destinationTile = chessBoard.getTile(tileId);
final Move move = Move.MoveFactory.createMove(chessBoard, sourceTile.getTileCoordinate(), destinationTile.getTileCoordinate());
final MoveTransition transition = chessBoard.getCurrentPlayer().makeMove(move);
if (transition.getMoveStatus() == MoveStatus.DONE) {
chessBoard = transition.getTransitionBoard();
moveLog.addMove(move);
SFXUtils.playSound("move");
System.out.println(chessBoard);
System.out.println("█████ move done: " move.toString() " █████");
}
sourceTile = null;
destinationTile = null;
humanMovedPiece = null;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
gameHistoryPanel.redo(chessBoard, moveLog);
takenPiecesPanel.redo(moveLog);
boardPanel.drawBoard(chessBoard);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
});
}
@Override
public void mouseEntered(final MouseEvent e) {
}
@Override
public void mouseExited(final MouseEvent e) {
}
});
addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(final MouseEvent e) {
System.out.println("im draggin");
TilePanel obj = (TilePanel)e.getSource();
JLabel draggedPieceImageLabel = (JLabel) obj.getComponent(0);
//obj.getComponent(0).setVisible(false);
draggedPieceImageLabel.setLocation(e.getX()-25,e.getY()-25);
add(draggedPieceImageLabel);
}
@Override
public void mouseMoved(MouseEvent e) {
}
});
validate();
}
Комментарии:
1. Ваш MouseMotionListener выполняет
add(draggedPieceImageLabel);
. Перетаскиваемый фрагмент не может находиться как в JLayeredPane, так и в TilePanel. Он нужен только в JLayeredPane.2. Я помню, как много лет назад у меня была именно эта проблема. Я думаю, что мое решение состояло в том, чтобы просто использовать 1 JPanel для всей платы с GridLayout. Вы захотите переопределить paintBackground, но во многих отношениях это упрощает задачу.
3. Может быть, это уместно? Перетаскивание и передача данных , что является уроком в trail под названием » Создание графического интерфейса пользователя с помощью JFC / Swing «, который является частью руководств Oracle по Java.
4. @VGR Я не упомянул, что опубликованный мной код — это версия, в которой не используется JLayeredPane. При использовании JLayeredPane я пытаюсь добавить к нему JLabel, но безрезультатно. Я сделал это так
boardLayeredPane.add(draggedPieceImageLabel, JLayeredPane.DRAG_LAYER);
, слоем boardLayeredPane по умолчанию была доска.5. @Abra Я проверю это, спасибо!