Java代码 - 纸牌

时间:2016-02-06 00:12:51

标签: java swing drawing

我用Java创建了一个单人纸牌游戏。

我的问题是:如何在每张黑色或红色卡片上填充物体(心形,黑桃,钻石,球杆)?

以下是我现在的代码:

// draw the card
  public void draw (Graphics g, int x, int y) {
    // clear rectangle, draw border
    g.clearRect(x, y, width, height);
    g.setColor(Color.black);
    g.drawRect(x, y, width, height);

    // draw body of card
    if (faceUp()) 
      {
    if (color() == red)
      g.setColor(Color.red);
        else
      g.setColor(Color.black);

    g.drawString(names[rank()], x+3, y+15);

    if (suit() == heart) 
      {
        g.drawLine(x+25, y+30, x+35, y+20);
        g.drawLine(x+35, y+20, x+45, y+30);
        g.drawLine(x+45, y+30, x+25, y+60);
        g.drawLine(x+25, y+60, x+5, y+30);
        g.drawLine(x+5, y+30, x+15, y+20);
        g.drawLine(x+15, y+20, x+25, y+30);
        //    g.fill(Color.red);
      }
    else if (suit() == spade) 
      {
        g.drawLine(x+25, y+20, x+40, y+50);
        g.drawLine(x+40, y+50, x+10, y+50);
        g.drawLine(x+10, y+50, x+25, y+20);
        g.drawLine(x+23, y+45, x+20, y+60);
        g.drawLine(x+20, y+60, x+30, y+60);
        g.drawLine(x+30, y+60, x+27, y+45); 
      }
    else if (suit() == diamond) 
      {
        g.drawLine(x+25, y+20, x+40, y+40);
        g.drawLine(x+40, y+40, x+25, y+60);
        g.drawLine(x+25, y+60, x+10, y+40);
        g.drawLine(x+10, y+40, x+25, y+20);
      }
    else if (suit() == club) 
      {
        g.drawOval(x+20, y+25, 10, 10);
        g.drawOval(x+25, y+35, 10, 10);
        g.drawOval(x+15, y+35, 10, 10);
        g.drawLine(x+23, y+45, x+20, y+55);
        g.drawLine(x+20, y+55, x+30, y+55);
        g.drawLine(x+30, y+55, x+27, y+45); 
      }
      }
    else // face down 
      {
    g.setColor(Color.black);
    g.drawLine(x+15, y+5, x+15, y+65);
    g.drawLine(x+35, y+5, x+35, y+65);
    g.drawLine(x+5, y+20, x+45, y+20);
    g.drawLine(x+5, y+35, x+45, y+35);
    g.drawLine(x+5, y+50, x+45, y+50);
      }
  }
}

2 个答案:

答案 0 :(得分:3)

我拿了你的片段,并制作了我自己的片段。该片段仅使用Graphics.fillPolygon填充心脏套装。我已经注释掉了片段中的旧线条,因此您可以将其与您所做的相比较。其他卡片我会留给你。

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

public class CardFrame {
    enum CardColor{red,black};
    enum CardSuit{heart,diamond,spade,club}
    public static void main( String[] args )
    {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JPanel cardDisplay = new JPanel() {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(50,100);
                    }
                    @Override
                    protected void paintComponent(Graphics g) {
                        super.paintComponent(g);
                        draw(g,0,0);
                    }

                    private int width = 50;
                    private int height = 80;

                    private boolean faceUp() {
                        return true;
                    }
                    private CardColor color() {
                        return CardColor.red;
                    }
                    private CardSuit suit() {
                        return CardSuit.heart;
                    }
                    private int rank() {
                        return 0;
                    }
                    private String[] names = {"1","2","3","4","5","6","7","8","9","10","J","Q","K","A"};
                    private void draw(Graphics g, int x, int y) {
                        // clear rectangle, draw border
                        g.clearRect(x, y, width, height);
                        g.setColor(Color.black);
                        g.drawRect(x, y, width, height);

                        // draw body of card
                        if (faceUp()) {
                            if (color() == CardColor.red)
                                g.setColor(Color.red);
                            else
                                g.setColor(Color.black);

                            g.drawString(names[rank()], x + 3, y + 15);

                            if (suit() == CardSuit.heart) {
//                              g.drawLine(x + 25, y + 30, x + 35, y + 20);
//                              g.drawLine(x + 35, y + 20, x + 45, y + 30);
//                              g.drawLine(x + 45, y + 30, x + 25, y + 60);
//                              g.drawLine(x + 25, y + 60, x + 5, y + 30);
//                              g.drawLine(x + 5, y + 30, x + 15, y + 20);
//                              g.drawLine(x + 15, y + 20, x + 25, y + 30);
                                int[] xPoints = new int[]{x + 5,x + 15,x + 25,x + 35,x + 45,x + 25};
                                int[] yPoints = new int[]{y + 30,y + 20,y + 30,y + 20,y + 30,y + 60};
                                g.fillPolygon(xPoints, yPoints, 6);
                            } else if (suit() == CardSuit.spade) {
                                // ...
                            } else if (suit() == CardSuit.diamond) {
                                // ...
                            } else if (suit() == CardSuit.club) {
                                //
                            }
                        } else // face down
                        {
                            // ...
                        }
                    }
                };

