Sunday, March 20, 2011

How to display a deck of playing card in a panel ?

In the previous article, I have demonstrated
How to display a GUI component in a customized way.

I created a graphics panel and drew a circle and a line on it.

In this article I am going to draw a deck of 52 playing cards to a customized panel.

To begin, it is recommended to take a look at the previous article first.

The idea is to derive a class from JPanel and override the paintComponent method.

// override the paint method
  public void paintComponent(java.awt.Graphics graphics)
  {
    super.paintComponent(graphics);  // paint background 

    // get panel dimemsion
    int w=getWidth();  // should be 200
    int h=getHeight(); // should be 200

    // create a Graphics2D object for drawing shape
    java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

    // draw a white playing card
    int cardWidth=60, cardHeight=80;
    gr.setColor(java.awt.Color.WHITE);
    gr.fillRect(0,0,cardWidth,cardHeight);
    gr.setColor(java.awt.Color.BLACK);  // with black border
    gr.drawRect(0,0,cardWidth-1,cardHeight-1);

    // draw the "three of Spade"
    Font font=new Font("Dialog",Font.PLAIN, 28);
    gr.setFont(font);
    String spade="\u2660";   // unicode for the "spade" character
    gr.drawString(spade+"3",5,45);

  }


Hence we come up with the first version of CardPanel.java, which is very similar to GraphicsPanel.java in the previous article.

/******************************************************************************
* File : CardPanel.java (version 1)
* Author : http://java.macteki.com/
* Description :
*   Display a shuffled deck of playing card.
* Tested with : JDK 1.6 under Windows XP and MAC osx 10.6.6
******************************************************************************/

import java.awt.Font;

class CardPanel extends javax.swing.JPanel
{

  public static CardPanel getInstance()
  {
    CardPanel panel=new CardPanel();
    panel.setBackground(new java.awt.Color(255,255,128));
    panel.setPreferredSize(new java.awt.Dimension(200,200));
    return panel;
  }

  // override the paint method
  public void paintComponent(java.awt.Graphics graphics)
  {
    super.paintComponent(graphics);  // paint background 

    // get panel dimemsion
    int w=getWidth();  // should be 200
    int h=getHeight(); // should be 200

    // create a Graphics2D object for drawing shape
    java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

    // draw a white playing card
    int cardWidth=60, cardHeight=80;
    gr.setColor(java.awt.Color.WHITE);
    gr.fillRect(0,0,cardWidth,cardHeight);
    gr.setColor(java.awt.Color.BLACK);  // with black border
    gr.drawRect(0,0,cardWidth-1,cardHeight-1);

    // draw the "three of Spade"
    Font font=new Font("Dialog",Font.PLAIN, 28);
    gr.setFont(font);
    String spade="\u2660";   // unicode for the "spade" character
    gr.drawString(spade+"3",5,45);

  }

  public static void main(String[] args) throws Exception
  {
    // create frame
    javax.swing.JFrame frame=new javax.swing.JFrame();
    frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);    

    frame.setTitle("Macteki CardPanel");

    // create graphics panel and add it to the frame
    CardPanel panel=CardPanel.getInstance();    

    frame.add(panel);  // panel size is 200x200, see getInstance()
    frame.pack();  // this will correctly set the size of frame 
                   // so that it is big enough to hold the panel

    frame.setVisible(true); 
  }
}



The following is a screen capture of the above program.


It is not a pretty playing card but it serves the purpose of demonstration. You may draw more sophisticated pattern in paintComponent() if you wish.


The above sample put the card drawing code in paintComponent(). This is not an ideal method for 3 reasons :
  1. The drawing code is repeated whenever the panel is repainted, this is not a efficient method if many cards are needed to be displayed.
  2. It is very difficult to change the position of a card.
  3. It is difficult to duplicate the image of a card.

Hence, it is better to move the card drawing code to a more 'abstract' space.

This can be done by defining a image object that hold the card image.

