Propertychangelistener на JDateChooser запускается дополнительно 2 раза, даже если слушатель отключен во время программной установки даты - PullRequest
0 голосов
/ 07 сентября 2018

Я использую JDateChooser для проекта Java-разработки, который я разрабатываю, и в этом случае дата может быть установлена ​​двумя способами: конечным пользователем или программно.

Итак, я определил propertychangelistener в соответствующем классе (переменная trig инициализируется равной нулю и отслеживает, сколько раз прослушивается изменение свойства).

public class WriteEntry{
private int trig=0;
private Date currentDate = new Date();
public JDateChooser dateChooser = new JDateChooser();
public CustomDate selectedDate = DateConverter.convertDate(currentDate);
private static String filename = StorageSpace.currentpath+CurrentUser.getInstance().getUserName()+"\\"+
        Integer.toString(selectedDate.getYear())+"\\"
          +Integer.toString(selectedDate.getMonth())+"\\"+Integer.toString(selectedDate.getDay())+".txt";
private JLabel dayinfo = new JLabel("");
private JTextArea contentfield = new JTextArea("");
private PropertyChangeListener lis = new PropertyChangeListener(){
      @Override
      public void propertyChange(PropertyChangeEvent e) {
          System.out.println("triggered "+trig++);
            if(dateBoundary())  {
                selectedDate = DateConverter.convertDate(dateChooser);
                filename = StorageSpace.currentpath+CurrentUser.getInstance().getUserName()+"\\"+
                        Integer.toString(selectedDate.getYear())+"\\"
                          +Integer.toString(selectedDate.getMonth())+"\\"+Integer.toString(selectedDate.getDay())+".txt";
            }
            else {
                updateDateChooser(selectedDate);
            }
            if(isAlreadyWritten())
            {
                try {
                    updateEditFields(selectedDate, "content");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }   
            }
            else
            {       
                contentfield.setText("Start writing here");
                dayinfo.setText("You are making entry for: "+ new SimpleDateFormat("dd/MM/yyyy").format(dateChooser.getDate()));        
            }
      }      
    };
WriteEntry() //constructor
{
dateChooser.setDateFormatString("dd MM yyyy");
dateChooser.addPropertyChangeListener(lis);
updateEditFields(DateConverter.convertDate(currentDate), "Start");
}
}

А вот код для dateBoundary ():

public static boolean dateBoundary() {
    Object[] option = {"I get it","My Bad!"};
    if(dateChooser.getDate().compareTo(currentDate)>0) {
        JOptionPane.showOptionDialog(HomePage.getFrame(),"message1",
                "",JOptionPane.DEFAULT_OPTION,JOptionPane.ERROR_MESSAGE,null,option,option[0]);
        return false;
    }
    if(dateChooser.getDate().compareTo(DateConverter.convertfromCustom(CurrentUser.getInstance().getDob()))<0){
JOptionPane.showOptionDialog(HomePage.getFrame(),"message2",
                "",JOptionPane.DEFAULT_OPTION,JOptionPane.ERROR_MESSAGE,null,option,option[0]);
        return false;
    }
    return true;
}

Код для isAlreadyWritten ():

public static boolean isAlreadyWritten() {
    File f = new File(filename);
    if(f.length()!=0)
    {
        Object[] option = {"Read","Edit"};
        JOptionPane.showOptionDialog(HomePage.getFrame(),"You already updated diary for this day. Do you want to edit?",
                "",JOptionPane.DEFAULT_OPTION,JOptionPane.INFORMATION_MESSAGE,null,option,option[0]);
        return true;
    }
    else
        return false;
}

Код для updateDateChooser ():

public static void updateDateChooser(CustomDate date) {
dateChooser.removePropertyChangeListener(lis); //to stop it from getting triggered when date is set programatically
dateChooser.setDate(DateConverter.convertfromCustom(date));
dateChooser.addPropertyChangeListener(lis);
}

Код для updateEditFields ():

public static void updateEditFields(CustomDate searchDate, String excontent) {
updateDateChooser(searchDate);
selectedDate = DateConverter.convertDate(dateChooser);
dayinfo.setText("You are editing entry for: "+ new SimpleDateFormat("dd/MM/yyyy").format(dateChooser.getDate()));
contentfield.setText(excontent);

}

