JLabel在实现mouseReleased()时未点击到正确位置。

huangapple go评论105阅读模式
英文:

JLabel not clicking into the right place when mouseReleased() is implemented

问题

我正在进行一个接龙(Solitaire)项目,目前在GUI部分遇到了严重问题。我尝试使用Rob Camick的OverlapLayout进行拖放操作,但它不按照我的期望工作。

我可以很好地拾取并移动卡片,但当我释放它时,它不会放在我想要的位置,并且卡片会消失。如何修复这个问题?

附上了我的代码的简化版本。当运行该代码时,如[附图]所示,卡片从一个堆栈拖动到另一个堆栈,但然后它会自动移动到屏幕底部,而不是放在它被放下的卡片上。

这是存在问题的类(我认为问题与使用for循环创建主堆栈的方式有关,因为在拖动时,拖动层会位于某些堆栈的后面):

  1. import java.awt.*;
  2. import java.awt.event.*;
  3. import javax.swing.*;
  4. import javax.swing.border.*;
  5. public class Test extends JPanel implements MouseListener, MouseMotionListener {
  6. //instance variables
  7. private static final int CARD_WIDTH = 73;
  8. private static final int CARD_HEIGHT = (int)(CARD_WIDTH*1.49137931+.5);
  9. private static final int MAIN_STACKS_X_DIFF = 35;
  10. private static final int MAIN_STACKS_Y_DIFF = 30;
  11. private final Dimension cardSize = new Dimension(CARD_WIDTH, CARD_HEIGHT);
  12. private JPanel mainStacks, mainWrapper;
  13. private Component card;
  14. private int xAdj, yAdj;
  15. private Deck deckOfCards;
  16. //methods
  17. public Test() {
  18. JFrame gameScreen = new JFrame();
  19. gameScreen.setTitle("Solitaire");
  20. gameScreen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  21. gameScreen.add(this);
  22. deckOfCards = new Deck();
  23. deckOfCards.shuffleDeck();
  24. mainStacks = makeMainStacks();
  25. mainWrapper = new JPanel();
  26. mainWrapper.setBackground(new Color(0,153,0));
  27. mainWrapper.add(mainStacks);
  28. gameScreen.setLayout(new BorderLayout());
  29. gameScreen.add(mainWrapper, BorderLayout.CENTER);
  30. setLayout(null);
  31. gameScreen.setSize(800,600);
  32. gameScreen.setResizable(false);
  33. gameScreen.getContentPane().setBackground(new Color(0,153,0));
  34. gameScreen.setVisible( true );
  35. }
  36. private JPanel makeMainStacks() {
  37. mainStacks = new JPanel(new GridLayout(0,7,MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
  38. for (int stack = 0; stack < 7; stack++) {
  39. JPanel mainStack = new JPanel( new OverlapLayout( new Point(0, 22)));
  40. mainStack.setBackground( new Color(0,153,0));
  41. mainStack.setBounds(10,0,CARD_WIDTH, CARD_HEIGHT);
  42. mainStack.setBorder( new EmptyBorder(10, 0, CARD_WIDTH, CARD_HEIGHT));
  43. mainStack.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT + 250));
  44. mainStacks.add(mainStack);
  45. for (int cardInStack = 0; cardInStack <= stack; cardInStack++) {
  46. Card placementCard = deckOfCards.drawCard(); //draws card
  47. placementCard.setIsFaceUp(false);
  48. //checking if the card is the last card in the stack
  49. if (stack == cardInStack) {
  50. System.out.println(stack + cardInStack);
  51. placementCard.setIsFaceUp(true); //card is facing up
  52. //sizes the image and sets it to placementCard
  53. ImageIcon placementCardFace = new ImageIcon(placementCard.getImagePath());
  54. Image placementCardScaled = placementCardFace.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
  55. JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
  56. mainStack.add(cardLabel);
  57. } //end of if statement
  58. //if statement to check whether the card should be revealed or not
  59. if (!placementCard.getIsFaceUp()) { //hide cardFront
  60. //sizes the image and sets it to placementCard
  61. ImageIcon placementCardBack = new ImageIcon(Card.getBackImagePath());
  62. Image placementCardScaled = placementCardBack.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
  63. JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
  64. cardLabel.setPreferredSize(cardSize);
  65. mainStack.add(cardLabel);
  66. } //end of if statement
  67. }
  68. mainStack.addMouseListener(this);
  69. mainStack.addMouseMotionListener(this);
  70. }
  71. JPanel mainStacksWrapper = new JPanel();
  72. mainStacksWrapper.add(mainStacks);
  73. mainStacksWrapper.setBackground(new Color(0,153,0));
  74. mainStacks.setBackground(new Color(0,153,0));
  75. return mainStacksWrapper;
  76. }
  77. @Override
  78. public void mousePressed(MouseEvent e) {
  79. try {
  80. JPanel stack = (JPanel)e.getComponent();
  81. card = stack.getComponent(0);
  82. if (card instanceof JPanel){}
  83. Point stackPos = card.getParent().getLocation();
  84. xAdj = stackPos.x-e.getX();
  85. yAdj = stackPos.y-e.getY();
  86. card.setLocation(e.getX() + xAdj + 35, e.getY() + yAdj);
  87. System.out.println(xAdj + yAdj);
  88. JLayeredPane lp = getRootPane().getLayeredPane();
  89. lp.add(card,JLayeredPane.DRAG_LAYER);
  90. setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
  91. } catch (ArrayIndexOutOfBoundsException err) {
  92. System.err.println("No card in stack. Cannot execute move.");
  93. }
  94. }
  95. @Override
  96. public void mouseDragged(MouseEvent e) {
  97. if (card==null){}
  98. int x = e.getX() + xAdj + 35;
  99. int y = e.getY() + yAdj;
  100. card.setLocation(x,y);
  101. }
  102. @Override
  103. public void mouseReleased(MouseEvent e) {
  104. setCursor(null);
  105. if (card == null) return;
  106. // Make sure the card is no longer painted on the layered pane
  107. card.setVisible(false);
  108. // check to see if card was dragged to a stack
  109. JPanel stackPanel = null;
  110. Point cardLocation = card.getLocation();
  111. cardLocation.y -= mainStacks.getParent().getLocation().y;
  112. for (int i = 0; i < mainStacks.getComponentCount(); i++)
  113. {
  114. JPanel panel = (JPanel)mainStacks.getComponent(i);
  115. if (panel.getBounds().contains(cardLocation))
  116. {
  117. stackPanel = panel;
  118. break;
  119. }
  120. }
  121. if (stackPanel == null)
  122. mainStacks.add( card );
  123. else
  124. stackPanel.add( card );
  125. card.setVisible(true);
  126. }
  127. @Override
  128. public void mouseClicked(MouseEvent e) {}
  129. @Override
  130. public void mouseMoved(MouseEvent e) {}
  131. @Override
  132. public void mouseEntered(MouseEvent e) {}
  133. @Override
  134. public void mouse
  135. <details>
  136. <summary>英文:</summary>
  137. so I am working on a Solitaire project and am currently severely stuck on the GUI part. I am trying to do drag and drop using Rob Camick&#39;s OverlapLayout [(source code here)](https://tips4java.wordpress.com/2009/07/26/overlap-layout/) but it doesn&#39;t work out how I want it to.
  138. I can pick up the card and move it just fine, but when I release it, it doesn&#39;t drop down where I want it to and the card disappears. How to fix this?
  139. Attached is a simplified version of my code. When that code is run, as seen in the [attached image], the card is dragged from one stack to another, but then it automatically moves to the bottom of the screen instead of on top of the card it was dropped on.
  140. This is the class with the problem (I believe that it has to do with the way I created the mainStacks using the for loop, because when I was dragging, the drag layer went behind some of the stacks themselves):
  141. ```java
  142. import java.awt.*;
  143. import java.awt.event.*;
  144. import javax.swing.*;
  145. import javax.swing.border.*;
  146. public class Test extends JPanel implements MouseListener, MouseMotionListener {
  147. //instance variables
  148. private static final int CARD_WIDTH = 73;
  149. private static final int CARD_HEIGHT = (int)(CARD_WIDTH*1.49137931+.5);
  150. private static final int MAIN_STACKS_X_DIFF = 35;
  151. private static final int MAIN_STACKS_Y_DIFF = 30;
  152. private final Dimension cardSize = new Dimension(CARD_WIDTH, CARD_HEIGHT);
  153. private JPanel mainStacks, mainWrapper;
  154. private Component card;
  155. private int xAdj, yAdj;
  156. private Deck deckOfCards;
  157. //methods
  158. public Test() {
  159. JFrame gameScreen = new JFrame();
  160. gameScreen.setTitle(&quot;Solitaire&quot;);
  161. gameScreen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  162. gameScreen.add(this);
  163. deckOfCards = new Deck();
  164. deckOfCards.shuffleDeck();
  165. mainStacks = makeMainStacks();
  166. mainWrapper = new JPanel();
  167. mainWrapper.setBackground(new Color(0,153,0));
  168. mainWrapper.add(mainStacks);
  169. gameScreen.setLayout(new BorderLayout());
  170. gameScreen.add(mainWrapper, BorderLayout.CENTER);
  171. setLayout(null);
  172. gameScreen.setSize(800,600);
  173. gameScreen.setResizable(false);
  174. gameScreen.getContentPane().setBackground(new Color(0,153,0));
  175. gameScreen.setVisible( true );
  176. }
  177. private JPanel makeMainStacks() {
  178. mainStacks = new JPanel(new GridLayout(0,7,MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
  179. for (int stack = 0; stack &lt; 7; stack++) {
  180. JPanel mainStack = new JPanel( new OverlapLayout( new Point(0, 22)));
  181. mainStack.setBackground( new Color(0,153,0));
  182. mainStack.setBounds(10,0,CARD_WIDTH, CARD_HEIGHT);
  183. mainStack.setBorder( new EmptyBorder(10, 0, CARD_WIDTH, CARD_HEIGHT));
  184. mainStack.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT + 250));
  185. mainStacks.add(mainStack);
  186. for (int cardInStack = 0; cardInStack &lt;= stack; cardInStack++) {
  187. Card placementCard = deckOfCards.drawCard(); //draws card
  188. placementCard.setIsFaceUp(false);
  189. //checking if the card is the last card in the stack
  190. if (stack == cardInStack) {
  191. System.out.println(stack + cardInStack);
  192. placementCard.setIsFaceUp(true); //card is facing up
  193. //sizes the image and sets it to placementCard
  194. ImageIcon placementCardFace = new ImageIcon(placementCard.getImagePath());
  195. Image placementCardScaled = placementCardFace.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
  196. JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
  197. mainStack.add(cardLabel);
  198. } //end of if statement
  199. //if statement to check whether the card should be revealed or not
  200. if (!placementCard.getIsFaceUp()) { //hide cardFront
  201. //sizes the image and sets it to placementCard
  202. ImageIcon placementCardBack = new ImageIcon(Card.getBackImagePath());
  203. Image placementCardScaled = placementCardBack.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
  204. JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
  205. cardLabel.setPreferredSize(cardSize);
  206. mainStack.add(cardLabel);
  207. } //end of if statement
  208. }
  209. mainStack.addMouseListener(this);
  210. mainStack.addMouseMotionListener(this);
  211. }
  212. JPanel mainStacksWrapper = new JPanel();
  213. mainStacksWrapper.add(mainStacks);
  214. mainStacksWrapper.setBackground(new Color(0,153,0));
  215. mainStacks.setBackground(new Color(0,153,0));
  216. return mainStacksWrapper;
  217. }
  218. @Override
  219. public void mousePressed(MouseEvent e) {
  220. try {
  221. JPanel stack = (JPanel)e.getComponent();
  222. card = stack.getComponent(0);
  223. if (card instanceof JPanel){}
  224. Point stackPos = card.getParent().getLocation();
  225. xAdj = stackPos.x-e.getX();
  226. yAdj = stackPos.y-e.getY();
  227. card.setLocation(e.getX() + xAdj + 35, e.getY() + yAdj);
  228. System.out.println(xAdj + yAdj);
  229. JLayeredPane lp = getRootPane().getLayeredPane();
  230. lp.add(card,JLayeredPane.DRAG_LAYER);
  231. setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
  232. } catch (ArrayIndexOutOfBoundsException err) {
  233. System.err.println(&quot;No card in stack. Cannot execute move.&quot;);
  234. }
  235. }
  236. @Override
  237. public void mouseDragged(MouseEvent e) {
  238. if (card==null){}
  239. int x = e.getX() + xAdj + 35;
  240. int y = e.getY() + yAdj;
  241. card.setLocation(x,y);
  242. }
  243. @Override
  244. public void mouseReleased(MouseEvent e) {
  245. setCursor(null);
  246. if (card == null) return;
  247. // Make sure the card is no longer painted on the layered pane
  248. card.setVisible(false);
  249. // check to see if card was dragged to a stack
  250. JPanel stackPanel = null;
  251. Point cardLocation = card.getLocation();
  252. cardLocation.y -= mainStacks.getParent().getLocation().y;
  253. for (int i = 0; i &lt; mainStacks.getComponentCount(); i++)
  254. {
  255. JPanel panel = (JPanel)mainStacks.getComponent(i);
  256. if (panel.getBounds().contains(cardLocation))
  257. {
  258. stackPanel = panel;
  259. break;
  260. }
  261. }
  262. if (stackPanel == null)
  263. mainStacks.add( card );
  264. else
  265. stackPanel.add( card );
  266. card.setVisible(true);
  267. }
  268. @Override
  269. public void mouseClicked(MouseEvent e) {}
  270. @Override
  271. public void mouseMoved(MouseEvent e) {}
  272. @Override
  273. public void mouseEntered(MouseEvent e) {}
  274. @Override
  275. public void mouseExited(MouseEvent e) {}
  276. public static void main(String[] args) {
  277. new Test();
  278. }
  279. }

These next few classes are just additional classes to help the code compile:

  1. import java.util.ArrayList;
  2. import java.util.Collections;
  3. public class Deck {
  4. //instance variables
  5. private final ArrayList&lt;Card&gt; cards;
  6. private int drawCardIndex;
  7. //methods
  8. /**
  9. * This constructor for the Deck class creates all 52 cards and adds them to cards List.
  10. */
  11. public Deck() {
  12. cards = new ArrayList&lt;&gt;(); //creating new deck
  13. for (Suit suit: Suit.values()) { //loops through suits
  14. for (Rank rank: Rank.values()) { //loops through ranks
  15. cards.add(new Card(suit, rank)); //adds a unique Card to cards
  16. }
  17. }
  18. drawCardIndex = 0; //initializes index
  19. } //end of constructor
  20. /**
  21. * This method returns the size of the deck.
  22. * @return int - size of deck
  23. */
  24. public int getSize() {
  25. return cards.size();
  26. } //end of getSize method
  27. /**
  28. * This method returns a drawn card in the deck of cards.
  29. * @return Card - a new drawn card in the deck of cards.
  30. */
  31. public Card drawCard() {
  32. if (drawCardIndex&lt;cards.size()) { //checks if all of the cards have been drawn
  33. Card card = cards.get(drawCardIndex); //draws a card
  34. drawCardIndex++; //increments
  35. return card; //return statement
  36. }
  37. return null; //returns null if all of the cards have already been drawn
  38. } //end of drawCard method
  39. /**
  40. * This method shuffles the deck of cards.
  41. */
  42. public void shuffleDeck() {
  43. Collections.shuffle(cards); //mixes ArrayList
  44. resetDrawCardIndex(); //resets the drawCardIndex variable
  45. } //end of shuffleDeck method
  46. /**
  47. * This method resets the drawCardIndex.
  48. */
  49. public void resetDrawCardIndex() {
  50. drawCardIndex = 0;
  51. } //end of resetDrawCardIndex method
  52. /**
  53. * This method removes a card from the deck.
  54. * @param card the card to be removed from the deck
  55. */
  56. public void removeCardFromDeck(Card card) {
  57. //checks if the stack is empty
  58. cards.remove(card); //removes the card from the deck
  59. } //end of removeCardFromStack method
  60. /**
  61. * This method checks if all of the cards have been drawn.
  62. * @return Boolean true or false based on if all the cards have been drawn or not.
  63. */
  64. public boolean isEmpty() {
  65. return cards.size() &lt;= drawCardIndex;
  66. } //end of isEmpty method
  67. } //end of Deck class
  1. public class Card {
  2. //instance variables
  3. private final Suit SUIT;
  4. private final Rank RANK;
  5. private boolean isFaceUp;
  6. //methods
  7. /**
  8. * This is the constructor method for the Card class.
  9. * @param suit - the suit to be assigned to the card.
  10. * @param rank - the rank to be assigned to the card.
  11. */
  12. public Card(Suit suit, Rank rank) {
  13. this.SUIT = suit;
  14. this.RANK = rank;
  15. this.isFaceUp = false;
  16. } //end of constructor
  17. /**
  18. * This method gives the user the suit of the Card.
  19. * @return Suit - the suit of the Card.
  20. */
  21. public Suit getSuit() {
  22. return this.SUIT;
  23. } //end of getSuit method
  24. /**
  25. * This method gives the user the rank of the Card.
  26. * @return Rank - the rank of the Card.
  27. */
  28. public Rank getRank() {
  29. return this.RANK;
  30. } //end of getRank method
  31. /**
  32. * This method gives whether the Card is faced up or not.
  33. * @return Boolean - true or false based on if the Card is facing up.
  34. */
  35. public boolean getIsFaceUp() {
  36. return this.isFaceUp;
  37. } //end of getIsFaceUp method
  38. /**
  39. * This method sets the isFaceUp variable to true if the card is facing up in the game.
  40. */
  41. public void setIsFaceUp(boolean b) {
  42. this.isFaceUp = b;
  43. } //end of setIsFaceUp method
  44. /**
  45. * This method determines if the Card is from a red suit.
  46. * @return Boolean - true or false if card is red
  47. */
  48. public boolean isRed() {
  49. return this.SUIT == Suit.HEARTS || this.SUIT == Suit.DIAMONDS;
  50. } //end of isRed method
  51. /**
  52. * This method determines if the Card is from a black suit.
  53. * @return a Boolean true or false if card is black
  54. */
  55. public boolean isBlack() {
  56. return this.SUIT == Suit.CLUBS || this.SUIT == Suit.SPADES;
  57. } //end of isBlack method
  58. /**
  59. * This method determines if the card can be stacked on the card behind it.
  60. * @return a Boolean true or false if it can be stacked
  61. */
  62. public boolean stackable(Card card) {
  63. return this.isRed() != card.isRed();
  64. } //end of stackable method
  65. /**
  66. * This method gives the name of each card.
  67. * @return a string that has two letters- the first letter of the rank and the first letter of the suit.
  68. */
  69. public String getName() {
  70. String number = &quot;&quot;; //variable declaration
  71. switch(RANK) { //switch to assign rank name
  72. case ACE -&gt; number = &quot;A&quot;; //case
  73. case TWO -&gt; number = &quot;2&quot;; //case
  74. case THREE -&gt; number = &quot;3&quot;; //case
  75. case FOUR -&gt; number = &quot;4&quot;; //case
  76. case FIVE -&gt; number = &quot;5&quot;; //case
  77. case SIX -&gt; number = &quot;6&quot;; //case
  78. case SEVEN -&gt; number = &quot;7&quot;; //case
  79. case EIGHT -&gt; number = &quot;8&quot;; //case
  80. case NINE -&gt; number = &quot;9&quot;; //case
  81. case TEN -&gt; number = &quot;10&quot;; //case
  82. case JACK -&gt; number = &quot;J&quot;; //case
  83. case QUEEN -&gt; number = &quot;Q&quot;; //case
  84. case KING -&gt; number = &quot;K&quot;; //case
  85. } //end of switch
  86. String suitName = &quot;&quot;; //variable declaration
  87. switch(SUIT) { //switch to assign suit name
  88. case HEARTS -&gt; suitName = &quot;H&quot;; //case
  89. case DIAMONDS -&gt; suitName = &quot;D&quot;; //case
  90. case SPADES -&gt; suitName = &quot;S&quot;; //case
  91. case CLUBS -&gt; suitName = &quot;C&quot;; //case
  92. } //end of switch
  93. return number + suitName; //return statement
  94. } //end of getName method
  95. /**
  96. * This method gives the path to a corresponding image.
  97. * @return String - returns the path of the image corresponding with the card.
  98. */
  99. public String getImagePath() {
  100. return &quot;C:\\ICS3U_SUMMATIVE_SOLITAIRE\\src\\cards\\&quot; + getName().substring(1,getName().length()) + &quot;\\&quot; + getName() + &quot;.jpg&quot;; //return statement
  101. } //end of getImage method
  102. /**
  103. * This method gives the file path to the back of the card image.
  104. * @return String the file path to the card back image
  105. */
  106. public static String getBackImagePath() {
  107. return &quot;C:\\ICS3U_SUMMATIVE_SOLITAIRE\\src\\CardBack.jpg&quot;;
  108. } //end of getImageBackPath method
  109. @Override
  110. /**
  111. * This method returns the description about the instance variables.
  112. * @return String - rank and suit of card along with image path.
  113. */
  114. public String toString() {
  115. return this.RANK + &quot; of &quot; + this.SUIT + &quot; ---- &quot; + getImagePath();
  116. }
  117. } //end of Card class
  1. public enum Suit {
  2. //enum fields
  3. /**
  4. * Hearts, Diamonds,
  5. */
  6. HEARTS,DIAMONDS,
  7. /**
  8. * Spades, Clubs
  9. */
  10. SPADES,CLUBS;
  11. }
  1. public enum Rank {
  2. //enum fields
  3. /**
  4. * 2,3,4,5,6,7,8,9,10
  5. */
  6. TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN,
  7. /**
  8. * A,J,Q,K
  9. */
  10. ACE,JACK,QUEEN,KING;
  11. }

I have reviewed past suggestions from SO, but those do not seem to help my problem. Also have tried altering the y values to see if that helps, but it didn't.

答案1

得分: 0

第一部分...

mouseEvent 报告的位置是相对于生成事件的组件的位置,即顶部/左部位置(0x0)是相对于组件的位置。

由于您在坐标上下文之间移动组件,因此需要相应地转换事件点,幸运的是,有一种简单的方法可以做到这一点 - SwingUtilities.convertMouseEvent(Component, Point, Component)

  1. @Override
  2. public void mousePressed(MouseEvent e) {
  3. try {
  4. JPanel stack = (JPanel) e.getComponent();
  5. card = stack.getComponent(0);
  6. if (card instanceof JPanel) {
  7. }
  8. Point cardInWorld = SwingUtilities.convertPoint(card.getParent(), card.getLocation(), getRootPane().getLayeredPane());
  9. Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());
  10. Point stackPos = card.getParent().getLocation();
  11. xAdj = pointInWorld.x - cardInWorld.getLocation().x;
  12. yAdj = pointInWorld.y - cardInWorld.getLocation().y;
  13. JLayeredPane lp = getRootPane().getLayeredPane();
  14. lp.add(card, JLayeredPane.DRAG_LAYER);
  15. card.setLocation(cardInWorld);
  16. setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
  17. } catch (ArrayIndexOutOfBoundsException err) {
  18. System.err.println("No card in stack. Cannot execute move.");
  19. }
  20. }
  21. @Override
  22. public void mouseDragged(MouseEvent e) {
  23. if (card == null) {
  24. }
  25. Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());
  26. int x = pointInWorld.x - xAdj;
  27. int y = pointInWorld.y - yAdj;
  28. card.setLocation(x, y);
  29. }

这将使卡片相对于鼠标光标保持位置不变 - 因此,它的位置(相对于鼠标光标)不会改变。这使得鼠标光标的位置成为放置的源头,而不是卡片的位置。

第二部分...

像这样遍历容器的内容始终是一个不好的主意,并且花费了相当多的时间来弄清楚。

问题的根源在于此处...

  1. private JPanel makeMainStacks() throws IOException {
  2. mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
  3. //...
  4. JPanel mainStacksWrapper = new JPanel();
  5. mainStacksWrapper.add(mainStacks);
  6. mainStacksWrapper.setBackground(new Color(0, 153, 0));
  7. mainStacks.setBackground(new Color(0, 153, 0));
  8. return mainStacksWrapper;
  9. }

您将 mainStacks 添加到一个“包装器”容器中,然后将其分配给 mainStacks(我很困惑)。

这样做的问题在于,当您遍历容器的内容时,它只包含一个子项。实际上包含所有实际需要的子堆栈的原始 mainStacks

一个更好的解决方案是将每个堆栈组件添加到一个 List 中,然后简单地对其进行迭代,这样,当UI结构更改时,您不会遇到问题。

首先,创建一个新的实例字段...

  1. private List<JPanel> stacks = new ArrayList<>(7);

然后在您的 makeMainStacks 中将每个 stack 添加到其中...

  1. private JPanel makeMainStacks() throws IOException {
  2. JPanel mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
  3. mainStacks.setBackground(Color.MAGENTA);
  4. for (int stack = 0; stack < 7; stack++) {
  5. //...
  6. stacks.add(mainStack);
  7. //...
  8. }
  9. //...
  10. }

最后,在 mouseReleased 中,将 MouseEvent 的点转换为每个堆栈的父坐标上下文,并查看是否有命中...

  1. @Override
  2. public void mouseReleased(MouseEvent e) {
  3. setCursor(null);
  4. if (card == null) {
  5. return;
  6. }
  7. // Make sure the card is no longer painted on the layered pane
  8. card.setVisible(false);
  9. JPanel stackPanel = null;
  10. for (JPanel stack : stacks) {
  11. // Check the bounds of each stack against its parent's
  12. // coordinates context
  13. Point localPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), stack.getParent());
  14. if (stack.getBounds().contains(localPoint)) {
  15. System.out.println("-->");
  16. stackPanel = stack;
  17. break;
  18. }
  19. }
  20. if (stackPanel == null) {
  21. mainStacks.add(card);
  22. } else {
  23. stackPanel.add(card);
  24. stackPanel.revalidate();
  25. stackPanel.repaint();
  26. }
  27. card.setVisible(true);
  28. }

结束语...

值得一提的是,我不会这样做,因为您正在发现的所有原因。我会采取自定义绘制的路线,但我怀疑您现在没有时间这样做。

英文:

First...

A mouseEvent reports it's location relative to the component which generated the event, that is, the top/left position (0x0) is relative to the component.

Since you are moving components between coordinate contexts, you need to convert the event point accordingly, luckily, there is a simple way to do this - SwingUtilities.convertMouseEvent(Component, Point, Component)

  1. @Override
  2. public void mousePressed(MouseEvent e) {
  3. try {
  4. JPanel stack = (JPanel) e.getComponent();
  5. card = stack.getComponent(0);
  6. if (card instanceof JPanel) {
  7. }
  8. Point cardInWorld = SwingUtilities.convertPoint(card.getParent(), card.getLocation(), getRootPane().getLayeredPane());
  9. Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());
  10. Point stackPos = card.getParent().getLocation();
  11. xAdj = pointInWorld.x - cardInWorld.getLocation().x;
  12. yAdj = pointInWorld.y - cardInWorld.getLocation().y;
  13. JLayeredPane lp = getRootPane().getLayeredPane();
  14. lp.add(card, JLayeredPane.DRAG_LAYER);
  15. card.setLocation(cardInWorld);
  16. setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
  17. } catch (ArrayIndexOutOfBoundsException err) {
  18. System.err.println(&quot;No card in stack. Cannot execute move.&quot;);
  19. }
  20. }
  21. @Override
  22. public void mouseDragged(MouseEvent e) {
  23. if (card == null) {
  24. }
  25. Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());
  26. int x = pointInWorld.x - xAdj;
  27. int y = pointInWorld.y - yAdj;
  28. card.setLocation(x, y);
  29. }

This will cause the card to maintained relative to the mouse cursor - so it's position (relative to the mouse cursor) won't change. This makes it so that the mouse cursor's point is the source of the drop, not the card's position.

Second...

Walking a container's contents like this...

  1. @Override
  2. public void mouseReleased(MouseEvent e) {
  3. setCursor(null);
  4. if (card == null) return;
  5. // Make sure the card is no longer painted on the layered pane
  6. card.setVisible(false);
  7. // check to see if card was dragged to a stack
  8. JPanel stackPanel = null;
  9. Point cardLocation = card.getLocation();
  10. cardLocation.y -= mainStacks.getParent().getLocation().y;
  11. for (int i = 0; i &lt; mainStacks.getComponentCount(); i++)
  12. {
  13. JPanel panel = (JPanel)mainStacks.getComponent(i);
  14. if (panel.getBounds().contains(cardLocation))
  15. {
  16. stackPanel = panel;
  17. break;
  18. }
  19. }
  20. if (stackPanel == null)
  21. mainStacks.add( card );
  22. else
  23. stackPanel.add( card );
  24. card.setVisible(true);
  25. }

is always a bad idea and took me some significant time to suss out.

The source of the problem starts right here...

  1. private JPanel makeMainStacks() throws IOException {
  2. mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
  3. //...
  4. JPanel mainStacksWrapper = new JPanel();
  5. mainStacksWrapper.add(mainStacks);
  6. mainStacksWrapper.setBackground(new Color(0, 153, 0));
  7. mainStacks.setBackground(new Color(0, 153, 0));
  8. return mainStacksWrapper;
  9. }

You add mainStacks into a "wrapper" container, and then assign it to mainStacks (I'm so confused).

The problem with this is, when you walk the container's contents, it only contains a single child. The original mainStacks, which actually contains all the child stacks you actually want.

A better solution would be to add each stack component to a List and simply iterate over it instead, that way, you're not going to get caught out when the UI structure changes.

Start by creating a new instance field...

  1. private List&lt;JPanel&gt; stacks = new ArrayList&lt;&gt;(7);

Then in your makeMainStacks add each stack to it...

  1. private JPanel makeMainStacks() throws IOException {
  2. JPanel mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
  3. mainStacks.setBackground(Color.MAGENTA);
  4. for (int stack = 0; stack &lt; 7; stack++) {
  5. JPanel mainStack = new JPanel(new OverlapLayout(new Point(0, 22)));
  6. mainStack.setBounds(10, 0, CARD_WIDTH, CARD_HEIGHT);
  7. mainStack.setBorder(
  8. new CompoundBorder(new LineBorder(Color.RED), new EmptyBorder(10, 0, CARD_WIDTH, CARD_HEIGHT))
  9. );
  10. mainStack.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT + 250));
  11. mainStacks.add(mainStack);
  12. stacks.add(mainStack);
  13. for (int cardInStack = 0; cardInStack &lt;= stack; cardInStack++) {
  14. Card placementCard = deckOfCards.drawCard(); //draws card
  15. placementCard.setIsFaceUp(false);
  16. //checking if the card is the last card in the stack
  17. if (stack == cardInStack) {
  18. System.out.println(stack + cardInStack);
  19. placementCard.setIsFaceUp(true); //card is facing up
  20. //sizes the image and sets it to placementCard
  21. ImageIcon placementCardFace = new ImageIcon(placementCard.getImagePath());
  22. Image placementCardScaled = placementCardFace.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
  23. JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
  24. mainStack.add(cardLabel);
  25. } //end of if statement
  26. //if statement to check whether the card should be revealed or not
  27. if (!placementCard.getIsFaceUp()) { //hide cardFront
  28. //sizes the image and sets it to placementCard
  29. ImageIcon placementCardBack = new ImageIcon(Card.getBackImagePath());
  30. Image placementCardScaled = placementCardBack.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
  31. JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
  32. cardLabel.setPreferredSize(cardSize);
  33. mainStack.add(cardLabel);
  34. } //end of if statement
  35. }
  36. mainStack.addMouseListener(this);
  37. mainStack.addMouseMotionListener(this);
  38. }
  39. JPanel mainStacksWrapper = new JPanel();
  40. mainStacksWrapper.add(mainStacks);
  41. mainStacksWrapper.setBackground(new Color(0, 153, 0));
  42. mainStacks.setBackground(new Color(0, 153, 0));
  43. return mainStacksWrapper;
  44. }

And then finally, in mouseReleased, convert the MouseEvent's point to each stack's parents coordinate context and see if you have a hit or not ...

  1. @Override
  2. public void mouseReleased(MouseEvent e) {
  3. setCursor(null);
  4. if (card == null) {
  5. return;
  6. }
  7. // Make sure the card is no longer painted on the layered pane
  8. card.setVisible(false);
  9. JPanel stackPanel = null;
  10. for (JPanel stack : stacks) {
  11. // Check the bounds of each stack against it&#39;s parents
  12. // coordinates context
  13. Point localPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), stack.getParent());
  14. if (stack.getBounds().contains(localPoint)) {
  15. System.out.println(&quot;--&gt;&quot;);
  16. stackPanel = stack;
  17. break;
  18. }
  19. }
  20. if (stackPanel == null) {
  21. mainStacks.add(card);
  22. } else {
  23. stackPanel.add(card);
  24. stackPanel.revalidate();
  25. stackPanel.repaint();
  26. }
  27. card.setVisible(true);
  28. }

Parting thoughts...

For what it's worth, I wouldn't have done it this way, for all the reasons you're discovering. I would have followed a custom painting route, but I doubt you have time for that now.

huangapple
  • 本文由 发表于 2023年6月19日 05:41:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76502633.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定