//
  private BufferedImage cardImage=createImage();

  public BufferedImage createImage()
  {
    // create a card image.
    int cardWidth=60, cardHeight=80;
    BufferedImage image=new BufferedImage(cardWidth, cardHeight, BufferedImage.TYPE_INT_ARGB);

    // get a graphics object of the image for drawing.
    java.awt.Graphics2D gr = (java.awt.Graphics2D) image.getGraphics();

    // draw a white playing card
    gr.setColor(java.awt.Color.WHITE);
    gr.fillRect(0,0,cardWidth,cardHeight);
    // with black border
    gr.setColor(java.awt.Color.BLACK);  
    gr.drawRect(0,0,cardWidth-1,cardHeight-1);

    // draw the "three of Spade"
    Font font=new Font("Dialog",Font.PLAIN, 28);
    gr.setFont(font);
    String spade="\u2660";   // unicode for the "spade" character
    gr.drawString(spade+"3",5,45);

    return image;    
  }

Then change the paintComponent() method to paint the image object instead of repeating the card drawing code. To make it clear that it is a more flexible method, two cards are drawn in different positions.

// override the paint method
  public void paintComponent(java.awt.Graphics graphics)
  {
    super.paintComponent(graphics);  // paint background 

    // get panel dimemsion
    int w=getWidth();  // should be 200
    int h=getHeight(); // should be 200

    // create a Graphics2D object for drawing shape
    java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

    gr.drawImage(cardImage,0,0,this);  // draw a card
    gr.drawImage(cardImage,100,100,this);  // draw another card
  }

The complete sample with output follows :

/******************************************************************************
* File : CardPanel.java (version 2)
* Author : http://java.macteki.com/
* Description :
*   Display a shuffled deck of playing card.
* Tested with : JDK 1.6 under Windows XP and MAC osx 10.6.6
******************************************************************************/

import java.awt.image.BufferedImage;
import java.awt.Font;

class CardPanel extends javax.swing.JPanel
{
  private BufferedImage cardImage=createImage();

  public static CardPanel getInstance()
  {
    CardPanel panel=new CardPanel();
    panel.setBackground(new java.awt.Color(255,255,128));
    panel.setPreferredSize(new java.awt.Dimension(200,200));
    return panel;
  }

  public BufferedImage createImage()
  {
    // create a card image.
    int cardWidth=60, cardHeight=80;
    BufferedImage image=new BufferedImage(cardWidth, cardHeight, BufferedImage.TYPE_INT_ARGB);

    // get a graphics object of the image for drawing.
    java.awt.Graphics2D gr = (java.awt.Graphics2D) image.getGraphics();

    // draw a white playing card
    gr.setColor(java.awt.Color.WHITE);
    gr.fillRect(0,0,cardWidth,cardHeight);
    // with black border
    gr.setColor(java.awt.Color.BLACK);  
    gr.drawRect(0,0,cardWidth-1,cardHeight-1);

    // draw the "three of Spade"
    Font font=new Font("Dialog",Font.PLAIN, 28);
    gr.setFont(font);
    String spade="\u2660";   // unicode for the "spade" character
    gr.drawString(spade+"3",5,45);

    return image;    
  }

  // override the paint method
  public void paintComponent(java.awt.Graphics graphics)
  {
    super.paintComponent(graphics);  // paint background 

    // get panel dimemsion
    int w=getWidth();  // should be 200
    int h=getHeight(); // should be 200

    // create a Graphics2D object for drawing shape
    java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

    gr.drawImage(cardImage,0,0,this);  // draw a card
    gr.drawImage(cardImage,100,100,this);  // draw another card
  }

  public static void main(String[] args) throws Exception
  {
    // create frame
    javax.swing.JFrame frame=new javax.swing.JFrame();
    frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);    

    frame.setTitle("Macteki CardPanel");

    // create graphics panel and add it to the frame
    CardPanel panel=CardPanel.getInstance();    

    frame.add(panel);  // panel size is 200x200, see getInstance()
    frame.pack();  // this will correctly set the size of frame 
                   // so that it is big enough to hold the panel

    frame.setVisible(true); 
  }
}


It is time to draw all the 52 cards.

To do this, first we change the data type of cardImage and make it become an array of 52 BufferedImage.

Old :
private BufferedImage cardImage=createImage();
New :
private BufferedImage[] cardImage=createAllImage();

In the card shuffling article, I represented a deck of playing card by String array.
It is suggested to have a look at that article first.

String[] deck={
"SA","S2","S3","S4","S5","S6","S7","S8","S9","ST","SJ","SQ","SK", // spade
"HA","H2","H3","H4","H5","H6","H7","H8","H9","HT","HJ","HQ","HK", // heart
"CA","C2","C3","C4","C5","C6","C7","C8","C9","CT","CJ","CQ","CK", // club
"DA","D2","D3","D4","D5","D6","D7","D8","D9","DT","DJ","DQ","DK"  // diamond
};


