简体   繁体   English

如何将 ActionListener 添加到 JCheckBox?

[英]How do I add an ActionListener to a JCheckBox?

Problem问题

So my program takes in a.csv file and loads the data and displays it.所以我的程序接受一个.csv 文件并加载数据并显示它。 When data is loaded in, it creates a new JCheckBox for every column header there is in the data.加载数据时,它会为数据中的每一列 header 创建一个新的JCheckBox How do I add an ActionListener such that when the user ticks/unticks any of the boxes, it should do a certain function?如何添加一个ActionListener以便当用户勾选/取消勾选任何框时,它应该执行某个 function?

When data is loaded in, it updates the JPanel by the code:加载数据时,它会通过以下代码更新JPanel

    public void updateChecklistPanel(){

        checklistPanel.removeAll();
        checklistPanel.setLayout(new GridLayout(currentData.getColumnNames().length, 1, 10, 0));
        for (String columnName : currentData.getColumnNames()){
            JCheckBox checkBox = new JCheckBox();
            checkBox.setText(columnName);
            checklistPanel.add(checkBox);
        }
        checklistPanel.revalidate();
        checklistPanel.repaint();
    }

I also have the following at the bottom:我在底部还有以下内容:

    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == newDataFrameItem){
            newFile();
            System.out.println("New DataFrame Loaded in");
        }
        if (e.getSource() == loadDataFrameItem){
            loadFile();
            System.out.println(".csv Data loaded into DataFrame.");
        }
        if (e.getSource() == saveDataFrameItem){
            System.out.println("Saved the data to a .csv file");
        }

    }

What I'm trying to do is that when a checkbox is unticked, it should hide a column in the JTable and when ticked, it should redisplay the column.我想要做的是,当取消选中复选框时,它应该隐藏JTable中的一列,并且在勾选时,它应该重新显示该列。

Current Solution当前解决方案

The solution that I have come up with is to make a variable allColumnHeaders that is an ArrayList of Strings.我想出的解决方案是创建一个变量allColumnHeaders ,它是字符串的 ArrayList。 I then also have a variable shownColumnHeaders that is an ArrayList of Booleans.然后,我还有一个变量shownColumnHeaders ,它是布尔值的 ArrayList。 When the user wants to show/hide a column, the showColumn(String columnName) and hideColumn(String columnName) function finds the index of the column Name in allColumnHeaders and sets the Boolean value of the index in shownColumnHeaders to either true/false.当用户想要显示/隐藏列时, showColumn(String columnName)hideColumn(String columnName) function 在allColumnHeaders中查找列 Name 的索引,并将 shownColumnHeaders 中索引的shownColumnHeaders值设置为 true/false。

It the proceeds to create a new table model where the columns are only added if the Boolean value for that column is true.它将继续创建一个新表 model,其中仅当该列的 Boolean 值为 true 时才添加列。 It will then set the model for the table to the new table model.然后它将表的 model 设置为新表 model。

The code for the following is show below:以下代码如下所示:

