Pastebin: Java Swing 日付コンボコントロール

Format
Java
Post date
2021-01-23 15:25
Publication Period
Unlimited
  1. import java.awt.Color;
  2. import java.awt.event.ActionEvent;
  3. import java.awt.event.ActionListener;
  4. import java.awt.event.KeyEvent;
  5. import java.beans.PropertyChangeEvent;
  6. import java.beans.PropertyChangeListener;
  7. import java.time.Instant;
  8. import java.util.Calendar;
  9. import java.util.GregorianCalendar;
  10. import java.util.regex.Matcher;
  11. import java.util.regex.Pattern;
  12. import javax.accessibility.Accessible;
  13. import javax.swing.AbstractAction;
  14. import javax.swing.JComboBox;
  15. import javax.swing.JComponent;
  16. import javax.swing.JPopupMenu;
  17. import javax.swing.JTextField;
  18. import javax.swing.KeyStroke;
  19. import javax.swing.event.DocumentEvent;
  20. import javax.swing.event.DocumentListener;
  21. import javax.swing.event.PopupMenuEvent;
  22. import javax.swing.event.PopupMenuListener;
  23. /*
  24. * カレンダー部品
  25. */
  26. public class JDateField extends JComboBox<String> implements CalendarListener{
  27. /*******************************************************************************
  28. * 定数
  29. ******************************************************************************/
  30. /*******************************************************************************
  31. * 部品
  32. ******************************************************************************/
  33. private JTextField textField = null;
  34. // private JButton buttonArrow = null;
  35. private JPopupMenu popupOrg = null;
  36. private JPopupMenu popup = null;
  37. private JCalendar calendar = null;
  38. private JCalendar.Style style = JCalendar.Style.Button;
  39. private boolean allowNoYear = false;
  40. private GregorianCalendar calMinDate = null;
  41. private GregorianCalendar calMaxDate = null;
  42. private Instant last_shown = null;
  43. /*******************************************************************************
  44. * コンストラクタ
  45. ******************************************************************************/
  46. public JDateField() {
  47. this(JCalendar.Style.Button);
  48. }
  49. public JDateField(JCalendar.Style s){
  50. super();
  51. style = s;
  52. setEditable(true);
  53. setMaximumRowCount(0);
  54. createComponents();
  55. }
  56. /*******************************************************************************
  57. * 公開メソッド
  58. ******************************************************************************/
  59. /*
  60. * ポップアップを表示する
  61. *
  62. * @see javax.swing.JComboBox#showPopup()
  63. */
  64. @Override
  65. public void showPopup(){
  66. setSelectedDate(getCalendar(getText()));
  67. if (last_shown != null && Instant.now().minusMillis(100).isBefore(last_shown))
  68. return;
  69. popup.show(this, 0, getBounds().height);
  70. last_shown = Instant.now();
  71. }
  72. /*
  73. * ポップアップを非表示にする
  74. *
  75. * @see javax.swing.JComboBox#hidePopup()
  76. */
  77. @Override
  78. public void hidePopup(){
  79. if (popup.isVisible())
  80. popup.setVisible(false);
  81. }
  82. /*
  83. * 年なしを許すかをセットする
  84. */
  85. public void setAllowNoYear(boolean b){
  86. allowNoYear = b;
  87. }
  88. /*
  89. * アクションリスナーを追加する
  90. */
  91. @Override
  92. public void addActionListener(ActionListener al){
  93. textField.addActionListener(al);
  94. }
  95. /*
  96. * 選択日をYYYY/MM/DD形式で指定する
  97. */
  98. public void setSelectedDate(String date){
  99. setSelectedDate(getCalendar(date));
  100. }
  101. /*
  102. * 選択日をGregorianCalendarクラスで指定する
  103. */
  104. public void setSelectedDate(GregorianCalendar cal){
  105. cleanUpCalendarTime(cal);
  106. if (calendar != null)
  107. calendar.setSelectedDate(cal);
  108. }
  109. /*
  110. * 有効な最小日をYYYY/MM/DD形式で指定する
  111. */
  112. public void setMinDate(String date){
  113. setMinDate(getCalendar(date));
  114. }
  115. /*
  116. * 有効な最小日をGregorianCalendarクラスで指定する
  117. */
  118. public void setMinDate(GregorianCalendar cal){
  119. cleanUpCalendarTime(cal);
  120. calMinDate = cal;
  121. if (calendar != null)
  122. calendar.setMinDate(cal);
  123. }
  124. /*
  125. * 有効な最大日をYYYY/MM/DD形式で指定する
  126. */
  127. public void setMaxDate(String date){
  128. setMaxDate(getCalendar(date));
  129. }
  130. /*
  131. * 有効な最大日をGregorianCalendarクラスで指定する
  132. */
  133. public void setMaxDate(GregorianCalendar cal){
  134. cleanUpCalendarTime(cal);
  135. calMaxDate = cal;
  136. if (calendar != null)
  137. calendar.setMaxDate(cal);
  138. }
  139. /*
  140. * テキストを取得する
  141. */
  142. public String getText(){
  143. return textField.getText();
  144. }
  145. /*
  146. * テキストをセットする
  147. */
  148. public void setText(String s){
  149. setSelectedItem(s);
  150. setSelectedDate(getCalendar(s));
  151. }
  152. /*******************************************************************************
  153. * コンポーネント
  154. ******************************************************************************/
  155. /*
  156. * コンポーネントを作成する
  157. */
  158. private void createComponents(){
  159. // オリジナルのポップアップを取得する
  160. Accessible accessible = this.getUI().getAccessibleChild(this, 0);
  161. if (accessible instanceof JPopupMenu ){
  162. popupOrg = (JPopupMenu) accessible;
  163. popupOrg.addPropertyChangeListener(pcl_popupOrg);
  164. }
  165. // テキストフィールドを取得する
  166. try{
  167. // buttonArrow = (JButton)this.getComponent(0);
  168. textField = (JTextField) this.getEditor().getEditorComponent();
  169. textField.getDocument().addDocumentListener(dl_textField);
  170. }
  171. catch(ClassCastException e){
  172. }
  173. // カレンダーのポップアップを作成する
  174. calendar = new JCalendar(style, false);
  175. calendar.setListener(this);
  176. String ESCKEYACTION = "escape";
  177. calendar.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
  178. .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ESCKEYACTION);
  179. calendar.getActionMap().put(ESCKEYACTION, aa_calendarEscape);
  180. popup = new JPopupMenu();
  181. popup.add(calendar);
  182. popup.addPopupMenuListener(pml_popup);
  183. }
  184. /*******************************************************************************
  185. * リスナー
  186. ******************************************************************************/
  187. /*
  188. * オリジナルのポップアップのプロパティ変更リスナー
  189. */
  190. private PropertyChangeListener pcl_popupOrg = new PropertyChangeListener(){
  191. @Override
  192. public void propertyChange(PropertyChangeEvent evt) {
  193. // オリジナルが表示されたら強制的に非表示にして、カレンダーをポップアップする
  194. if (evt.getPropertyName().equals("visible") && evt.getNewValue() == Boolean.TRUE){
  195. popupOrg.setVisible(false);
  196. showPopup();
  197. }
  198. }
  199. };
  200. /*
  201. * カレンダーのポップアップのリスナー
  202. */
  203. private PopupMenuListener pml_popup = new PopupMenuListener(){
  204. @Override
  205. public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
  206. }
  207. @Override
  208. public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
  209. // 非表示になる時に時刻を保存する
  210. last_shown = Instant.now();
  211. }
  212. @Override
  213. public void popupMenuCanceled(PopupMenuEvent e) {
  214. }
  215. };
  216. /*
  217. * テキストフィールドの文書変更リスナー
  218. *
  219. * 文書が変更されたら前景色を更新してポップアップを非表示にする
  220. */
  221. private DocumentListener dl_textField = new DocumentListener(){
  222. @Override
  223. public void insertUpdate(DocumentEvent e) {
  224. updateTextFieldForeground();
  225. hidePopup();
  226. }
  227. @Override
  228. public void removeUpdate(DocumentEvent e) {
  229. updateTextFieldForeground();
  230. hidePopup();
  231. }
  232. @Override
  233. public void changedUpdate(DocumentEvent e) {
  234. updateTextFieldForeground();
  235. hidePopup();
  236. }
  237. };
  238. /*
  239. * カレンダーの時刻変更リスナー
  240. *
  241. * @see tainavi.CalendarListener#notifyDateChange(tainavi.JCalendar, java.lang.String)
  242. */
  243. @Override
  244. public void notifyDateChange(JCalendar cal, String date) {
  245. // テキストをセットしてポップアップを非表示にする
  246. setText(date);
  247. hidePopup();
  248. }
  249. /*
  250. * カレンダーをESCキーで抜けるための仮想アクション
  251. */
  252. private AbstractAction aa_calendarEscape = new AbstractAction(){
  253. @Override
  254. public void actionPerformed(ActionEvent e) {
  255. hidePopup();
  256. }
  257. };
  258. /*******************************************************************************
  259. * 内部関数
  260. ******************************************************************************/
  261. /*
  262. * テキストフィールドの前景色を更新する
  263. */
  264. private void updateTextFieldForeground(){
  265. textField.setForeground(isValidDate((textField.getText())) ? Color.BLACK : Color.RED);
  266. }
  267. /*
  268. * 指定された文字列が有効な日付かどうかを返す
  269. */
  270. private boolean isValidDate(String s){
  271. if (s == null)
  272. return false;
  273. if (s.isEmpty())
  274. return true;
  275. // YYYY/MM/DD形式であること
  276. GregorianCalendar cal = getCalendar(s);
  277. if (cal == null)
  278. return false;
  279. // 最小値、最大値の間にあること
  280. if (!isValidDateRange(cal))
  281. return false;
  282. return true;
  283. }
  284. /*
  285. * 指定された日が最小値、最大値の間にあるかを返す
  286. */
  287. private boolean isValidDateRange(GregorianCalendar cal){
  288. long time = cal.getTimeInMillis();
  289. if (calMinDate != null){
  290. long timeMin = calMinDate.getTimeInMillis();
  291. if (time < timeMin)
  292. return false;
  293. }
  294. if (calMaxDate != null){
  295. long timeMax = calMaxDate.getTimeInMillis();
  296. if (time > timeMax)
  297. return false;
  298. }
  299. return true;
  300. }
  301. /*
  302. * カレンダーの時分秒をクリアする
  303. */
  304. private void cleanUpCalendarTime(GregorianCalendar cal){
  305. if (cal == null)
  306. return;
  307. cal.set(Calendar.HOUR, 0);
  308. cal.set(Calendar.MINUTE, 0);
  309. cal.set(Calendar.SECOND, 0);
  310. cal.set(Calendar.MILLISECOND, 0);
  311. }
  312. /*
  313. * YYYY/MM/DD, MM/DD形式からGregorianCalendarクラスを取得する
  314. */
  315. private GregorianCalendar getCalendar(String date) {
  316. if (date == null)
  317. return null;
  318. GregorianCalendar cal = new GregorianCalendar();
  319. int year, month, day;
  320. // YYYY/MM/DD形式
  321. Matcher ma = Pattern.compile("^(\\d{4})/(\\d{2})/(\\d{2})$").matcher(date);
  322. if ( ma.find()) {
  323. year = Integer.parseInt(ma.group(1));
  324. month = Integer.parseInt(ma.group(2));
  325. day = Integer.parseInt(ma.group(3));
  326. }
  327. else{
  328. if (!allowNoYear)
  329. return null;
  330. // MM/DD形式
  331. Matcher mb = Pattern.compile("^(\\d{2})/(\\d{2})$").matcher(date);
  332. if (mb.find()){
  333. year = cal.get(Calendar.YEAR);
  334. month = Integer.parseInt(mb.group(1));
  335. day = Integer.parseInt(mb.group(2));
  336. }
  337. else
  338. return null;
  339. }
  340. // 年、月、日の値が有効な範囲であること
  341. if (!isValidDate(year, month, day))
  342. return null;
  343. cal.set(Calendar.YEAR, year);
  344. cal.set(Calendar.MONTH, month-1);
  345. cal.set(Calendar.DATE, day);
  346. cleanUpCalendarTime(cal);
  347. return cal;
  348. }
  349. /*
  350. * 年、月、日の値が有効な範囲であるかを返す
  351. */
  352. private boolean isValidDate(int year, int month, int day){
  353. if (year < 1900 || year > 2100)
  354. return false;
  355. if (month < 1 || month > 12)
  356. return false;
  357. GregorianCalendar cal = new GregorianCalendar(year, month-1, 1, 0, 0, 0);
  358. if (day < 1 || day > cal.getActualMaximum(Calendar.DATE))
  359. return false;
  360. return true;
  361. }
  362. }
다운로드 Printable view

URL of this paste

Embed with JavaScript

Embed with iframe

Raw text