无法从 jpanel 中删除组件
Unable to remove component from jpanel
我重构了我的代码以生成一个最小的、可重现的示例。
问题 - 我无法从面板上移除卡片(我在出现问题的代码中进行了评论,请参阅 class "ClearCardEventListener")。
完整代码如下。
在运行程序之前,您将需要一个需要下载并添加到资源文件夹的图像= https://ibb.co/MNccGS0
- 运行 程序
- 单击 "Add card" 按钮(这将在面板上添加 2 of hearts 图像图标)
单击 "Clear card" 按钮(这是问题所在,我无法从面板中取出卡)
package debug.debug;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class App extends JFrame {
public static void main(String[] args) {
App app = new App();
Insets insets = app.getInsets();
app.setSize(300 + insets.left + insets.right,
300 + insets.top + insets.bottom);
}
JPanel panel;
JButton clearCardButton;
JButton addCardButton;
List<Card> playerCards = new ArrayList<Card> ();
Deck deck = new Deck();
public App() {
setTitle("BlackJack Game");
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
panel = new JPanel();
add(panel);
panel.setLayout(null);
addCardButton = new JButton("Add card");
clearCardButton = new JButton("Clear card");
panel.add(addCardButton);
panel.add(clearCardButton);
addCardButton.addActionListener(new AddCardEventListener(this, this.deck));
clearCardButton.addActionListener(new ClearCardEventListener(this, this.deck));
addCardButton.setBounds(150, 50, addCardButton.getPreferredSize().width, addCardButton.getPreferredSize().height);
clearCardButton.setBounds(150, 100, clearCardButton.getPreferredSize().width, clearCardButton.getPreferredSize().height);
}
public JPanel getPanel() {
return panel;
}
public void addPlayerCard(Card card) {
playerCards.add(card);
}
public List<Card> getPlayerCards() {
return playerCards;
}
public List<JLabel> getPlayerCardLabels() {
List<JLabel> playerCardLabels = new ArrayList<JLabel> ();
for (int i = 0; i<this.getPlayerCards().size(); i++) {
playerCardLabels.add(this.getPlayerCards().get(i).getCardLabel());
}
return playerCardLabels;
}
}
class AddCardEventListener implements ActionListener {
private App app;
private Deck deck;
AddCardEventListener(App app, Deck deck) {
this.app = app;
this.deck = deck;
}
public void actionPerformed(ActionEvent arg0) {
// Player gets a card
app.addPlayerCard(deck.getCard());
// Display that player's card
app.getPanel()
.add(app.getPlayerCards().get(0).getCardLabel()).setBounds(0, 0, 72, 96);
}
}
class ClearCardEventListener implements ActionListener {
private App app;
private Deck deck;
ClearCardEventListener(App app, Deck deck) {
this.app = app;
this.deck = deck;
}
public void actionPerformed(ActionEvent arg0) {
System.out.println("DEBUG: " + app.getPlayerCards().get(0).getCardLabel());
/***
*
* NOT ABLE TO REMOVE CARD, WHY ???
*
*/
app.getPlayerCards().get(0).getCardLabel().setIcon(null);
app.getPlayerCards().get(0).getCardLabel().revalidate();
app.getPlayerCards().get(0).getCardLabel().repaint();
app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel());
}
}
class Card {
private String suit;
private String rank;
private int value;
public void setSuit(String suit) {
this.suit = suit;
}
public void setRank(String rank) {
this.rank = rank;
}
public void setValue(int value) {
this.value = value;
}
public String getSuit() {
return suit;
}
public String getRank() {
return rank;
}
public int getValue() {
return value;
}
// Hardcoded for debugging, so every card image is a 2 of hearts
public JLabel getCardLabel() {
return new JLabel(new ImageIcon(getClass()
.getResource("/cards/2h.png")));
}
}
class Deck {
private final String RANKS[] = {
"Ace",
"Deuce",
"Three",
"Four",
"Five",
"Six",
"Seven",
"Eight",
"Nine",
"Ten",
"Jack",
"Queen",
"King"
};
private final String SUITS[] = {
"Spades",
"Hearts",
"Diamonds",
"Clubs"
};
private Card[] deck;
static private int cardPosition = 0;
public Deck() {
deck(); // Create the deck
shuffle(); // shuffle the deck
}
public Card[] deck() {
deck = new Card[52];
for (int x = 0; x<deck.length; x++) {
String rankTemp = RANKS[x % 13];
String suitTemp = SUITS[x / 13];
deck[x] = new Card();
deck[x].setRank(rankTemp);
deck[x].setSuit(suitTemp);
deck[x].setValue((x % 13) + 1);
if (deck[x].getValue() > 10)
deck[x].setValue(10);
else if (deck[x].getValue() == 1)
deck[x].setValue(11);
}
return deck;
}
public void shuffle() {
Collections.shuffle(Arrays.asList(deck()));
}
public Card[] getDeck() {
return deck;
}
public Card getCard() {
return deck[cardPosition++];
}
}
问题 #1
每次你调用 Card#getCardLabel
它都会创建一个新的 JLabel
实例...
class Card {
// Hardcoded for debugging, so every card image is a 2 of hearts
public JLabel getCardLabel() {
return new JLabel(new ImageIcon(getClass()
.getResource("/cards/2h.png")));
}
}
这意味着当您...
app.getPanel().add(app.getPlayerCards().get(0).getCardLabel()).setBounds(0, 0, 72, 96);
和...
System.out.println("DEBUG: " + app.getPlayerCards().get(0).getCardLabel());
和...
app.getPlayerCards().get(0).getCardLabel().setIcon(null);
app.getPlayerCards().get(0).getCardLabel().revalidate();
app.getPlayerCards().get(0).getCardLabel().repaint();
app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel());
每次调用实际上都是创建一个 JLabel
的新实例,因此屏幕上显示的与您要修改的那个无关。
因此,您的 Card#getCardLabel
应该只创建一个 JLabel
一次,这也称为 "lazy loading"...
class Card {
private JLabel label;
//...
// Hardcoded for debugging, so every card image is a 2 of hearts
public JLabel getCardLabel() {
if (label == null) {
// Simple image for demonstration purposes
BufferedImage img = new BufferedImage(100, 150, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, 100, 150);
g2d.setColor(Color.RED);
g2d.drawLine(0, 0, 100, 150);
g2d.drawLine(100, 0, 0, 150);
g2d.dispose();
label = new JLabel(new ImageIcon(img));
}
return label;
}
}
问题 #2
Swing 是惰性的(好的,它是 "optimised"),这意味着添加或删除组件本身不会触发布局或绘制过程。这样做是为了让您可以对 UI 进行大量更改,而不是让它尝试更新每个更改,这会非常慢。
这意味着当你添加或删除一个组件时,你还应该调用 repaint
来触发一个新的绘制通道(我也推荐 revalidate
,但你没有使用任何布局管理器,所以它不会为你做任何事情)
app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel());
app.getPanel().repaint();
我重构了我的代码以生成一个最小的、可重现的示例。
问题 - 我无法从面板上移除卡片(我在出现问题的代码中进行了评论,请参阅 class "ClearCardEventListener")。
完整代码如下。
在运行程序之前,您将需要一个需要下载并添加到资源文件夹的图像= https://ibb.co/MNccGS0
- 运行 程序
- 单击 "Add card" 按钮(这将在面板上添加 2 of hearts 图像图标)
单击 "Clear card" 按钮(这是问题所在,我无法从面板中取出卡)
package debug.debug; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class App extends JFrame { public static void main(String[] args) { App app = new App(); Insets insets = app.getInsets(); app.setSize(300 + insets.left + insets.right, 300 + insets.top + insets.bottom); } JPanel panel; JButton clearCardButton; JButton addCardButton; List<Card> playerCards = new ArrayList<Card> (); Deck deck = new Deck(); public App() { setTitle("BlackJack Game"); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); panel = new JPanel(); add(panel); panel.setLayout(null); addCardButton = new JButton("Add card"); clearCardButton = new JButton("Clear card"); panel.add(addCardButton); panel.add(clearCardButton); addCardButton.addActionListener(new AddCardEventListener(this, this.deck)); clearCardButton.addActionListener(new ClearCardEventListener(this, this.deck)); addCardButton.setBounds(150, 50, addCardButton.getPreferredSize().width, addCardButton.getPreferredSize().height); clearCardButton.setBounds(150, 100, clearCardButton.getPreferredSize().width, clearCardButton.getPreferredSize().height); } public JPanel getPanel() { return panel; } public void addPlayerCard(Card card) { playerCards.add(card); } public List<Card> getPlayerCards() { return playerCards; } public List<JLabel> getPlayerCardLabels() { List<JLabel> playerCardLabels = new ArrayList<JLabel> (); for (int i = 0; i<this.getPlayerCards().size(); i++) { playerCardLabels.add(this.getPlayerCards().get(i).getCardLabel()); } return playerCardLabels; } } class AddCardEventListener implements ActionListener { private App app; private Deck deck; AddCardEventListener(App app, Deck deck) { this.app = app; this.deck = deck; } public void actionPerformed(ActionEvent arg0) { // Player gets a card app.addPlayerCard(deck.getCard()); // Display that player's card app.getPanel() .add(app.getPlayerCards().get(0).getCardLabel()).setBounds(0, 0, 72, 96); } } class ClearCardEventListener implements ActionListener { private App app; private Deck deck; ClearCardEventListener(App app, Deck deck) { this.app = app; this.deck = deck; } public void actionPerformed(ActionEvent arg0) { System.out.println("DEBUG: " + app.getPlayerCards().get(0).getCardLabel()); /*** * * NOT ABLE TO REMOVE CARD, WHY ??? * */ app.getPlayerCards().get(0).getCardLabel().setIcon(null); app.getPlayerCards().get(0).getCardLabel().revalidate(); app.getPlayerCards().get(0).getCardLabel().repaint(); app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel()); } } class Card { private String suit; private String rank; private int value; public void setSuit(String suit) { this.suit = suit; } public void setRank(String rank) { this.rank = rank; } public void setValue(int value) { this.value = value; } public String getSuit() { return suit; } public String getRank() { return rank; } public int getValue() { return value; } // Hardcoded for debugging, so every card image is a 2 of hearts public JLabel getCardLabel() { return new JLabel(new ImageIcon(getClass() .getResource("/cards/2h.png"))); } } class Deck { private final String RANKS[] = { "Ace", "Deuce", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" }; private final String SUITS[] = { "Spades", "Hearts", "Diamonds", "Clubs" }; private Card[] deck; static private int cardPosition = 0; public Deck() { deck(); // Create the deck shuffle(); // shuffle the deck } public Card[] deck() { deck = new Card[52]; for (int x = 0; x<deck.length; x++) { String rankTemp = RANKS[x % 13]; String suitTemp = SUITS[x / 13]; deck[x] = new Card(); deck[x].setRank(rankTemp); deck[x].setSuit(suitTemp); deck[x].setValue((x % 13) + 1); if (deck[x].getValue() > 10) deck[x].setValue(10); else if (deck[x].getValue() == 1) deck[x].setValue(11); } return deck; } public void shuffle() { Collections.shuffle(Arrays.asList(deck())); } public Card[] getDeck() { return deck; } public Card getCard() { return deck[cardPosition++]; } }
问题 #1
每次你调用 Card#getCardLabel
它都会创建一个新的 JLabel
实例...
class Card {
// Hardcoded for debugging, so every card image is a 2 of hearts
public JLabel getCardLabel() {
return new JLabel(new ImageIcon(getClass()
.getResource("/cards/2h.png")));
}
}
这意味着当您...
app.getPanel().add(app.getPlayerCards().get(0).getCardLabel()).setBounds(0, 0, 72, 96);
和...
System.out.println("DEBUG: " + app.getPlayerCards().get(0).getCardLabel());
和...
app.getPlayerCards().get(0).getCardLabel().setIcon(null);
app.getPlayerCards().get(0).getCardLabel().revalidate();
app.getPlayerCards().get(0).getCardLabel().repaint();
app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel());
每次调用实际上都是创建一个 JLabel
的新实例,因此屏幕上显示的与您要修改的那个无关。
因此,您的 Card#getCardLabel
应该只创建一个 JLabel
一次,这也称为 "lazy loading"...
class Card {
private JLabel label;
//...
// Hardcoded for debugging, so every card image is a 2 of hearts
public JLabel getCardLabel() {
if (label == null) {
// Simple image for demonstration purposes
BufferedImage img = new BufferedImage(100, 150, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, 100, 150);
g2d.setColor(Color.RED);
g2d.drawLine(0, 0, 100, 150);
g2d.drawLine(100, 0, 0, 150);
g2d.dispose();
label = new JLabel(new ImageIcon(img));
}
return label;
}
}
问题 #2
Swing 是惰性的(好的,它是 "optimised"),这意味着添加或删除组件本身不会触发布局或绘制过程。这样做是为了让您可以对 UI 进行大量更改,而不是让它尝试更新每个更改,这会非常慢。
这意味着当你添加或删除一个组件时,你还应该调用 repaint
来触发一个新的绘制通道(我也推荐 revalidate
,但你没有使用任何布局管理器,所以它不会为你做任何事情)
app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel());
app.getPanel().repaint();