import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class MRE extends JPanel {

    private static JTable table;
    private static ArrayList<String> allColumnHeaders = new ArrayList<>();
    private static ArrayList<Boolean> shownColumnHeaders = new ArrayList<>();

    private static void createAndShowGUI()
    {
        table = new JTable(5, 7);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane( table );

        JPanel buttons = new JPanel( new GridLayout(0, 1) );

        for (int i = 0; i < table.getColumnCount(); i++) {
            String column = table.getColumnName(i);
            allColumnHeaders.add(column);
            JCheckBox checkBox = new JCheckBox(column);

            checkBox.addActionListener(event -> {
                JCheckBox cb = (JCheckBox) event.getSource();
                if (cb.isSelected()) {
                    System.out.println(checkBox.getText() + " is now being displayed");
                    showColumn(checkBox.getText());
                } else {
                    System.out.println(checkBox.getText() + " is now being hidden");
                    hideColumn(checkBox.getText());
                }

                table.setModel(createTableModel());
            });

            checkBox.setSelected( true );
            buttons.add( checkBox );

            shownColumnHeaders.add(true);

        }

        JPanel wrapper = new JPanel();
        wrapper.add( buttons );

        JFrame frame = new JFrame("MRE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(scrollPane, BorderLayout.CENTER);
        frame.add(wrapper, BorderLayout.LINE_END);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static DefaultTableModel createTableModel(){

        DefaultTableModel tableModel = new DefaultTableModel(0, 0);
        String[] columnValues = new String[1];
        for (int i = 0; i < shownColumnHeaders.size(); i++){
            if (shownColumnHeaders.get(i)){
                tableModel.addColumn(allColumnHeaders.get(i), columnValues);
            }
        }

        return tableModel;
    }

    public static void showColumn(String columnName){
        for (int i = 0; i < allColumnHeaders.size(); i++) {
            if (allColumnHeaders.get(i).equals(columnName)){
                shownColumnHeaders.set(i, true);
            }
        }
    }

    public static void hideColumn(String columnName){
        for (int i = 0; i < allColumnHeaders.size(); i++) {
            if (allColumnHeaders.get(i).equals(columnName)){
                shownColumnHeaders.set(i, false);
            }
        }
    }

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

}

Introduction介绍

It took me a while, but I came up with the following JTable GUI.我花了一段时间,但我想出了以下JTable GUI。 Here's the starting display.这是开始的显示。

JCheckBox 表 GUI 1

Here's the GUI after I remove the item description.这是我删除项目描述后的 GUI。

JCheckBox 表 GUI 2

Here's the GUI after I remove the item price.这是我删除商品价格后的 GUI。

JCheckBox 表 GUI 3

Here's the GUI after I add the columns back.这是我添加列后的 GUI。

JCheckBox 表 GUI 4

Explanation解释

I created an Item class to hold one item.我创建了一个Item class 来容纳一个项目。

I created an Inventory class to hold a List of Item instances and a String array of the column headers.我创建了一个Inventory class 来保存一个Item实例列表和一个列标题的String数组。

I created the JFrame and two JPanels .我创建了JFrame和两个JPanels One JPanel holds the JTable and the other holds the JCheckBoxes .一个JPanel持有JTable ,另一个持有JCheckBoxes I used Swing layout managers to create the JPanels .我使用 Swing 布局管理器来创建JPanels

So far, so basic.到目前为止,很基本。 Creating the JTable took a bit of effort.创建JTable需要一些努力。 I wanted to display the item price as currency, but that wasn't important for this example GUI.我想将商品价格显示为货币,但这对于这个示例 GUI 来说并不重要。

I created an ActionListener to add and remove columns from the JTable .我创建了一个ActionListener来添加和删除JTable中的列。 I had to experiment a bit.我不得不做一些实验。 The TableColumnModel addColumn method appends the column to the table. TableColumnModel addColumn方法将列附加到表中。

I had to create a DisplayTableColumn class to hold a TableColumn and a boolean that tells me whether or not to display the TableColumn .我必须创建一个DisplayTableColumn class 来保存一个TableColumn和一个告诉我是否显示TableColumn的 boolean 。 I wound up removing all the columns from the JTable and adding all the columns back to the JTable so that I could maintain the column sequence.我最终从JTable中删除了所有列,并将所有列添加回JTable以便我可以保持列顺序。 I probably ran 100 tests before I could get this code to work.我可能运行了 100 次测试才能让这段代码工作。

Code代码

Here's the complete runnable code.这是完整的可运行代码。

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class JCheckBoxTableGUI implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new JCheckBoxTableGUI());
    }
    
    private final Inventory inventory;
    
    private final InventoryTableModel tableModel;
    
    private JFrame frame;
    
    private JTable table;
    
    public JCheckBoxTableGUI() {
        this.tableModel = new InventoryTableModel();
        this.inventory = new Inventory();
        
        String[] columns = inventory.getTableHeader();
        for (String column : columns) {
            tableModel.addColumn(column);
        }
        
        List<Item> items = inventory.getInventory();
        for (Item item : items) {
            Object[] object = new Object[5];
            object[0] = item.getItemNumber();
            object[1] = item.getItemName();
            object[2] = item.getItemDescription();
            object[3] = item.getItemQuantity();
            object[4] = item.getItemPrice();
            tableModel.addRow(object);
        }
    }

    @Override
    public void run() {
        frame = new JFrame("JCheckBox Table GUI");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.add(createTablePanel(), BorderLayout.CENTER);
        frame.add(createSelectionPanel(), BorderLayout.AFTER_LINE_ENDS);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
    
    private JPanel createTablePanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        table = new JTable(tableModel);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        table.getColumnModel().getColumn(0).setPreferredWidth(100);
        table.getColumnModel().getColumn(1).setPreferredWidth(150);
        table.getColumnModel().getColumn(2).setPreferredWidth(150);
        table.getColumnModel().getColumn(3).setPreferredWidth(100);
        table.getColumnModel().getColumn(4).setPreferredWidth(100);
        JScrollPane scrollPane = new JScrollPane(table);
        scrollPane.setPreferredSize(new Dimension(620, 300));
        panel.add(scrollPane, BorderLayout.CENTER);
        
        return panel;
    }
    
    private JPanel createSelectionPanel() {
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        JPanel innerPanel = new JPanel(new GridLayout(0, 1, 5, 5));
        
        ColumnListener listener = new ColumnListener(this);
        String[] columns = inventory.getTableHeader();
        for (String column : columns) {
            JCheckBox checkBox = new JCheckBox("Display " +  column);
            checkBox.addActionListener(listener);
            checkBox.setActionCommand(column);
            checkBox.setSelected(true);
            innerPanel.add(checkBox);
        }
        
        panel.add(innerPanel);
        
        return panel;
    }
    
    public JTable getTable() {
        return table;
    }
    
    public JFrame getFrame() {
        return frame;
    }
    
    public class ColumnListener implements ActionListener {
        
        private final JCheckBoxTableGUI frame;
        
        private final List<DisplayTableColumn> displayColumns;

        public ColumnListener(JCheckBoxTableGUI frame) {
            this.frame = frame;
            this.displayColumns = new ArrayList<>();
            
            TableColumnModel tcm = frame.getTable().getColumnModel();
            for (int index = 0; index < tcm.getColumnCount(); index++) {
                TableColumn tc = tcm.getColumn(index);
                displayColumns.add(new DisplayTableColumn(tc, true));
            }
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            JCheckBox checkBox =  (JCheckBox) event.getSource();
            String column = event.getActionCommand();
            TableColumnModel tcm = frame.getTable().getColumnModel();
            
            for (int index = 0; index < displayColumns.size(); index++) {
                DisplayTableColumn dtc = displayColumns.get(index);
                if (dtc.isShowTableColumn()) {
                    tcm.removeColumn(dtc.getTableColumn());
                }
            }
            
            int columnIndex = getColumnIndex(column);
            displayColumns.get(columnIndex).setShowTableColumn(
                    checkBox.isSelected());
            
            for (int index = 0; index < displayColumns.size(); index++) {
                DisplayTableColumn dtc = displayColumns.get(index);
                if (dtc.isShowTableColumn()) {
                    tcm.addColumn(dtc.getTableColumn());
                }
            }
            
            frame.getFrame().pack();
        }
        
        private int getColumnIndex(String column) {
            for (int index = 0; index < displayColumns.size(); index++) {
                DisplayTableColumn dtc = displayColumns.get(index);
                if (column.equals(dtc.getTableColumn().getHeaderValue())) {
                    return index;
                }
            }
            
            return -1;
        }
        
    }
    
    public class DisplayTableColumn {
        
        private boolean showTableColumn;
        
        private final TableColumn tableColumn;

        public DisplayTableColumn(TableColumn tableColumn, boolean showTableColumn) {
            this.tableColumn = tableColumn;
            this.showTableColumn = showTableColumn;
        }

        public boolean isShowTableColumn() {
            return showTableColumn;
        }

        public void setShowTableColumn(boolean showTableColumn) {
            this.showTableColumn = showTableColumn;
        }

        public TableColumn getTableColumn() {
            return tableColumn;
        }
        
    }
    
    public class InventoryTableModel extends DefaultTableModel {

        private static final long serialVersionUID = 1L;

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            if (columnIndex <= 2) {
                return String.class;
            } else if (columnIndex == 3) {
                return Integer.class;
            } else {
                return Integer.class;
            }
        }

    }
    
    public class Inventory {
        
        private final List<Item> inventory;
        
        private final String[] tableHeader;
        
        public Inventory() {
            this.tableHeader = new String[] { "Item Number", "Item Name", 
                    "Item Description", "Item Quantity",
                    "Item Price" };
            
            this.inventory = new ArrayList<>();
            
            inventory.add(new Item("X101111", "Samsung Camera", " ", 20, 69.99));
            inventory.add(new Item("X101112", "Samsung Monitor", " ", 10, 279.99));
            inventory.add(new Item("X101113", "Samsung Smartphone", " ", 110, 599.99));
            inventory.add(new Item("X101114", "Apple Watch", " ", 20, 1259.99));
            inventory.add(new Item("X101115", "Sony Playstation 5", " ", 0, 399.99));
        }

        public String[] getTableHeader() {
            return tableHeader;
        }

        public List<Item> getInventory() {
            return inventory;
        }
        
    }
    
    public class Item {
        
        private int itemPrice;
        private int itemQuantity;
        
        private final String itemNumber;
        private final String itemName;
        private final String itemDescription;
        
        public Item(String itemNumber, String itemName, 
                String itemDescription, int itemQuantity, double itemPrice) {
            this.itemNumber = itemNumber;
            this.itemName = itemName;
            this.itemDescription = itemDescription;
            this.itemQuantity = itemQuantity;
            setItemPrice(itemPrice);
        }

        public int getItemPrice() {
            return itemPrice;
        }

        public void setItemPrice(double itemPrice) {
            this.itemPrice = (int) Math.round(itemPrice * 100.0);
        }

        public int getItemQuantity() {
            return itemQuantity;
        }

        public void setItemQuantity(int itemQuantity) {
            this.itemQuantity = itemQuantity;
        }

        public String getItemNumber() {
            return itemNumber;
        }

        public String getItemName() {
            return itemName;
        }

        public String getItemDescription() {
            return itemDescription;
        }
        
    }

}

