Java 无法从jpanel中删除组件

Java 无法从jpanel中删除组件,java,swing,oop,jpanel,jcomponent,Java,Swing,Oop,Jpanel,Jcomponent,我已经重构了我的代码,以生成一个最小的、可复制的示例 问题-我无法从面板中移除卡(我已经在出现问题的代码中进行了注释,请参阅类“ClearCardEventListener”) 完整代码如下 在运行程序之前,需要下载一个映像并将其添加到resources文件夹= 运行程序 单击“添加卡”按钮(这将在面板上添加2个红心图像图标) 单击“清除卡”按钮(这就是问题所在,我无法从面板中移除卡) package debug.debug; 导入java.awt.Insets; 导入java.awt.even

我已经重构了我的代码,以生成一个最小的、可复制的示例

问题-我无法从面板中移除卡(我已经在出现问题的代码中进行了注释,请参阅类“ClearCardEventListener”)

完整代码如下

在运行程序之前,需要下载一个映像并将其添加到resources文件夹=

  • 运行程序
  • 单击“添加卡”按钮(这将在面板上添加2个红心图像图标)
  • 单击“清除卡”按钮(这就是问题所在,我无法从面板中移除卡)

    package debug.debug;
    导入java.awt.Insets;
    导入java.awt.event.ActionEvent;
    导入java.awt.event.ActionListener;
    导入java.util.ArrayList;
    导入java.util.array;
    导入java.util.Collections;
    导入java.util.List;
    导入javax.swing.ImageIcon;
    导入javax.swing.JButton;
    导入javax.swing.JFrame;
    导入javax.swing.JLabel;
    导入javax.swing.JPanel;
    公共类应用程序扩展了JFrame{
    公共静态void main(字符串[]args){
    App App=新App();
    Insets Insets=app.getInsets();
    应用设置尺寸(300+插图左+插图右,
    300+插图。顶部+插图。底部);
    }
    JPanel小组;
    JButton clearCardButton;
    JButton addCardButton;
    List playerCards=new ArrayList();
    甲板=新甲板();
    公共应用程序(){
    setTitle(“21点游戏”);
    setVisible(真);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(空);
    panel=新的JPanel();
    添加(面板);
    panel.setLayout(空);
    addCardButton=新JButton(“添加卡”);
    clearCardButton=新的JButton(“清除卡”);
    面板。添加(添加卡片按钮);
    面板。添加(clearCardButton);
    addCardButton.addActionListener(新的AddCardEventListener(this,this.deck));
    addActionListener(新的ClearCardEventListener(this,this.deck));
    addCardButton.setBounds(150,50,addCardButton.getPreferredSize().width,addCardButton.getPreferredSize().height);
    clearCardButton.setBounds(150100,clearCardButton.getPreferredSize().width,clearCardButton.getPreferredSize().height);
    }
    公共JPanel getPanel(){
    返回面板;
    }
    公共作废addPlayerCard(卡片){
    玩家卡。添加(卡片);
    }
    公共列表getPlayerCards(){
    返回游戏卡;
    }
    公共列表getPlayerCardLabels(){
    List playerCardLabels=new ArrayList();
    对于(int i=0;i问题#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());
    
    而且

    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
    的新实例,因此屏幕上的调用与您试图修改的调用无关

    因此,您的
    卡#getCardLabel
    应该只创建一次
    JLabel
    ,这也被称为“延迟加载”

    问题2 Swing是惰性的(好吧,它是“优化的”),这意味着添加或删除组件本身不会触发布局或绘制过程。这样做是为了让您可以对UI进行大量更改,而不是让它尝试更新每个更改,这将非常缓慢

    这意味着在添加或删除组件时,还应该调用
    repaint
    来触发新的绘制过程(我也建议
    revalidate
    ,但您没有使用任何布局管理器,因此它不会对您做任何事)


    app.getPanel()下面添加
    app.getPanel().repaint()
    ).remove
    call-我还强烈建议使用适当的布局管理器,这将对您有很大帮助。此外,
    getCardLabel
    在每次调用时都会创建一个新的
    JLabel
    ,这意味着屏幕上的标签不是您试图移除的标签。通常,Swing组件不应不能从视图中添加或删除。创建一个保存信息的逻辑模型。Swing视图读取逻辑模型。您的控制器类更新逻辑模型并要求视图重新绘制。谢谢,它现在可以工作了!面板上的重新绘制清楚了。
    app.getPanel().remove(app.getPlayerCards().get(0).getCardLabel());
    app.getPanel().repaint();