Java: Example - Card Demo

This program displays card images, which can then be dragged around the screen. The images are stored in an array that represents the z coordinate (what's on top of what). This order is never changed even if a card is dragged, which give the peculiar effect of dragging under.

It is divided into several source files. The card images are in a zipped file.

CardDemo1.java subclasses JFrame, but does little more than initialize the cards, and build a GUI with the CardTable as the content pane.

CardTable.java is a subclass of JComponent that displays the cards and handles mouse events.

Card.java represents a single card.

cards20.zip is a zipped file of the card images (.gif), including a back and joker. These images are GPLed, and don't use the MIT License of this page.

CardDemo1.java - The main program as a subclass of JFrame

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
// File   : GUI-lowlevel/cards1/cards/CardDemo1
// Purpose: Basic GUI to show dragging cards.
//          Illustrates how to load images from files.
// Author : Fred Swartz - 2007-02-19 - Placed in public domain.
//
// Enhancements:
//        * This really doesn't have a user interface beyond dragging.
//          It doesn't do anything, and therefore has no model.
//          Make it play a game.
//        * Needs to have a Deck class to shuffle, deal, ... Cards.
//          Persumably based on ArrayList<Card>.
//        * Perhaps a Suit and Face class would be useful.
//        * Like Deck, there would also be a class for Hand.
//        * May need Player class too.

package cards;

import java.net.URL;
import javax.swing.*;

////////////////////////////////////////////////////////////// class CardDemoGUI

class CardDemo1 extends JFrame {
    //=================================================================== fields
    
    private static Card[] _deck = new Card[52];
    
    //===================================================================== main
    public static void main(String[] args) {
        CardDemo1 window = new CardDemo1();
        window.setTitle("Card Demo 1");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setContentPane(new CardTable(_deck));
        window.pack();
        window.setLocationRelativeTo(null);
        window.setVisible(true);
    }
    
    //============================================================== constructor
    public CardDemo1() {
        //... ClassLoader is where to get images from this .jar file.
        ClassLoader cldr = this.getClass().getClassLoader();
        
        int n = 0;         // Which card.
        int xPos = 0;      // Where it should be placed initially.
        int yPos = 0;
        
        //... Read in the cards using particular file name conventions.
        //    Images for the backs and Jokers are ignored here.
        String suits = "shdc";
        String faces = "a23456789tjqk";
        for (int suit=0; suit < suits.length(); suit++) {
            for (int face=0; face < faces.length(); face++) {
                //... Get the image from the images subdirectory.
                String imagePath = "cards/images/" + faces.charAt(face) +
                        suits.charAt(suit) + ".gif";
                URL imageURL = cldr.getResource(imagePath);
                ImageIcon img = new ImageIcon(imageURL);
                
                //... Create a card and add it to the deck.
                Card card = new Card(img);
                card.moveTo(xPos, yPos);
                _deck[n] = card;
                
                //... Update local vars for next card.
                xPos += 5;
                yPos += 4;
                n++;
            }
        }
    }
}

CardTable.java - A subclass of JComponent for displaying the cards

This displays the cards, and listens to mouse events so that it can drag the cards around. There should really be a "model" of a card game, and this would simply interrogate the model to find out what to display, and pass mouse actions to the model.

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 
 86 
 87 
 88 
 89 
 90 
 91 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
// File   : GUI-lowlevel/cards1/cards/CardTable.java
// Purpose: This is just a JComponent for drawing the cards that are
//          showing on the table.
//
// Author : Fred Swartz - February 19, 2007 - Placed in public domain.
//
// Enhancements:
//        * Use model. Currently, it is initialized with a whole deck of cards,
//          but instead it should be intialized with a "model" which
//          it should interrogate (calling model methods) to find out what
//          should be displayed.
//        * Similarly, actions by the mouse might be used to set things in the
//          model, Perhaps by where it's dragged to, or double-clicked, or
//          with pop-up menu, or ...

package cards;

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

////////////////////////////////////////////////////////////////////// CardTable
public class CardTable extends JComponent implements MouseListener,
        MouseMotionListener {
    
    //================================================================ constants
    private static final Color BACKGROUND_COLOR = Color.GREEN;
    private static final int   TABLE_SIZE       = 400;    // Pixels.
    
    //=================================================================== fields
    //... Initial image coords.
    private int _initX     = 0;   // x coord - set from drag
    private int _initY     = 0;   // y coord - set from drag
    
    //... Position in image of mouse press to make dragging look better.
    private int _dragFromX = 0;  // Displacement inside image of mouse press.
    private int _dragFromY = 0;
    
    private Card[] _deck;        // Should really be in a model, but ...
    private Card   _currentCard = null;  // Current selected card.
    
    //============================================================== constructor
    public CardTable(Card[] deck) {
        _deck = deck;                // Should be passed a model.
        
        //... Initialize graphics
        setPreferredSize(new Dimension(TABLE_SIZE, TABLE_SIZE));
        setBackground(Color.blue);
        
        //... Add mouse listeners.
        addMouseListener(this);
        addMouseMotionListener(this);
    }
    
    //=========================================================== paintComponent
    @Override
    public void paintComponent(Graphics g) {
        //... Paint background
        int width = getWidth();
        int height = getHeight();
        g.setColor(BACKGROUND_COLOR);
        g.fillRect(0, 0, width, height);
        
        //... Display the cards, starting with the first array element.
        //    The array order defines the z-axis depth.
        for (Card c : _deck) {
            c.draw(g, this);
        }
    }
    
    //====================================================== method mousePressed
    // Check to see if press is within any card.
    // If it is,
    // * Set _currentCard so mouseDragged knows what to drag.
    // * Record where in the image (relative to the upper left coordinates)
    //   the mouse was clicked, because it looks best if we drag from there.
    // TODO: Move the card to the last position so that it displays on top.
    public void mousePressed(MouseEvent e) {
        int x = e.getX();   // Save the x coord of the click
        int y = e.getY();   // Save the y coord of the click
        
        //... Find card image this is in.  Check from top down.
        _currentCard = null;  // Assume not in any image.
        for (int crd=_deck.length-1; crd>=0; crd--) {
            Card testCard = _deck[crd];
            if (testCard.contains(x, y)) {
                //... Found, remember this card for dragging.
                _dragFromX = x - testCard.getX();  // how far from left
                _dragFromY = x - testCard.getY();  // how far from top
                _currentCard = testCard;  // Remember what we're dragging.
                break;        // Stop when we find the first match.
            }
        }
    }
    
    //============================================================= mouseDragged
    // Set x,y to mouse position and repaint.
    public void mouseDragged(MouseEvent e) {
        if (_currentCard != null) {   // Non-null if pressed inside card image.
            
            int newX = e.getX() - _dragFromX;
            int newY = e.getY() - _dragFromY;
            
            //--- Don't move the image off the screen sides
            newX = Math.max(newX, 0);
            newX = Math.min(newX, getWidth() - _currentCard.getWidth());
            
            //--- Don't move the image off top or bottom
            newY = Math.max(newY, 0);
            newY = Math.min(newY, getHeight() - _currentCard.getHeight());
            
            _currentCard.moveTo(newX, newY);
            
            this.repaint(); // Repaint because position changed.
        }
    }
    
    //======================================================= method mouseExited
    // Turn off dragging if mouse exits panel.
    public void mouseExited(MouseEvent e) {
        _currentCard = null;
    }
    
    //=============================================== Ignore other mouse events.
    public void mouseMoved   (MouseEvent e) {}  // ignore these events
    public void mouseEntered(MouseEvent e) {}  // ignore these events
    public void mouseClicked(MouseEvent e) {}  // ignore these events
    public void mouseReleased(MouseEvent e) {}  // ignore these events
}

Card.java - Class representing one card

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
// File   : GUI-lowlevel/cards1/cards/Card.java
// Purpose: Represents one card.
// Author : Fred Swartz - February 19, 2007 - Placed in public domain.
//
// Enhancements:
//          * Needs to have Suit and Face value.

package cards;

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

/////////////////////////////////////////////////////////////////////////// Card
class Card {
    //=================================================================== fields
    private ImageIcon _image;
    private int       _x;
    private int       _y;
    
    //============================================================== constructor
    public Card(ImageIcon image) {
        _image = image;
    }
    
    //=================================================================== moveTo
    public void moveTo(int x, int y) {
        _x = x;
        _y = y;
    }
    
    //================================================================= contains
    public boolean contains(int x, int y) {
        return (x > _x && x < (_x + getWidth()) && 
                y > _y && y < (_y + getHeight()));
    }
    
    //================================================================= getWidth
    public int getWidth() {
        return _image.getIconWidth();
    }
    
    //================================================================ getHeight
    public int getHeight() {
        return _image.getIconHeight();
    }
    
    //===================================================================== getX
    public int getX() {
        return _x;
    }
    
    //===================================================================== getY
    public int getY() {
        return _x;
    }
    
    //===================================================================== draw
    public void draw(Graphics g, Component c) {
        _image.paintIcon(c, g, _x, _y);
    }
}