Full sample with output follows :
/******************************************************************************
* File : CardPanel.java (version 3)
* Author : http://java.macteki.com/
* Description :
*   Display a shuffled deck of playing card.
* Tested with : JDK 1.6 under Windows XP and MAC osx 10.6.6
******************************************************************************/

import java.awt.image.BufferedImage;
import java.awt.Font;

class CardPanel extends javax.swing.JPanel
{
  String[] deck={
    "SA","S2","S3","S4","S5","S6","S7","S8","S9","ST","SJ","SQ","SK", // spade
    "HA","H2","H3","H4","H5","H6","H7","H8","H9","HT","HJ","HQ","HK", // heart
    "CA","C2","C3","C4","C5","C6","C7","C8","C9","CT","CJ","CQ","CK", // club
    "DA","D2","D3","D4","D5","D6","D7","D8","D9","DT","DJ","DQ","DK"  // diamond
  };

  private BufferedImage[] cardImage=createAllImage();

  public static CardPanel getInstance()
  {
    CardPanel panel=new CardPanel();
    panel.setBackground(new java.awt.Color(255,255,128));
    panel.setPreferredSize(new java.awt.Dimension(800,400));
    return panel;
  }

  public BufferedImage[] createAllImage()
  {
    BufferedImage[] imageArray = new BufferedImage[52];
    for (int i=0;i<52;i++)
    {
      imageArray[i]=createImage(deck[i]);
    }
    return imageArray;
  }

  // sample : createImage("S3");    
  public BufferedImage createImage(String card)
  {
    // create a card image.
    int cardWidth=60, cardHeight=80;
    BufferedImage image=new BufferedImage(cardWidth, cardHeight, BufferedImage.TYPE_INT_ARGB);

    // get a graphics object of the image for drawing.
    java.awt.Graphics2D gr = (java.awt.Graphics2D) image.getGraphics();

    // draw a white playing card
    gr.setColor(java.awt.Color.WHITE);
    gr.fillRect(0,0,cardWidth,cardHeight);
    // with black border
    gr.setColor(java.awt.Color.BLACK);  
    gr.drawRect(0,0,cardWidth-1,cardHeight-1);

    // draw the "three of Spade"
    Font font=new Font("Dialog",Font.PLAIN, 28);
    gr.setFont(font);

    String prefix=card.substring(0,1);  // first character
    String postfix=card.substring(1,2); // second character
    String suit="";
    java.awt.Color color=java.awt.Color.BLACK;
    if (prefix.equals("S")) 
    {
      suit="\u2660";   // unicode for the "spade" character
    }
    else if (prefix.equals("H"))
    {
      suit="\u2665";   // unicode for the "heart" character
      color=java.awt.Color.RED;
    }
    else if (prefix.equals("C"))
    {
      suit="\u2663";
    }
    else if (prefix.equals("D"))
    {
      suit="\u2666";   // unicode for the "diamond" character
      color=java.awt.Color.RED;
    }
    
    String point=postfix;
    int x=5;
    if (postfix.equals("T")) 
    {
      x=1;
      point="10";  // special handling for "ten"
    }
 
    gr.setColor(color);
    gr.drawString(suit+point,x,45);

    return image;    
  }

  // override the paint method
  public void paintComponent(java.awt.Graphics graphics)
  {
    super.paintComponent(graphics);  // paint background 

    // get panel dimemsion
    int w=getWidth();  
    int h=getHeight(); 

    // create a Graphics2D object for drawing shape
    java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

    int y=0;
    int x=0;
    for (int i=0;i<52;i++)
    {      
      gr.drawImage(cardImage[i],x,y,this);  // draw a card
      x+=61;
      if ((i+1)%13==0) {y+=81; x=0;}
    }
    
  }

  public static void main(String[] args) throws Exception
  {
    // create frame
    javax.swing.JFrame frame=new javax.swing.JFrame();
    frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);    

    frame.setTitle("Macteki CardPanel");

    // create graphics panel and add it to the frame
    CardPanel panel=CardPanel.getInstance();    

    frame.add(panel);  // panel size is 800x400, see getInstance()
    frame.pack();  // this will correctly set the size of frame 
                   // so that it is big enough to hold the panel

    frame.setVisible(true); 
  }
}



Thanks for reading. Comments are welcome.

1 comment: