1

I am using a JFrame to set up a game of Solitaire, using Cards that extend JLabel so that they can be dragged around the screen. However, one of my requirements is that I be able to double click a card and it snaps up to the 4 stacks of aces. This is not a problem. what is causing problems is that I set this up in an array, so that the aces would go to the spot that corresponds with the suit of the ace, then I have the spots rearrange if a card is dragged up there. If it is double clicked, the aces must go to the first spot (from the left) then the second, and so on. I had planned to use getComponentAt() to find out what was at the first spot and if I could place the ace there, if not then i move on to the second and so on. For some reason though, even if I hard code in the parameters into getComponentAt() to spots I know have a component, it is still returning null.

this is the relevant portion of my code:

Container contentPane = getContentPane();
contentPane.setLayout(null);
...
for(int i = 0; i < 4; i++)
{
    aces[i] = new Card();
    aces[i].setBounds((i * 75) + 475, 25, 75, 100);
    contentPane.add(aces[i]);
    aces[i].setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
    aces[i].setSuit(i + 1);

}
System.out.println(contentPane.getComponentAt(475, 25));

This is returning null every time, no matter where in the component i put the coordinates. Any explanations?

*Updated SSCCE: This is the main class, solitaire:

import java.util.*;
import javax.swing.*;
import java.awt.*;

public class Solitaire extends JFrame
{
    private Container contentPane;

    private Card aces[];

    public static void main(String args[])
    {
        Solitaire frame = new Solitaire();
        frame.setVisible(true);
    }

    public Solitaire()
    {
        super();

        contentPane = getContentPane();
        contentPane.setLayout(null);
        contentPane.setBackground(Color.GREEN.darker().darker());

        setTitle("Solitaire");
        setSize(800,800);
        setResizable(false);
        setLocation(250, 50);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        play();
    }

    public void play()
    {
        contentPane.removeAll();

        aces = new Card[4];

        for(int i = 0; i < 4; i++)
        {
            aces[i] = new Card(0,i + 1);
            aces[i].setBounds((i * 75) + 475, 25, 75, 100);
            contentPane.add(aces[i]);
            aces[i].setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));


        }
        for(int i = 0; i < contentPane.getComponents().length; i++)
        System.out.println(contentPane.getComponents()[i]);

        System.out.println(contentPane.getComponentAt(475, 25));
       repaint();
    }
}

and this is Class Card:

import javax.swing.*;
public class Card extends JLabel
{
    private int numb;

    private int value;

    private int suit;

    public Card(int n, int s)
    {
        super();
        numb = n;
        suit = s;
    }
}

This ran for me, comment if you need more to solve the problem.

Harper
  • 140
  • 2
  • 4
  • 10
  • For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Apr 18 '13 at 03:47
  • Use `contentpane.getComponentAt(index)` – Amarnath Apr 18 '13 at 03:47
  • @AndrewThompson So do you have that response programmed to a function key? ;) – MadProgrammer Apr 18 '13 at 03:47
  • @Che That will just return the component in the list of components based on the order that they were added... – MadProgrammer Apr 18 '13 at 03:48
  • 1
    @MadProgrammer No, no. I have a web page of common responses. It is more copy/paste than function key. ;) – Andrew Thompson Apr 18 '13 at 03:49
  • ok, 1 sec on the SSCCE, i need to trim a lot, should i include the Javadocing to make it more readable? – Harper Apr 18 '13 at 03:58
  • @Che for index, is that the componentZOrder or the order it was added? because i mess with Z order quite a bit throughout the program. – Harper Apr 18 '13 at 03:58
  • 1
    `getComponentAt` first checks to see if the given coordinates fall within it self (ie the content pane), if it doesn't it will return `null`. I tested this by calling `setVisible` before and after I added the "cards" and if the frame was visible first, it worked fine, otherwise it returned null...You should also beware that it will return itself if no other component exists at the point – MadProgrammer Apr 18 '13 at 04:03
  • @Harper It will be based on the order in which you have added your components – Amarnath Apr 18 '13 at 04:11

1 Answers1

3

There are lots of problems with using getComponentAt

The first is, the parent container must actually be realized and sized. If not, it will return null. The method is capable of returning the container itself if no children exist at the location.

I then child Component#contains to test to see if the given point feel within the child components directly, but this wants the point to be translated into the child's coordinate space...

So instead, I simply went straight to getBounds...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Point;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestComponentLocation {

    public static void main(String[] args) {
        new TestComponentLocation();
    }

    public TestComponentLocation() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.pack();
                frame.setLocationRelativeTo(null);


                Container contentPane = frame.getContentPane();
                contentPane.setLayout(null);
                for (int i = 0; i < 4; i++) {
                    JPanel panel = new JPanel();
                    panel.setBounds((i * 75) + 475, 25, 75, 100);
                    System.out.println(panel.getBounds());
                    contentPane.add(panel);
                    panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
                }
                System.out.println(getComponentAt(contentPane, new Point(475, 25)));
                System.out.println(getComponentAt(contentPane, new Point(100, 25)));

                frame.setVisible(true);

            }
        });
    }

    public Component getComponentAt(Container parent, Point p) {
        Component comp = null;
        for (Component child : parent.getComponents()) {
            if (child.getBounds().contains(p)) {
                comp = child;
            }
        }
        return comp;
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Ok, I just added the your code for getComponentAt() to my version of the SSCCE and it is returning my frame, not the component at 475, 25. Is there something specific about my Cards that does not work? – Harper Apr 18 '13 at 04:33
  • However, I just tried it with Just your code using JPanels instead of Cards, and it worked. so it is something about Card, does the JLabel have to have an imageIcon/be opaque for getComponentAt() to find it? – Harper Apr 18 '13 at 04:39
  • Shouldn't make a difference. Just make sure you're passing the immediate parent containing the children you want to find. The method works based on the bounds of the child – MadProgrammer Apr 18 '13 at 04:45
  • Just confirming, I added it to the contentPane so that is the parent, right? cause I'm going to feel incredibly stupid if that has been my problem this entire time. – Harper Apr 18 '13 at 04:48
  • Yes, so in your case you would do something like `getComponentAt(getContentPane(), point)` ... – MadProgrammer Apr 18 '13 at 04:53
  • Ok, so i just added that entire block in again, and it worked perfectly. i had misspelled something in a conversion, and it wasn't working till i copied and pasted the entire thing again. that works beautifully, thank you! I do have to ask though, how much time do you spend on here? Every time i ask a question, you have almost always commented or answered withing 30 minutes. – Harper Apr 18 '13 at 05:05
  • My wife would say to much ;) - I've learned a lot from seeing what other people say about questions and simply trying things out (like this) ;) – MadProgrammer Apr 18 '13 at 05:07
  • Haha, Ok. well thank you, that was a far better way to do it than any of the makeshift things i had going and is definitely a way I need to consider for other problems in the future. – Harper Apr 18 '13 at 05:13