                JFrame frm = new JFrame();
                frm.setContentPane(cardDisplay);
                frm.pack();
                frm.setVisible(true);
            }
        });
    }
}

结果:

enter image description here

答案 1 :(得分:0)

我从your recent question中删除了一些答案中的一些建议:

  • 首先,没有"静态构造函数",静态初始化程序块,是的,但这些不是以类名作为构造函数开始的,所以不要&#39 ;看起来像构造函数,更重要的是,他们不会 表现 作为构造函数 - 他们不会创建类的实例。相反,它们用于在类加载时调用的特定于类的(非特定于实例的)代码。
  • 我自己,我在GUI卡创建时创建了我的卡特定实例,因此我认为无论您在哪里创建卡的GUI表示,此代码都应该放在构造函数中。请注意,这可能(并且应该)与您创建的逻辑卡分开,但我不会在下面的示例中执行此操作。
  • 由于卡片支持图像是特定于班级的,而不是卡片实例特定的,因此我可以在静态初始化程序块中创建我的卡片支持图像,或者作为singleton instance。这种方式应该只创建一次。在下面的例子中,我使用单例模式(有人说"反" -pattern)。在Swing GUI中使用它应该是安全的,因为Swing是单线程的,所以它只能在一个线程中调用。
  • 根据我在原始问题中的评论,您的代码当前所做的最慢的事情是重复读取图像文件,并且您希望通过将图像存储在变量中然后根据需要使用它们来避免这样做,如以下示例显示。
  • 侧面推荐(与原始问题无关):考虑使用RenderingHints来平滑您可能正在进行的任何文本或图形绘制。例如,请参阅下面的代码。
  • 还可以使用您的卡片等级和套装的枚举。这是在讨论枚举主题时使用的规范示例,正如您在下面看到的那样,它们非常适合使用枚举。
  • 你的代码使用了很多"魔法"数字,换句话说,许多无法解释的数字文字,例如g.drawString(club, x + 12, y + 45);。所以有人只是想看看你的代码会想知道,12和45代表什么?最好通过使用带有逻辑名称的变量或常量来避免这种情况,以使代码更具自我评论性。例如:g2.drawString(s.getSymbol(), SYMBOL_X, SYMBOL_Y);,您可以猜到SYMBOL_X是符号的x位置。

例如,请查看此代码,该代码可以复制到单个文件中并运行:

import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.*;

/**
 * 
 * @author Hovercraftfullofeels
 * link: https://stackoverflow.com/a/35351199/522444
 *
 */    
public class PlayingCardExample {

    private static void createAndShowGui() {
        PlayingCardPanel mainPanel = new PlayingCardPanel();

        JFrame frame = new JFrame("Playing Card Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            createAndShowGui();
        });
    }
}

@SuppressWarnings("serial")
class PlayingCardPanel extends JPanel {
    private static final Color BG = Color.GREEN.darker().darker();
    private static final int GAP = 15;
    private MyDeck myDeck = new MyDeck();

    public PlayingCardPanel() {
        setBackground(BG);
        int rows = Suit.values().length;
        int cols = Rank.values().length;
        setLayout(new GridLayout(rows, cols, GAP, GAP));
        setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));

        while (myDeck.size() > 0) {
            Card myCard = myDeck.deal();
            final CardLabel cardLabel = new CardLabel(myCard);
            add(cardLabel.getLabel());
            cardLabel.addMouseListener(new CardListener(cardLabel));
        }
    }
}

// class to allow us to flip cards on mouse press
class CardListener extends MouseAdapter {
    private CardLabel cardLabel;

    public CardListener(CardLabel cardLabel) {
        this.cardLabel = cardLabel;
    }

    @Override
    public void mousePressed(MouseEvent e) {
        boolean faceDown = ! cardLabel.isFaceDown();
        cardLabel.setFaceDown(faceDown);
    }
}

class CardLabel {
    private JLabel label = new JLabel();
    private Card myCard;
    private boolean faceDown = true;

    public CardLabel(Card myCard) {
        this.myCard = myCard;
        setFaceDown(true);
    }

    public void addMouseListener(MouseListener listener) {
        label.addMouseListener(listener);
    }

    public boolean isFaceDown() {
        return faceDown;
    }

    public void setFaceDown(boolean faceDown) {
        this.faceDown = faceDown;
        // get my singleton icon:
        Icon cardBackIcon = CardBack.getInstance().getIcon();
        Icon icon = faceDown ? cardBackIcon : myCard.getIcon();
        label.setIcon(icon);
    }

    public JLabel getLabel() {
        return label;
    }

