简体   繁体   中英

Adding and removing checkboxes dynamically

I want to make ToDoList App. After successfully adding task to do (which contains checkbox, JLabel and date, all putted in a box) i want to remove them dynamically. With adding it's not problem but when i try to remove (ater clicking checked in checkbox) it works only once. Then it either removes not once which are intended or not removing them at all. I am not sure why it's not working so I paste all code below.

JSpinner dateSpin;
Box eventBox, boxBox;
Box[] taskBox = new Box[1000];
JTextField eventName;
Date date;
Checkbox[] doneCheck = new Checkbox[1000];
JLabel taskLabel;
JPanel panel;
JScrollPane scrollPane;
SimpleDateFormat simpleDate;
int i = 0;

public static void main(String[] args) {

    new Main();
}

private Main(){
    this.setSize(400, 600);
    this.setTitle("To-Do List");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setResizable(false);
    this.setLocationRelativeTo(null);


    panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    boxBox = Box.createVerticalBox();
    scrollPane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

    eventBox = Box.createHorizontalBox();
    eventBox.setBorder(BorderFactory.createEtchedBorder());

    JLabel plusSign = new JLabel("+");
    plusSign.setFont(new Font("Serafi", PLAIN, 20));
    plusSign.setMaximumSize(new Dimension(Integer.MAX_VALUE, plusSign.getMinimumSize().height));
    eventBox.add(plusSign);

    eventName = new JTextField(20);
    eventName.setFont(new Font("Times", Font.ITALIC, 15));
    eventName.setMaximumSize(new Dimension(Integer.MAX_VALUE, eventName.getMinimumSize().height));
    eventName.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if(e.getSource() == eventName){
                /* to do: saving every task in some file, figure out how to remove
                   those tasks (checkbox + jlabel) -> whole box from screen or how to send them to "done"
                   also "done" to do*/



                simpleDate = new SimpleDateFormat("E-dd-MM-yyyy");
                taskBox[i] = Box.createHorizontalBox();
                taskBox[i].setBorder(BorderFactory.createTitledBorder(simpleDate.format(date)));


                doneCheck[i] = new Checkbox();
                doneCheck[i].addItemListener(new ItemListener() {
                    @Override
                    public void itemStateChanged(ItemEvent e) {

                        int k = 0;
                        for (int j = 0; j < doneCheck.length; j++) {
                            if(doneCheck[j].getState()){
                                //remove(doneCheck[k]);
                                //System.out.println("?" + i + "?" + k + " " + e.getSource().toString());
                                System.out.println("xxxxx" + doneCheck[j].getState());
                                break;
                            }
                            System.out.println("oooooo");
                            k++;

                        }
                        System.out.println(doneCheck.length + taskBox[k].toString());
                        //System.out.println("! " + k +  " " + e.getSource().toString());
                        boxBox.remove(taskBox[k]);
                        //boxBox.removeAll();
                        boxBox.revalidate();
                        boxBox.repaint();

                    }
                });

                taskBox[i].add(doneCheck[i]);

                String taskName = eventName.getText();
                taskLabel = new JLabel(taskName);
                taskLabel.setMinimumSize(new Dimension(500,10));
                taskLabel.setPreferredSize(new Dimension(300, 10));
                taskBox[i].add(taskLabel);

                boxBox.add(taskBox[i]);
                boxBox.setMaximumSize(new Dimension(Integer.MAX_VALUE, boxBox.getMinimumSize().height + 11));
                panel.add(boxBox);
                panel.revalidate();
                panel.repaint();
                i++;
            }
        }
    });
    eventBox.add(eventName);

    date = new Date();
    dateSpin = new JSpinner(new SpinnerDateModel(date, null, null, Calendar.DAY_OF_MONTH));
    JSpinner.DateEditor dateEditor = new JSpinner.DateEditor(dateSpin, "dd/MM/yy");
    dateSpin.setEditor(dateEditor);
    dateSpin.setMaximumSize(new Dimension(Integer.MAX_VALUE, dateSpin.getMinimumSize().height));
    dateSpin.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            if(e.getSource() == dateSpin){
                date = (Date) dateSpin.getValue();

            }
        }
    });
    eventBox.add(dateSpin);

    panel.add(eventBox, new FlowLayout());

    this.add(scrollPane);
    this.setVisible(true);
}

Unless you are initializing doneCheck items somewhere, this:

Checkbox[] doneCheck = new Checkbox[1000];

And this:

int k = 0;
for (int j = 0; j < doneCheck.length; j++) {
    if (doneCheck[j].getState()) {
--------^^^^^^^^^^^^

Is probably one the reason it fails: you probably got a NullPointerException somewhere, eg: when value of j > 0 . The NPE will probably be catched by the EventDispatchThread which may or may not be kind enough to show it on stderr...

I fail to see why you are using this array, and you can shorten your code and avoid NPE like this:

Checkbox cb = new Checkbox();
cb.addItemListener(event -> {
  if (cb.getState()) { // not null!
    boxBox.remove(cb);
    boxBox.revalidate();
    boxBox.repaint();
  }
});

doneCheck[i] = cb; // I still don't know why you need that.

You never remove elements from the taskBox and doneCheck arrays.

Now if you mark the first entry as done, your ItemListener will always find this first entry when looping over the doneCheck array.

Marking the entries as done in reverse order (always the last shown entry) will remove one entry after the other.


As to your software design: it's considered bad practice to manage your data in several parallel arrays.

Please consider creating a custom class for the todo items that manages all the elements of a single todo item.

My guess is that you have 2 variables global int i = 0 and local int k = 0 in here

           public void itemStateChanged(ItemEvent e) {

                   // int k = 0;//<-------- LOCAL
                    for (int j = 0; j < doneCheck.length; j++) {
                        if(doneCheck[j].getState()){
                         //Either k = j;
                            boxBox.remove(taskBox[j]);                  
                            //remove(doneCheck[k]);
                            //System.out.println("?" + i + "?" + k + " " +      e.getSource().toString());
                            System.out.println("xxxxx" + doneCheck[j].getState());
                            break;
                        }
                        System.out.println("oooooo");
                        //k++;//<-- ALWAYS == last j value before the break;

                    }
                    System.out.println(doneCheck.length + taskBox[k].toString());
                    //System.out.println("! " + k +  " " + e.getSource().toString());
                    //boxBox.remove(taskBox[k]);//
                    //boxBox.removeAll();
                    boxBox.revalidate();
                    boxBox.repaint();

                }

Every time you call for itemStateChanged int k = 0; will be initialized to 0 and you will be removing element[j] from array of taskBox. As you k++ statement will be equal to the last j value before the break; because it sits after the if(doneCheck[j].getState()){... Try moving boxBox.remove(taskBox[j]); inside the for loop and using j instead of k.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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