Java代码-纸牌
我用Java创建了一个纸牌游戏 我的问题是:如何在每张卡片上用黑色或红色填充对象(红心、黑桃、钻石、梅花) 以下是我现在掌握的代码:Java代码-纸牌,java,swing,drawing,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(
// 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);
}
}
}
我拿了你的片段,自己做了一个片段。该代码片段仅使用
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);
}
});
}
}
结果:
我的一些建议从以下内容中删除:
- 首先,没有所谓的“静态构造函数”,静态初始值设定项块,是的,但它们并不像构造函数那样以类名开头,因此看起来不像构造函数,更重要的是,它们不像构造函数那样行为,它们不创建类的实例。相反,它们用于类加载时调用的特定于类(而不是特定于实例)的代码
- 我自己,我会在GUI卡创建上创建我的卡特定实例,因此我认为无论您在哪里创建卡的GUI表示,这些代码都应该放在构造函数中。请注意,这可能(并且应该)与您创建的逻辑卡分开,尽管我在下面的示例中没有这样做
- 由于卡备份映像是特定于类的,而不是特定于卡实例的,因此我将在静态初始值设定项块中创建卡备份映像,或者将其作为一个实例创建。这样,它应该一次创建一次。在下面的示例中,我使用单例模式(有人说是“反”模式)。在Swing GUI中使用它应该是安全的,因为Swing是单线程的,所以只能在一个线程中调用它
- 正如我在您的原始问题中的评论,您的代码目前做的最慢的事情是重复读取图像文件,您希望通过将图像存储在变量中,然后根据需要使用它们来避免这样做,如下例所示
- 一个侧面建议(与你原来的问题无关):考虑使用渲染提示来平滑你可能正在做的任何文本或图形的绘制。例如,请参见下面的代码
- 也可以为你的卡等级和套装使用枚举。这是讨论枚举主题时使用的规范示例,正如您将在下面看到的,它们非常适合使用枚举
- 你的代码使用了很多“神奇”数字,换句话说,很多无法解释的数字文字,比如
g.drawString(club,x+12,y+45)代码>。所以有人只要看一下你的代码就会想,12和45代表什么?最好通过使用带有逻辑名称的变量或常量来避免这种情况,从而使代码更具自注释性。例如,类似于:
g2.drawString(s.getSymbol(),SYMBOL_X,SYMBOL_Y)代码>您可以猜测符号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;
}
}
import java.awt.BasicStroke;
导入java.awt.Font;
导入java.awt.Graphics2D;
导入java.awt.GridLayout;
导入java.awt.RenderingHints;
导入java.awt.TexturePaint;
导入java.awt.event.*;
导入java.awt.geom.Rectangle2D;
导入java.awt.image.buffereImage;
导入java.awt.Color;
导入java.util.ArrayList;
导入java.util.Collections;
导入java.util.List;
导入javax.swing.*;
/**
*
*@author@fullofeels
*链接:https://stackoverflow.com/a/35351199/522444
*
*/
公共类播放卡示例{
私有静态void createAndShowGui(){
PlayingCardPanel主面板=新的PlayingCardPanel();
JFrame=新JFrame(“扑克牌示例”);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(主面板);
frame.pack();
frame.setLocationByPlatform(真);
frame.setVisible(true);
}
公共静态void main(字符串[]args){
SwingUtilities.invokeLater(()->{
createAndShowGui();
});
}
}
@抑制警告(“串行”)
类播放CardPanel扩展JPanel{
私有静态最终颜色BG=Color.GREEN.darker().darker();
专用静态最终内部间隙=15;
私有MyDeck MyDeck=新MyDeck();
公共播放CardPanel(){
退避地(BG);
int rows=Suit.values().length;
int cols=Rank.values().length;
setLayout(新网格布局(行、列、间隙、间隙));
setBorder(BorderFactory.createEmptyByOrder(间隙,间隙,间隙));
而(myDeck.size()>0){
Card myCard=myDeck.deal();
最终CardLabel CardLabel=新的CardLabel(myCard);
添加(cardLabel.getLabel());
addMouseListener(新的CardListener(cardLabel));
}
}
}
//类以允许我们在鼠标按下时翻转卡片
类CardListener扩展了MouseAdapter{
私人信用卡标签;
公共CardListener(CardLabel CardLabel){
this.cardLabel=cardLabel;
}
@凌驾
公共无效鼠标按下(MouseEvent e){
布尔faceDown=!cardLabel.isFaceDown();
cardLabel.setFaceDown(面朝下);
}
}
类别卡片标签{
专用JLabel标签=新JLabel();
私人卡我的卡;
private boolean faceDown=true;
公共卡标签(卡myCard){
this.myCard=myCard;
setFaceDown(真);
}
public void addMouseStener(MouseStener侦听器){
label.addMouseListener(侦听器);
}
公共布尔值isFaceDown(){
正面朝下返回;
}
公共void setFaceDown(布尔值faceDown){
this.faceDown=faceDown;
//获取我的单身图标:
Icon cardBackIcon=CardBack.getInstance().getIcon();
Icon-Icon=faceDown?cardBackIcon:myCard.getIcon();
label.setIcon(图标);
}
公共JLabel getLabel(){
退货标签;
}
公共卡getMyCard(){
返回m