    public Card getMyCard() {
        return myCard;
    }

}

// singleton class to create the backing image shared by all cards
class CardBack {
    private static final Color BG = Color.WHITE;
    private static final Color COLOR = Color.BLUE;
    private static final int W = 10;
    private static final float STROKE_WIDTH = 3f;
    private static CardBack instance = null;
    private BufferedImage image;
    private Icon icon;

    // singleton constructor is private and so is only called by this class itself
    private CardBack() {
        BufferedImage repeatImg = new BufferedImage(W, W, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = repeatImg.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setBackground(BG);
        g2.clearRect(0, 0, W, W);
        g2.setStroke(new BasicStroke(STROKE_WIDTH));
        g2.setColor(COLOR);
        g2.drawLine(0, 0, W, W);
        g2.drawLine(0, W, W, 0);
        g2.dispose();

        int width = Card.WIDTH;
        int height = Card.HEIGHT;
        int imageType = BufferedImage.TYPE_INT_ARGB;
        image = new BufferedImage(width, height, imageType);
        g2 = image.createGraphics();
        Rectangle2D anchor = new Rectangle2D.Double(0, 0, W, W);
        TexturePaint texturePaint = new TexturePaint(repeatImg, anchor);
        g2.setPaint(texturePaint);
        g2.fillRect(0, 0, width, height);
        g2.dispose();
        icon = new ImageIcon(image);
    }

    public BufferedImage getImage() {
        return image;
    }

    public Icon getIcon() {
        return icon;
    }

    public static CardBack getInstance() {
        // create the instance in a lazy fashion -- only create it if it has not
        // yet been created. Thus, it should only be created *once*
        if (instance == null) {
            instance = new CardBack();
        }
        return instance;
    }
}

class MyDeck {
    List<Card> cards = new ArrayList<>();

    public MyDeck() {
        initialize();
        shuffle();
    }

    public final void initialize() {
        cards.clear();
        for (Rank rank : Rank.values()) {
            for (Suit suit : Suit.values()) {
                cards.add(new Card(rank, suit));
            }
        }
    }

    public int size() {
        return cards.size();
    }

    public Card deal() {
        if (cards.size() > 0) {
            return cards.remove(0);
        } else {
            // TODO: better to use exceptions here!
            // String text = "cards size is " + cards.size();
            // throw new MyDeckException(text);
            return null;
        }
    }

    public void shuffle() {
        Collections.shuffle(cards);
    }
}

// Quick an dirty code below. If this were a "real" program,
// I'd probably create a class without image or icon
// and then use a wrapper or decorator class to add the image information
// since this would be GUI library specific
class Card {
    public static final int WIDTH = 50;
    public static final int HEIGHT = 70;
    private static final Font TEXT_FONT = new Font(Font.DIALOG, Font.BOLD, 14);
    private static final Font SYMBOL_FONT = TEXT_FONT.deriveFont(Font.PLAIN, 28f);
    private static final int NAME_X = 3;
    private static final int NAME_Y = 15;
    private static final int SYMBOL_X = 12;
    private static final int SYMBOL_Y = 45;
    private Rank rank;
    private Suit suit;
    private BufferedImage image;
    private Icon icon;

    public Card(Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;

        // create each card's image and icon once on Card creation
        image = createImage(rank, suit);
        icon = new ImageIcon(image);
    }

    private static BufferedImage createImage(Rank r, Suit s) {
        BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        g2.setColor(java.awt.Color.WHITE);
        g2.fillRect(0, 0, WIDTH, HEIGHT);
        g2.setColor(java.awt.Color.BLACK);
        g2.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);

        g2.setColor(s.getColor());
        g2.setFont(TEXT_FONT);
        g2.drawString(r.getName(), NAME_X, NAME_Y);

        g2.setFont(SYMBOL_FONT);
        g2.drawString(s.getSymbol(), SYMBOL_X, SYMBOL_Y);

        g2.dispose();
        return img;
    }

    public Rank getRank() {
        return rank;
    }

    public Suit getSuit() {
        return suit;
    }

    public BufferedImage getImage() {
        return image;
    }

    public Icon getIcon() {
        return icon;
    }
}

enum Rank {
    ACE("A"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"), SEVEN("7"), EIGHT("8"), 
    NINE("9"), TEN("10"), JACK("J"), QUEEN("Q"), KING("K");
    private String name;

    private Rank(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

// Suit enum will hold its own color and symbol 
enum Suit {
    CLUB("Club", "\u2663", Color.BLACK), 
    DIAMOND("Diamond", "\u2666", Color.RED), 
    HEART("Heart", "\u2665", Color.RED), 
    SPADE("Spade", "\u2660", Color.BLACK);

    private String name;
    private String symbol;
    private Color color;

    private Suit(String name, String symbol, Color color) {
        this.name = name;
        this.symbol = symbol;
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public String getSymbol() {
        return symbol;
    }

    public Color getColor() {
        return color;
    }

}