This only demonstrates how to create a basic MRE:这仅演示了如何创建一个基本的 MRE:

  1. the csv file is irrelevant. csv 文件无关紧要。
  2. data in the model is irrelevant. model 中的数据无关紧要。
  3. your loadFile, newFile and saveFile buttons are irrelevant.您的 loadFile、newFile 和 saveFile 按钮无关紧要。
  4. the TableModel is irrelevant TableModel 无关紧要

Your question is about adding an ActionListener to dynamically created checkboxes based on the columns of the table.您的问题是关于将 ActionListener 添加到基于表的列动态创建的复选框中。

So all you need is a table with some columns and the resulting checkboxes.因此,您所需要的只是一个包含一些列和生成的复选框的表格。

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

public class MRE extends JPanel
{
    private static void createAndShowGUI()
    {
        JTable table = new JTable(5, 7);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane( table );

        JPanel buttons = new JPanel( new GridLayout(0, 1) );

        for (int i = 0; i < table.getColumnCount(); i++)
        {
            String column = table.getColumnName(i);
            JCheckBox checkBox = new JCheckBox("Display " + column);
            checkBox.setSelected( true );
            buttons.add( checkBox );
        }

        JPanel wrapper = new JPanel();
        wrapper.add( buttons );

        JFrame frame = new JFrame("MRE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(scrollPane, BorderLayout.CENTER);
        frame.add(wrapper, BorderLayout.LINE_END);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

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

If you can modify this to demonstrate how to use your "reusable class" to manage column visibility, then I will update the above code to use my reusable class with 4 additional lines of code.如果您可以修改它以演示如何使用“可重用类”来管理列可见性,那么我将更新上面的代码以使用我的可重用 class 和另外 4 行代码。

Edit:编辑:

Suggested improvements to your code:对代码的建议改进:

  1. Make the code reusable and self contained使代码可重用和自包含
  2. Don't use static methods不要使用 static 方法
  3. Don't change the data.不要更改数据。

Your original question was:你原来的问题是:

How do I add an ActionListener to a JCheckBox?

So to do that you need a class that:所以要做到这一点,你需要一个 class :

  1. implements an ActionListener实现一个ActionListener
  2. is self contained to implement the functionality you require.是自包含的以实现您需要的功能。

So the basic structure could be like the following:所以基本结构可能如下所示:

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

public class SomeFunction implements ActionListener
{
    private JTable table;

    public SomeFunction(JTable table)
    {
        this.table = table;
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource() instanceof AbstractButton)
        {
            String command = e.getActionCommand();
            AbstractButton button = (AbstractButton)e.getSource();

            if (button.isSelected())
                doSelected( command );
            else
                doUnselected( command );
        }
    }

    private void doSelected(String command)
    {
        System.out.println(command + " selected");
    }

    private void doUnselected(String command)
    {
        System.out.println(command + " unselected");
    }

    private static void createAndShowGUI()
    {
        JTable table = new JTable(5, 7);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane( table );

        JPanel buttons = new JPanel( new GridLayout(0, 1) );
        SomeFunction listener = new SomeFunction( table ); // added
//      TableColumnManager listener = new TableColumnManager(table, false);
//      ColumnListener listener = new ColumnListener();

        for (int i = 0; i < table.getColumnCount(); i++)
        {
            String column = table.getColumnName(i);
            JCheckBox checkBox = new JCheckBox("Display " + column);
            checkBox.setSelected( true );
            checkBox.setActionCommand( column ); // added
            checkBox.addActionListener( listener ); // added
            buttons.add( checkBox );
        }

        JPanel wrapper = new JPanel();
        wrapper.add( buttons );

        JFrame frame = new JFrame("MRE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(scrollPane, BorderLayout.CENTER);
        frame.add(wrapper, BorderLayout.LINE_END);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

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

Whatever your function does it only needs to know about the table it should act upon.无论您的 function 做什么,它只需要知道它应该操作的表。

So try to restructure your code into a reusable class.因此,尝试将您的代码重组为可重用的 class。

If you want to use Gilberts code, then you need to restructure the ColumnListener class into a separate reusable self contained class.如果要使用 Gilberts 代码,则需要将ColumnListener class 重组为单独的可重用自包含 class。

Finally you can also try my reusable class.最后你也可以试试我的可重复使用的 class。 Check out Table Column Manager for the class to download.查看 class 的表格列管理器以进行下载。

Whatever reusable class you decide to use, you will only need to change a single line of code from the above example (once your functionality is contained in a reusable class).无论您决定使用什么可重用 class,您只需更改上述示例中的一行代码(一旦您的功能包含在可重用类中)。

Note the static methods would not be part of the final class.请注意 static 方法不会成为最终 class 的一部分。 They are only there so simplify testing and posting of an MRE in a single class file.它们只是为了简化在单个 class 文件中测试和发布 MRE。

Hope this helps with future design considerations.希望这有助于未来的设计考虑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM