Unable to remove component from jpanel

Zahidul Islam :

I have refactored my code to produce a Minimal, Reproducible Example.

Issue - I am not able to remove the card from the panel (I have commented in the code where the issue occurs, see class "ClearCardEventListener").

Full code below.

Before running the program, you will need a single image that needs to be download and added to the resources folder = https://ibb.co/MNccGS0

  1. Run the program
  2. Click on the "Add card" button (this will add the 2 of hearts image icon on the panel)
  3. Click on the "Clear card" button (this is the issue, where I'm unable to remove the card from the panel)

    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++];
        }
    
    }
    
MadProgrammer :

Problem #1

Every time you call Card#getCardLabel it is creating a new JLabel instance...

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

This means that when you do...

app.getPanel().add(app.getPlayerCards().get(0).getCardLabel()).setBounds(0, 0, 72, 96);

and...

System.out.println("DEBUG: " + app.getPlayerCards().get(0).getCardLabel());

and...

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());

each call is actually create a new instance of a JLabel, so the one on the screen has nothing to do with the one you are trying to modify.

So, instead, your Card#getCardLabel should create ONE JLabel ONCE only, this is also known as "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;
    }
}

Problem #2

Swing is lazy (ok, it's "optimised"), which means that adding or removing components, in of itself, does not trigger a layout or paint pass. This is done so you can make massive changes to the UI and not have it trying to update each change, which would be very slow.

This means that when you add or remove a component, you should also call repaint to trigger a new paint pass (I'd also recommend revalidate, but you're not using any layout managers, so it's not going to do anything for you)

app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel());
app.getPanel().repaint();

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=334746&siteId=1