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

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

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

问题

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

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

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

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

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class Test extends JPanel implements MouseListener, MouseMotionListener {

    //instance variables
    private static final int CARD_WIDTH = 73;
    private static final int CARD_HEIGHT = (int)(CARD_WIDTH*1.49137931+.5);
    private static final int MAIN_STACKS_X_DIFF = 35;
    private static final int MAIN_STACKS_Y_DIFF = 30;

    private final Dimension cardSize = new Dimension(CARD_WIDTH, CARD_HEIGHT);

    private JPanel mainStacks, mainWrapper;

    private Component card;
    private int xAdj, yAdj;

    private Deck deckOfCards;

    //methods

    public Test() {
        JFrame gameScreen = new JFrame();
        gameScreen.setTitle("Solitaire");
        gameScreen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gameScreen.add(this);

        deckOfCards = new Deck();
        deckOfCards.shuffleDeck();

        mainStacks = makeMainStacks();
        mainWrapper = new JPanel();
        mainWrapper.setBackground(new Color(0,153,0));
        mainWrapper.add(mainStacks);

        gameScreen.setLayout(new BorderLayout());
        gameScreen.add(mainWrapper, BorderLayout.CENTER);

        setLayout(null);
        gameScreen.setSize(800,600);
        gameScreen.setResizable(false);
        gameScreen.getContentPane().setBackground(new Color(0,153,0));
        gameScreen.setVisible( true );
    }

    private JPanel makeMainStacks() {
        mainStacks = new JPanel(new GridLayout(0,7,MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
        for (int stack = 0; stack < 7; stack++) {

            JPanel mainStack = new JPanel( new OverlapLayout( new Point(0, 22)));
            mainStack.setBackground( new Color(0,153,0));
            mainStack.setBounds(10,0,CARD_WIDTH, CARD_HEIGHT);
            mainStack.setBorder( new EmptyBorder(10, 0, CARD_WIDTH, CARD_HEIGHT));
            mainStack.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT + 250));
            mainStacks.add(mainStack);
            for (int cardInStack = 0; cardInStack <= stack; cardInStack++) {

                Card placementCard = deckOfCards.drawCard(); //draws card
                placementCard.setIsFaceUp(false);
                //checking if the card is the last card in the stack
                if (stack == cardInStack) {
                    System.out.println(stack + cardInStack);
                    placementCard.setIsFaceUp(true); //card is facing up
                    //sizes the image and sets it to placementCard
                    ImageIcon placementCardFace = new ImageIcon(placementCard.getImagePath());
                    Image placementCardScaled = placementCardFace.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
                    JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
                    mainStack.add(cardLabel);
                } //end of if statement

                //if statement to check whether the card should be revealed or not
                if (!placementCard.getIsFaceUp()) { //hide cardFront
                    //sizes the image and sets it to placementCard
                    ImageIcon placementCardBack = new ImageIcon(Card.getBackImagePath());
                    Image placementCardScaled = placementCardBack.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
                    JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
                    cardLabel.setPreferredSize(cardSize);
                    mainStack.add(cardLabel);
                } //end of if statement
            }
            mainStack.addMouseListener(this);
            mainStack.addMouseMotionListener(this);
        }

        JPanel mainStacksWrapper = new JPanel();
        mainStacksWrapper.add(mainStacks);
        mainStacksWrapper.setBackground(new Color(0,153,0));
        mainStacks.setBackground(new Color(0,153,0));
        return mainStacksWrapper;
    }

    @Override
    public void mousePressed(MouseEvent e) {
        try {
            JPanel stack = (JPanel)e.getComponent();
            card = stack.getComponent(0);

            if (card instanceof JPanel){}

            Point stackPos = card.getParent().getLocation();
            xAdj = stackPos.x-e.getX();
            yAdj = stackPos.y-e.getY();
            card.setLocation(e.getX() + xAdj + 35, e.getY() + yAdj);
            System.out.println(xAdj + yAdj);

            JLayeredPane lp = getRootPane().getLayeredPane();
            lp.add(card,JLayeredPane.DRAG_LAYER);
            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        } catch (ArrayIndexOutOfBoundsException err) {
            System.err.println("No card in stack. Cannot execute move.");
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (card==null){}

        int x = e.getX() + xAdj + 35;
        int y = e.getY() + yAdj;
        card.setLocation(x,y);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        setCursor(null);

        if (card == null) return;

        //  Make sure the card is no longer painted on the layered pane

        card.setVisible(false);

        //  check to see if card was dragged to a stack

        JPanel stackPanel = null;
        Point cardLocation = card.getLocation();
        cardLocation.y -= mainStacks.getParent().getLocation().y;

        for (int i = 0; i < mainStacks.getComponentCount(); i++)
        {
            JPanel panel = (JPanel)mainStacks.getComponent(i);

            if (panel.getBounds().contains(cardLocation))
            {
                stackPanel = panel;
                break;
            }
        }

        if (stackPanel == null)
            mainStacks.add( card );
        else
            stackPanel.add( card );

        card.setVisible(true);
    }

    @Override
    public void mouseClicked(MouseEvent e) {}
    @Override
    public void mouseMoved(MouseEvent e) {}
    @Override
    public void mouseEntered(MouseEvent e) {}
    @Override
    public void mouse

<details>
<summary>英文:</summary>

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.

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?

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.


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):
```java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class Test extends JPanel implements MouseListener, MouseMotionListener {
    
    //instance variables
    private static final int CARD_WIDTH = 73;
    private static final int CARD_HEIGHT = (int)(CARD_WIDTH*1.49137931+.5);
    private static final int MAIN_STACKS_X_DIFF = 35;
    private static final int MAIN_STACKS_Y_DIFF = 30;
    
    private final Dimension cardSize = new Dimension(CARD_WIDTH, CARD_HEIGHT);
    
    private JPanel mainStacks, mainWrapper;
        
    private Component card;
    private int xAdj, yAdj;
    
    private Deck deckOfCards;
        
    //methods
    
    public Test() {
        JFrame gameScreen = new JFrame();
        gameScreen.setTitle(&quot;Solitaire&quot;);
        gameScreen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gameScreen.add(this);
        
        deckOfCards = new Deck();
        deckOfCards.shuffleDeck();
        
        mainStacks = makeMainStacks();
        mainWrapper = new JPanel();
        mainWrapper.setBackground(new Color(0,153,0));
        mainWrapper.add(mainStacks);
        
        gameScreen.setLayout(new BorderLayout());
        gameScreen.add(mainWrapper, BorderLayout.CENTER);
        
        setLayout(null);
        gameScreen.setSize(800,600);
        gameScreen.setResizable(false);
        gameScreen.getContentPane().setBackground(new Color(0,153,0));
        gameScreen.setVisible( true );
    }
        
    private JPanel makeMainStacks() {
        mainStacks = new JPanel(new GridLayout(0,7,MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
        for (int stack = 0; stack &lt; 7; stack++) {
            
            JPanel mainStack = new JPanel( new OverlapLayout( new Point(0, 22)));
            mainStack.setBackground( new Color(0,153,0));
            mainStack.setBounds(10,0,CARD_WIDTH, CARD_HEIGHT);
            mainStack.setBorder( new EmptyBorder(10, 0, CARD_WIDTH, CARD_HEIGHT));
            mainStack.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT + 250));
            mainStacks.add(mainStack);
            for (int cardInStack = 0; cardInStack &lt;= stack; cardInStack++) {   
                
                Card placementCard = deckOfCards.drawCard(); //draws card
                placementCard.setIsFaceUp(false);
                //checking if the card is the last card in the stack
                if (stack == cardInStack) {
                    System.out.println(stack + cardInStack);
                    placementCard.setIsFaceUp(true); //card is facing up
                    //sizes the image and sets it to placementCard
                    ImageIcon placementCardFace = new ImageIcon(placementCard.getImagePath());
                    Image placementCardScaled = placementCardFace.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
                    JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
                    mainStack.add(cardLabel);
                } //end of if statement
                
                //if statement to check whether the card should be revealed or not
                if (!placementCard.getIsFaceUp()) { //hide cardFront
                    //sizes the image and sets it to placementCard
                    ImageIcon placementCardBack = new ImageIcon(Card.getBackImagePath());
                    Image placementCardScaled = placementCardBack.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
                    JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
                    cardLabel.setPreferredSize(cardSize);
                    mainStack.add(cardLabel);
                } //end of if statement
            }
            mainStack.addMouseListener(this);
            mainStack.addMouseMotionListener(this);
        }
        
        JPanel mainStacksWrapper = new JPanel();
        mainStacksWrapper.add(mainStacks);
        mainStacksWrapper.setBackground(new Color(0,153,0));
        mainStacks.setBackground(new Color(0,153,0));
        return mainStacksWrapper;
    }
    
    
    @Override
    public void mousePressed(MouseEvent e) {
        try {
        JPanel stack = (JPanel)e.getComponent();
        card = stack.getComponent(0);
        
        if (card instanceof JPanel){}
        
        Point stackPos = card.getParent().getLocation();
        xAdj = stackPos.x-e.getX();
        yAdj = stackPos.y-e.getY();
        card.setLocation(e.getX() + xAdj + 35, e.getY() + yAdj);
        System.out.println(xAdj + yAdj);
        
        JLayeredPane lp = getRootPane().getLayeredPane();
        lp.add(card,JLayeredPane.DRAG_LAYER);
        setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        } catch (ArrayIndexOutOfBoundsException err) {
            System.err.println(&quot;No card in stack. Cannot execute move.&quot;);
        }
    }
    
    @Override
    public void mouseDragged(MouseEvent e) {
        if (card==null){}
        
        int x = e.getX() + xAdj + 35;
        int y = e.getY() + yAdj;
        card.setLocation(x,y);
    }
    
    @Override
    public void mouseReleased(MouseEvent e) {
        setCursor(null);

        if (card == null) return;

        //  Make sure the card is no longer painted on the layered pane

        card.setVisible(false);

        //  check to see if card was dragged to a stack

        JPanel stackPanel = null;
        Point cardLocation = card.getLocation();
        cardLocation.y -= mainStacks.getParent().getLocation().y;

        for (int i = 0; i &lt; mainStacks.getComponentCount(); i++)
        {
            JPanel panel = (JPanel)mainStacks.getComponent(i);

            if (panel.getBounds().contains(cardLocation))
            {
                stackPanel = panel;
                break;
            }
        }

        if (stackPanel == null)
            mainStacks.add( card );
        else
            stackPanel.add( card );

        card.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) {
        new Test();
    }
}

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

import java.util.ArrayList;
import java.util.Collections;

public class Deck {
    
    //instance variables
    private final ArrayList&lt;Card&gt; cards;
    private int drawCardIndex;
    
    //methods
    /**
     * This constructor for the Deck class creates all 52 cards and adds them to cards List.
     */
    public Deck() {
        cards = new ArrayList&lt;&gt;(); //creating new deck
        
        for (Suit suit: Suit.values()) { //loops through suits
            for (Rank rank: Rank.values()) { //loops through ranks
                cards.add(new Card(suit, rank)); //adds a unique Card to cards
            }
        }
        drawCardIndex = 0; //initializes index
    } //end of constructor
    
    /**
     * This method returns the size of the deck.
     * @return int - size of deck
     */
    public int getSize() {
        return cards.size();
    } //end of getSize method
    
    /**
     * This method returns a drawn card in the deck of cards.
     * @return Card - a new drawn card in the deck of cards.
     */
    public Card drawCard() {
        if (drawCardIndex&lt;cards.size()) { //checks if all of the cards have been drawn
            Card card = cards.get(drawCardIndex); //draws a card
            drawCardIndex++; //increments
            return card; //return statement
        }
        return null; //returns null if all of the cards have already been drawn
    } //end of drawCard method
    
    /**
     * This method shuffles the deck of cards.
     */
    public void shuffleDeck() {
        Collections.shuffle(cards); //mixes ArrayList
        resetDrawCardIndex(); //resets the drawCardIndex variable
    } //end of shuffleDeck method
    
    /**
     * This method resets the drawCardIndex.
     */
    public void resetDrawCardIndex() {
        drawCardIndex = 0;
    } //end of resetDrawCardIndex method
    
    /**
     * This method removes a card from the deck.
     * @param card the card to be removed from the deck
     */
    public void removeCardFromDeck(Card card) {
        //checks if the stack is empty
        cards.remove(card); //removes the card from the deck
    } //end of removeCardFromStack method
    
    /**
     * This method checks if all of the cards have been drawn.
     * @return Boolean true or false based on if all the cards have been drawn or not.
     */
    public boolean isEmpty() {
        return cards.size() &lt;= drawCardIndex;
    } //end of isEmpty method
    
} //end of Deck class
public class Card {
    //instance variables
    private final Suit SUIT;
    private final Rank RANK;
    private boolean isFaceUp;
    
    //methods
    /**
     * This is the constructor method for the Card class.
     * @param suit - the suit to be assigned to the card.
     * @param rank - the rank to be assigned to the card.
     */
    public Card(Suit suit, Rank rank) {
        this.SUIT = suit;
        this.RANK = rank;
        this.isFaceUp = false;
    } //end of constructor
    
    /**
     * This method gives the user the suit of the Card.
     * @return Suit - the suit of the Card.
     */
    public Suit getSuit() {
        return this.SUIT;
    } //end of getSuit method
    /**
     * This method gives the user the rank of the Card.
     * @return Rank - the rank of the Card.
     */
    public Rank getRank() {
        return this.RANK;
    } //end of getRank method
    
    /**
     * This method gives whether the Card is faced up or not.
     * @return Boolean - true or false based on if the Card is facing up.
     */
    public boolean getIsFaceUp() {
        return this.isFaceUp;
    } //end of getIsFaceUp method
    /**
     * This method sets the isFaceUp variable to true if the card is facing up in the game.
     */
    public void setIsFaceUp(boolean b) {
        this.isFaceUp = b;
    } //end of setIsFaceUp method
    
    /**
     * This method determines if the Card is from a red suit.
     * @return Boolean - true or false if card is red
     */
    public boolean isRed() {
        return this.SUIT == Suit.HEARTS || this.SUIT == Suit.DIAMONDS;
    } //end of isRed method
    /**
     * This method determines if the Card is from a black suit.
     * @return a Boolean true or false if card is black
     */
    public boolean isBlack() {
        return this.SUIT == Suit.CLUBS || this.SUIT == Suit.SPADES;
    } //end of isBlack method
    
    /**
     * This method determines if the card can be stacked on the card behind it.
     * @return a Boolean true or false if it can be stacked
     */
    public boolean stackable(Card card) {
        return this.isRed() != card.isRed();
    } //end of stackable method
    
    /**
     * This method gives the name of each card.
     * @return a string that has two letters- the first letter of the rank and the first letter of the suit.
     */
    public String getName() {
        String number = &quot;&quot;; //variable declaration
        switch(RANK) { //switch to assign rank name
            case ACE -&gt; number = &quot;A&quot;; //case
            case TWO -&gt; number = &quot;2&quot;; //case
            case THREE -&gt; number = &quot;3&quot;; //case
            case FOUR -&gt; number = &quot;4&quot;; //case
            case FIVE -&gt; number = &quot;5&quot;; //case
            case SIX -&gt; number = &quot;6&quot;; //case
            case SEVEN -&gt; number = &quot;7&quot;; //case
            case EIGHT -&gt; number = &quot;8&quot;; //case
            case NINE -&gt; number = &quot;9&quot;; //case
            case TEN -&gt; number = &quot;10&quot;; //case
            case JACK -&gt; number = &quot;J&quot;; //case
            case QUEEN -&gt; number = &quot;Q&quot;; //case
            case KING -&gt; number = &quot;K&quot;; //case
        } //end of switch
        String suitName = &quot;&quot;; //variable declaration
        switch(SUIT) { //switch to assign suit name
            case HEARTS -&gt; suitName = &quot;H&quot;; //case
            case DIAMONDS -&gt; suitName = &quot;D&quot;; //case
            case SPADES -&gt; suitName = &quot;S&quot;; //case
            case CLUBS -&gt; suitName = &quot;C&quot;; //case
        } //end of switch
        return number + suitName; //return statement
    } //end of getName method
    
    /**
     * This method gives the path to a corresponding image.
     * @return String - returns the path of the image corresponding with the card.
     */
    public String getImagePath() {
        return &quot;C:\\ICS3U_SUMMATIVE_SOLITAIRE\\src\\cards\\&quot; + getName().substring(1,getName().length()) + &quot;\\&quot; + getName() + &quot;.jpg&quot;; //return statement
    } //end of getImage method
    /**
     * This method gives the file path to the back of the card image.
     * @return String the file path to the card back image
     */
    public static String getBackImagePath() {
        return &quot;C:\\ICS3U_SUMMATIVE_SOLITAIRE\\src\\CardBack.jpg&quot;;
    } //end of getImageBackPath method
    
    @Override
    /**
     * This method returns the description about the instance variables.
     * @return String - rank and suit of card along with image path.
     */
    public String toString() {
        return this.RANK + &quot; of &quot; + this.SUIT + &quot; ---- &quot; + getImagePath();
    }
} //end of Card class
public enum Suit {
   //enum fields
   /**
    * Hearts, Diamonds,
    */
   HEARTS,DIAMONDS,
   /**
    * Spades, Clubs
    */
   SPADES,CLUBS;
}
public enum Rank {
   //enum fields
   /**
    * 2,3,4,5,6,7,8,9,10
    */
   TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN,
   /**
    * A,J,Q,K
    */
   ACE,JACK,QUEEN,KING;
}

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)

@Override
public void mousePressed(MouseEvent e) {
    try {
        JPanel stack = (JPanel) e.getComponent();
        card = stack.getComponent(0);

        if (card instanceof JPanel) {
        }

        Point cardInWorld = SwingUtilities.convertPoint(card.getParent(), card.getLocation(), getRootPane().getLayeredPane());
        Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());

        Point stackPos = card.getParent().getLocation();
        xAdj = pointInWorld.x - cardInWorld.getLocation().x;
        yAdj = pointInWorld.y - cardInWorld.getLocation().y;

        JLayeredPane lp = getRootPane().getLayeredPane();
        lp.add(card, JLayeredPane.DRAG_LAYER);
        card.setLocation(cardInWorld);
        setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
    } catch (ArrayIndexOutOfBoundsException err) {
        System.err.println("No card in stack. Cannot execute move.");
    }
}

@Override
public void mouseDragged(MouseEvent e) {
    if (card == null) {
    }

    Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());

    int x = pointInWorld.x - xAdj;
    int y = pointInWorld.y - yAdj;
    card.setLocation(x, y);
}

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

第二部分...

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

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

private JPanel makeMainStacks() throws IOException {
    mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
    //...
    JPanel mainStacksWrapper = new JPanel();
    mainStacksWrapper.add(mainStacks);
    mainStacksWrapper.setBackground(new Color(0, 153, 0));
    mainStacks.setBackground(new Color(0, 153, 0));
    return mainStacksWrapper;
}

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

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

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

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

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

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

private JPanel makeMainStacks() throws IOException {
    JPanel mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
    mainStacks.setBackground(Color.MAGENTA);
    for (int stack = 0; stack < 7; stack++) {
        //...

        stacks.add(mainStack);

        //...
    }
    //...
}

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

@Override
public void mouseReleased(MouseEvent e) {
    setCursor(null);

    if (card == null) {
        return;
    }

    //  Make sure the card is no longer painted on the layered pane
    card.setVisible(false);

    JPanel stackPanel = null;
    for (JPanel stack : stacks) {
        // Check the bounds of each stack against its parent's
        // coordinates context
        Point localPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), stack.getParent());
        if (stack.getBounds().contains(localPoint)) {
            System.out.println("-->");
            stackPanel = stack;
            break;
        }
    }

    if (stackPanel == null) {
        mainStacks.add(card);
    } else {
        stackPanel.add(card);
        stackPanel.revalidate();
        stackPanel.repaint();
    }

    card.setVisible(true);
}

结束语...

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

英文:

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)

    @Override
public void mousePressed(MouseEvent e) {
try {
JPanel stack = (JPanel) e.getComponent();
card = stack.getComponent(0);
if (card instanceof JPanel) {
}
Point cardInWorld = SwingUtilities.convertPoint(card.getParent(), card.getLocation(), getRootPane().getLayeredPane());
Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());
Point stackPos = card.getParent().getLocation();
xAdj = pointInWorld.x - cardInWorld.getLocation().x;
yAdj = pointInWorld.y - cardInWorld.getLocation().y;
JLayeredPane lp = getRootPane().getLayeredPane();
lp.add(card, JLayeredPane.DRAG_LAYER);
card.setLocation(cardInWorld);
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
} catch (ArrayIndexOutOfBoundsException err) {
System.err.println(&quot;No card in stack. Cannot execute move.&quot;);
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (card == null) {
}
Point pointInWorld = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), getRootPane().getLayeredPane());
int x = pointInWorld.x - xAdj;
int y = pointInWorld.y - yAdj;
card.setLocation(x, y);
}

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...

@Override
public void mouseReleased(MouseEvent e) {
setCursor(null);
if (card == null) return;
//  Make sure the card is no longer painted on the layered pane
card.setVisible(false);
//  check to see if card was dragged to a stack
JPanel stackPanel = null;
Point cardLocation = card.getLocation();
cardLocation.y -= mainStacks.getParent().getLocation().y;
for (int i = 0; i &lt; mainStacks.getComponentCount(); i++)
{
JPanel panel = (JPanel)mainStacks.getComponent(i);
if (panel.getBounds().contains(cardLocation))
{
stackPanel = panel;
break;
}
}
if (stackPanel == null)
mainStacks.add( card );
else
stackPanel.add( card );
card.setVisible(true);
}

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

The source of the problem starts right here...

private JPanel makeMainStacks() throws IOException {
mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
//...
JPanel mainStacksWrapper = new JPanel();
mainStacksWrapper.add(mainStacks);
mainStacksWrapper.setBackground(new Color(0, 153, 0));
mainStacks.setBackground(new Color(0, 153, 0));
return mainStacksWrapper;
}

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...

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

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

private JPanel makeMainStacks() throws IOException {
JPanel mainStacks = new JPanel(new GridLayout(0, 7, MAIN_STACKS_X_DIFF, MAIN_STACKS_Y_DIFF));
mainStacks.setBackground(Color.MAGENTA);
for (int stack = 0; stack &lt; 7; stack++) {
JPanel mainStack = new JPanel(new OverlapLayout(new Point(0, 22)));
mainStack.setBounds(10, 0, CARD_WIDTH, CARD_HEIGHT);
mainStack.setBorder(
new CompoundBorder(new LineBorder(Color.RED), new EmptyBorder(10, 0, CARD_WIDTH, CARD_HEIGHT))
);
mainStack.setPreferredSize(new Dimension(CARD_WIDTH, CARD_HEIGHT + 250));
mainStacks.add(mainStack);
stacks.add(mainStack);
for (int cardInStack = 0; cardInStack &lt;= stack; cardInStack++) {
Card placementCard = deckOfCards.drawCard(); //draws card
placementCard.setIsFaceUp(false);
//checking if the card is the last card in the stack
if (stack == cardInStack) {
System.out.println(stack + cardInStack);
placementCard.setIsFaceUp(true); //card is facing up
//sizes the image and sets it to placementCard
ImageIcon placementCardFace = new ImageIcon(placementCard.getImagePath());
Image placementCardScaled = placementCardFace.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
mainStack.add(cardLabel);
} //end of if statement
//if statement to check whether the card should be revealed or not
if (!placementCard.getIsFaceUp()) { //hide cardFront
//sizes the image and sets it to placementCard
ImageIcon placementCardBack = new ImageIcon(Card.getBackImagePath());
Image placementCardScaled = placementCardBack.getImage().getScaledInstance(CARD_WIDTH, CARD_HEIGHT, Image.SCALE_SMOOTH);
JLabel cardLabel = new JLabel(new ImageIcon(placementCardScaled));
cardLabel.setPreferredSize(cardSize);
mainStack.add(cardLabel);
} //end of if statement
}
mainStack.addMouseListener(this);
mainStack.addMouseMotionListener(this);
}
JPanel mainStacksWrapper = new JPanel();
mainStacksWrapper.add(mainStacks);
mainStacksWrapper.setBackground(new Color(0, 153, 0));
mainStacks.setBackground(new Color(0, 153, 0));
return mainStacksWrapper;
}

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 ...

@Override
public void mouseReleased(MouseEvent e) {
setCursor(null);
if (card == null) {
return;
}
//  Make sure the card is no longer painted on the layered pane
card.setVisible(false);
JPanel stackPanel = null;
for (JPanel stack : stacks) {
// Check the bounds of each stack against it&#39;s parents
// coordinates context
Point localPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), stack.getParent());
if (stack.getBounds().contains(localPoint)) {
System.out.println(&quot;--&gt;&quot;);
stackPanel = stack;
break;
}
}
if (stackPanel == null) {
mainStacks.add(card);
} else {
stackPanel.add(card);
stackPanel.revalidate();
stackPanel.repaint();
}
card.setVisible(true);
}

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:

确定