Теперь моя функция определения даты работает как положено. Всякий раз, когда выбирается дата, превышающая текущую, отображается диалоговое окно optiondial, и оно исчезает после щелчка, а datechooser устанавливается на последнюю выбранную дату, хотя метод propertychange называется трижды:

  • один раз до отображения диалогового окна
  • дважды после закрытия диалога.

Но мой isAlreadyWritten () работает не так, как ожидалось, и optiondialog отображается 4 раза, а метод propertychange () вызывается четыре раза: один раз перед каждым отображением диалогового окна.

Я хочу понять, почему propertychange вызывается 4 раза, хотя datechooser отсоединяется от слушателя, когда дата установлена ​​программно?

1 Ответ

0 голосов
/ 07 сентября 2018

Итак, я собрал этот быстрый фрагмент и запустил его

import com.toedter.calendar.JDateChooser;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
                JDateChooser dateChooser = new JDateChooser();
                dateChooser.addPropertyChangeListener(new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        System.out.println(evt.getPropertyName());
                    }
                });
                dateChooser.setDate(new Date());

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(dateChooser);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

Я открыл селектор даты и выбрал дату. Программа вывела ...

date
ancestor
date
date
  1. Я установил дату программно
  2. ancestor это добавляется в контейнер
  3. Был ли я выбрать дату выбора
  4. Выбрал ли я дату

Итак, как вы можете видеть, вы не только получаете спам с большим количеством изменений свойства «дата», вы также получаете все «другие» изменения свойства ?

Итак, первое, что вы хотите сделать, это ограничить уведомления только свойством date, что-то вроде ...

dateChooser.addPropertyChangeListener("date", new PropertyChangeListener() {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        System.out.println(evt.getPropertyName());
    }
});

Это, по крайней мере, означает, что вас не волнует вся дополнительная информация, которая вас не волнует.

Хотя вы можете добавлять и удалять слушателя, я, как правило, испытываю боль, поскольку у меня не всегда есть ссылка на слушателей, но вместо этого я склоняюсь использовать флаг состояния

private boolean manualDate = false;
//...
dateChooser.addPropertyChangeListener("date", new PropertyChangeListener() {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (manualDate) {
            return;
        }
        System.out.println(evt.getPropertyName());
    }
});

manualDate = true;
dateChooser.setDate(new Date());
manualDate = false;

Не большое изменение, но само по себе это означает, что теперь у вас осталось два уведомления о событиях.

Вместо этого вы должны сравнить oldValue с newValue из PropertyChangeEvent

JDateChooser dateChooser = new JDateChooser();
dateChooser.addPropertyChangeListener("date", new PropertyChangeListener() {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (manualDate) {
            return;
        }
        Date newDate = (Date) evt.getNewValue();
        Date oldDate = (Date) evt.getOldValue();
        if (newDate != null && oldDate != null) {
            LocalDate newLD = LocalDate.ofInstant(newDate.toInstant(), ZoneId.systemDefault());
            LocalDate oldLD = LocalDate.ofInstant(oldDate.toInstant(), ZoneId.systemDefault());
            if (newLD.equals(oldLD)) {
                return;
            }
        }
        System.out.println(evt.getPropertyName());
    }
});

А теперь мы подошли к одному событию изменения. Единственный недостаток - он не скажет вам, когда они повторно выберут текущую дату.

Немного лучший рабочий процесс может состоять в том, чтобы игнорировать все это и просто иметь JButton, который пользователь может нажать, чтобы выполнить любые когда-либо связанные действия, которые вам нужно выполнить

Пример выполнения ...

import com.toedter.calendar.JDateChooser;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    private boolean manualDate;

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
                JDateChooser dateChooser = new JDateChooser();
                dateChooser.addPropertyChangeListener("date", new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        if (manualDate) {
                            return;
                        }
                        Date newDate = (Date) evt.getNewValue();
                        Date oldDate = (Date) evt.getOldValue();
                        if (newDate != null && oldDate != null) {
                            LocalDate newLD = LocalDate.ofInstant(newDate.toInstant(), ZoneId.systemDefault());
                            LocalDate oldLD = LocalDate.ofInstant(oldDate.toInstant(), ZoneId.systemDefault());
                            if (newLD.equals(oldLD)) {
                                return;
                            }
                        }
                        System.out.println(evt.getPropertyName());
                    }
                });
                manualDate = true;
                dateChooser.setDate(new Date());
                manualDate = false;

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(dateChooser);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}
...