嗯,问题是我正在使用JCalendar库中的IDateEditor
接口实现,我注意到Component.firePropertyChange(String propertyName, Object oldValue, Object newValue)方法不公开但受保护。情况如下所示:
public class DateFormattedTextField implements IDateEditor {
private JFormattedTextField editor;
private DateUtil dateUtil;
...
@Override
public void setDate(Date date) {
Date oldDate = (Date)editor.getValue();
if(dateUtil.checkDate(date)){
editor.setValue(date);
editor.firePropertyChange("date", oldDate, date); // <-- error here
}
}
}
正如您所看到的,由于此方法受到保护,我无法触发属性更改。当然,如果我让我的课程从JFormattedTextfield延伸而不是使用简单的变量,我可以很容易地摆脱这个问题。
public class DateFormattedTextField extends JFormattedTextField implements IDateEditor {
private DateUtil dateUtil;
...
@Override
public void setDate(Date date) {
Date oldDate = (Date)getValue();
if(dateUtil.checkDate(date)){
setValue(date);
firePropertyChange("date", oldDate, date); // <-- No problem here
}
}
}
但那不是我要问的。我想知道:为什么这种方法受到保护?
我知道它应该是一些设计问题,但我无法弄清楚为什么会这样,特别是考虑到大多数触发属性更改事件的方法都是公开的:
也许最有经验的开发人员可以对此有所了解。提前谢谢。
到目前为止,这是我的代码。随意使用/修改/播放它。
public class DateFormattedTextField implements IDateEditor {
private JFormattedTextField editor;
private DateUtil dateUtil;
private DateFormat dateFormat;
private String dateFormatString;
public DateFormattedTextField(){
dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
editor = new JFormattedTextField(dateFormat);
editor.setColumns(10);
editor.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT);
dateUtil = new DateUtil();
}
@Override
public Date getDate() {
return (Date)editor.getValue();
}
@Override
public void setDate(Date date) {
Date oldDate = (Date)editor.getValue();
if(dateUtil.checkDate(date)){
editor.setValue(date);
editor.firePropertyChange("date", oldDate, date); // <-- error here
}
}
@Override
public void setDateFormatString(String dateFormatString) {
this.dateFormatString = dateFormatString;
}
@Override
public String getDateFormatString() {
return this.dateFormatString;
}
@Override
public void setSelectableDateRange(Date min, Date max) {
dateUtil.setSelectableDateRange(min, max);
}
@Override
public Date getMaxSelectableDate() {
return dateUtil.getMaxSelectableDate();
}
@Override
public Date getMinSelectableDate() {
return dateUtil.getMinSelectableDate();
}
@Override
public void setMaxSelectableDate(Date max) {
dateUtil.setMaxSelectableDate(max);
}
@Override
public void setMinSelectableDate(Date min) {
dateUtil.setMinSelectableDate(min);
}
@Override
public JComponent getUiComponent() {
return editor;
}
@Override
public void setLocale(Locale locale) {
editor.setLocale(locale); // to be reviewed
}
@Override
public void setEnabled(boolean enabled) {
editor.setEnabled(enabled);
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
editor.addPropertyChangeListener(listener);
}
@Override
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
editor.addPropertyChangeListener(propertyName, listener);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
editor.removePropertyChangeListener(listener);
}
@Override
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
editor.removePropertyChangeListener(propertyName, listener);
}
}
答案 0 :(得分:3)
更改通知是任何observable(通常是特别是java bean)的固有责任:如果在更改任何绑定属性时不会触发PropertyChangeEvent,它将违反其合同。既然如此,其他任何一方都不需要使用fireXX方法。
因此,范围比保护范围更广泛没有任何意义。如果您觉得需要它,那么您做错了。
答案 1 :(得分:0)
为了完整起见,我正在添加这个答案。按照@ kleopatra的明智解释和评论,我意识到我在混合概念和责任。在这种情况下,属性更改通知不是用作基础编辑器组件的JFormattedTextField
,而是IDateEditor
实现本身。
因此,为了完成界面,我使用了PropertyChangeSupport来保存属性更改侦听器列表并通过PropertyChangeEvent通知它们:
public class DefaultDateEditor implements IDateEditor {
...
private final JFormattedTextField editor;
private final PropertyChangeSupport propertyChangeSupport;
...
public DefaultDateEditor() {
...
propertyChangeSupport = new PropertyChangeSupport(this);
...
editor = new JFormattedTextField();
...
}
@Override
public final void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
@Override
public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
@Override
public final void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
@Override
public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
public final PropertyChangeListener[] getPropertyChangeListeners() {
return propertyChangeSupport.getPropertyChangeListeners();
}
public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
return propertyChangeSupport.getPropertyChangeListeners(propertyName);
}
protected final void firePropertyChangeEvent(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
...
}
正如您所看到的,添加/删除PropertyChangeListeners
并在属性更改事件上通知他们的任务被委托给PropertyChangeSupport
类成员,但事件的来源是this
,那是IDateEditor
接口实现者。正如@kleopatra所说,这是客户期望成为事件来源的来源,而不是潜在的编辑组件。
以下是结果类的完整代码(对不起扩展名)。如果您愿意,请随意使用/修改或玩它。
/**
* Custom implementation of {@code IDateEditor} interface. Unlike the default
* implementation provided with JCalendar library, this won't allow invalid
* inputs of any kind from the user.
*
* @author dic19
*/
public class DefaultDateEditor implements IDateEditor {
private Date date;
private String dateFormatString;
private Locale locale;
private SimpleDateFormat dateFormat;
private final DateFormatter dateFormatter;
private final JFormattedTextField editor;
private final DateUtil dateUtil;
private final PropertyChangeSupport propertyChangeSupport;
public DefaultDateEditor() {
date = new Date();
dateUtil = new DateUtil();
propertyChangeSupport = new PropertyChangeSupport(this);
addPropertyChangeListener("dateFormatString", new DateFormatStringChangeListener());
addPropertyChangeListener("locale", new LocaleChangeListener());
dateFormatString = "yyyy-MM-dd HH:mm:ss";
locale = Locale.getDefault();
dateFormat = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale);
dateFormat.applyPattern(dateFormatString);
dateFormatter = new DateFormatter();
dateFormatter.setCommitsOnValidEdit(true);
dateFormatter.setAllowsInvalid(false);
dateFormatter.setOverwriteMode(true);
editor = new JFormattedTextField();
editor.setValue(date);
editor.setColumns(15);
editor.setToolTipText(dateFormatString);
editor.addPropertyChangeListener("value", new EditorValueChangeListener());
installFormatterFactoryOnEditor();
}
private void installFormatterFactoryOnEditor() {
dateFormatter.setFormat(dateFormat);
DefaultFormatterFactory factory = editor.getFormatterFactory() instanceof DefaultFormatterFactory
? (DefaultFormatterFactory) editor.getFormatterFactory()
: new DefaultFormatterFactory();
factory.setDefaultFormatter(dateFormatter);
factory.setDisplayFormatter(dateFormatter);
factory.setEditFormatter(dateFormatter);
factory.setNullFormatter(dateFormatter);
editor.setFormatterFactory(factory);
}
@Override
public Date getDate() {
return date != null ? new Date(date.getTime()) : null;
}
@Override
public void setDate(Date date) {
if (dateUtil.checkDate(date)) {
Date oldValue = this.date;
this.date = date != null ? new Date(date.getTime()) : null;
editor.setValue(this.date);
firePropertyChangeEvent("date", oldValue, date);
}
}
@Override
public void setDateFormatString(String dateFormatString) {
String oldDateFormat = this.dateFormatString;
this.dateFormatString = dateFormatString;
firePropertyChangeEvent("dateFormatString", oldDateFormat, dateFormatString);
}
@Override
public String getDateFormatString() {
return dateFormatString;
}
@Override
public void setSelectableDateRange(Date min, Date max) {
dateUtil.setSelectableDateRange(min, max);
}
@Override
public Date getMaxSelectableDate() {
return dateUtil.getMaxSelectableDate();
}
@Override
public Date getMinSelectableDate() {
return dateUtil.getMinSelectableDate();
}
@Override
public void setMaxSelectableDate(Date max) {
dateUtil.setMaxSelectableDate(max);
}
@Override
public void setMinSelectableDate(Date min) {
dateUtil.setMinSelectableDate(min);
}
@Override
public JComponent getUiComponent() {
return editor;
}
@Override
public void setLocale(Locale locale) {
Locale oldLocale = this.locale;
this.locale = locale;
firePropertyChangeEvent("locale", oldLocale, locale);
}
@Override
public void setEnabled(boolean enabled) {
editor.setEnabled(enabled);
}
@Override
public final void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
@Override
public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
@Override
public final void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
@Override
public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
public final PropertyChangeListener[] getPropertyChangeListeners() {
return propertyChangeSupport.getPropertyChangeListeners();
}
public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
return propertyChangeSupport.getPropertyChangeListeners(propertyName);
}
protected final void firePropertyChangeEvent(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
private class EditorValueChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("value".equals(evt.getPropertyName())) {
System.out.println("Old value:" + evt.getOldValue());
System.out.println("New value:" + evt.getNewValue());
setDate((Date)evt.getNewValue());
}
}
}
private class DateFormatStringChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("dateFormatString".equals(evt.getPropertyName())) {
dateFormat.applyPattern(dateFormatString);
editor.setToolTipText(dateFormatString);
installFormatterFactoryOnEditor();
}
}
}
private class LocaleChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("locale".equals(evt.getPropertyName())) {
dateFormat = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale);
dateFormat.applyPattern(dateFormatString);
editor.setLocale(locale);
installFormatterFactoryOnEditor();
}
}
}
}