# 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);
        }
    }