先看效果:
1 JToolBar jToolBar = new JToolBar(); 2 3 DptLocalDatePicker searchDate = new DptLocalDatePicker(null, DateTimeFormatter.ofPattern("yyyy-MM-dd")) { 4 @Override 5 public Dimension getMaximumSize() { 6 return new Dimension(140, 35); 7 } 8 9 @Override 10 public void setValue(LocalDate value) { 11 super.setValue(value); 12 initData(); 13 } 14 }; 15 16 searchDate.setFocusable(false); 17 searchDate.setPreferredSize(new Dimension(140, 35)); 18 } 19 20 JButton clearDateBtn = new JButton(new FlatSVGIcon("icons/deletes.svg", 20, 20)); 21 clearDateBtn.addActionListener(new ActionListener() { 22 @Override 23 public void actionPerformed(ActionEvent e) { 24 searchDate.setValue(null); 25 } 26 }); 27 28 jToolBar.add(searchDate); 29 jToolBar.add(clearDateBtn);业务应用
<properties> <flatlaf.version>3.1.1</flatlaf.version> </properties> <dependency> <groupId>com.formdev</groupId> <artifactId>flatlaf</artifactId> <version>${flatlaf.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.formdev</groupId> <artifactId>flatlaf-intellij-themes</artifactId> <version>${flatlaf.version}</version> </dependency> <dependency> <groupId>com.formdev</groupId> <artifactId>flatlaf-extras</artifactId> <version>${flatlaf.version}</version> </dependency>pom.xml相关依赖
1 package com.example.dpt.component; 2 3 import com.formdev.flatlaf.extras.FlatSVGIcon; 4 import com.formdev.flatlaf.ui.FlatComboBoxUI; 5 6 import javax.swing.*; 7 import javax.swing.event.PopupMenuEvent; 8 import javax.swing.event.PopupMenuListener; 9 import java.awt.*; 10 import java.time.LocalDate; 11 import java.time.format.DateTimeFormatter; 12 import java.time.format.FormatStyle; 13 import java.time.temporal.TemporalAccessor; 14 15 16 /** 17 * @Description: 日期组件 18 * @param: 19 * @return: 20 */ 21 public class DptLocalDatePicker extends JComboBox<LocalDate> { 22 23 public final DefaultComboBoxModel<LocalDate> comboModel = new DefaultComboBoxModel<>(); 24 public LocalDate min; 25 public LocalDate max; 26 public DptMonthView DptMonthView; 27 public final JPopupMenu popupMenu = new JPopupMenu(); 28 29 /** 30 * Constructs a LocalDateCombo with today's date, no upper or lower limits, formatted in a medium 31 * style. 32 * 33 * @see FormatStyle#MEDIUM 34 */ 35 public DptLocalDatePicker() { 36 this(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)); 37 } 38 39 /** 40 * Constructs a LocalDateCombo with today's date and no upper or lower limits, formatted according 41 * to the provided formatter. 42 * 43 * @param formatter Formats the date for display 44 * @see DateTimeFormatter 45 */ 46 public DptLocalDatePicker(DateTimeFormatter formatter) { 47 this(LocalDate.now(), formatter); 48 } 49 50 /** 51 * Constructs a LocalDateCombo with the date provided and no lower or upper limits, formatted in a 52 * medium style. 53 * 54 * @param value The initial value 55 * @see FormatStyle#MEDIUM 56 */ 57 public DptLocalDatePicker(LocalDate value) { 58 this(value, DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)); 59 } 60 61 /** 62 * Constructs a LocalDateCombo with the date provided and no lower or upper limits, formatted 63 * according to the provided formatter. 64 * 65 * @param value The initial value 66 * @param formatter Formats the date for display 67 */ 68 public DptLocalDatePicker(LocalDate value, DateTimeFormatter formatter) { 69 this(value, null, null, formatter); 70 } 71 72 /** 73 * Constructs a LocalDateCombo with the date, lower (earliest) and upper (latest) limits provided, 74 * formatted in a medium style. 75 * <p> 76 * Dates outside the specified range are not displayed. 77 * <p> 78 * This class does not attempt to verify that minDate <= value <= maxDate. It is the 79 * responsibility of client code to supply sane values. 80 * 81 * @param value The initial value 82 * @param minDate The minimum value (earliest date); <CODE>null</CODE> for no limit. 83 * @param maxDate The maximum value (latest date); <CODE>null</CODE> for no limit. 84 * @see FormatStyle#MEDIUM 85 */ 86 public DptLocalDatePicker(LocalDate value, LocalDate minDate, LocalDate maxDate) { 87 this(value, minDate, maxDate, DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)); 88 } 89 90 /** 91 * Constructs a LocalDateCombo with the date, lower (earliest) and upper (latest) limits provided, 92 * formatted according to the provided formatter. 93 * <p> 94 * Dates outside the specified range are not displayed. 95 * <p> 96 * This class does not attempt to verify that minDate <= value <= maxDate. It is the 97 * responsibility of client code to supply sane values. 98 * 99 * @param value The initial value 100 * @param minDate The minimum value (earliest date); <CODE>null</CODE> for no limit. 101 * @param maxDate The maximum value (latest date); <CODE>null</CODE> for no limit. 102 * @param formatter Formats the date for display 103 */ 104 public DptLocalDatePicker(LocalDate value, LocalDate minDate, LocalDate maxDate, 105 DateTimeFormatter formatter) { 106 // Thursday after 24 of September 107 initCompont(value, minDate, maxDate, formatter); 108 addPopupMenuListener(new PopupMenuListener() { 109 @Override 110 public void popupMenuWillBecomeVisible(PopupMenuEvent pme) { 111 112 final boolean popupShown = popupMenu.isShowing(); 113 SwingUtilities.invokeLater(() -> { 114 hidePopup(); 115 if (popupShown) { 116 popupMenu.setVisible(false); 117 } else { 118 popupMenu.show(DptLocalDatePicker.this, 0, getHeight()); 119 } 120 }); 121 } 122 123 @Override 124 public void popupMenuWillBecomeInvisible(PopupMenuEvent pme) { 125 } 126 127 @Override 128 public void popupMenuCanceled(PopupMenuEvent pme) { 129 } 130 }); 131 } 132 133 private void initCompont(LocalDate value, LocalDate minDate, LocalDate maxDate, DateTimeFormatter formatter) { 134 if (value == null) { 135 value = LocalDate.now(); 136 } 137 LocalDate longNameDate = LocalDate.now().withDayOfMonth(24).withMonth(9); 138 longNameDate = longNameDate.plusDays(4 - longNameDate.getDayOfWeek().getValue()); 139 setPrototypeDisplayValue(longNameDate); 140 DptMonthView = new DptMonthView(value, minDate, maxDate, true); 141 comboModel.addElement(value); 142 143 setRenderer(new DefaultListCellRenderer() { 144 @Override 145 public Component getListCellRendererComponent(JList<?> list, Object value, 146 int index, boolean isSelected, boolean hasFocus) { 147 super.getListCellRendererComponent(list, value, index, isSelected, hasFocus); 148 if (value != null) { 149 setText(formatter.format((TemporalAccessor) value)); 150 } 151 return this; 152 } 153 }); 154 min = minDate; 155 max = maxDate; 156 popupMenu.add(DptMonthView); 157 DptMonthView.addPropertyChangeListener("Confirm", pce -> { 158 popupMenu.setVisible(false); 159 }); 160 DptMonthView.addPropertyChangeListener("Value", pce -> { 161 setValue((LocalDate) pce.getNewValue()); 162 firePropertyChange("Value", pce.getOldValue(), pce.getNewValue()); 163 }); 164 } 165 166 /** 167 * Returns the current value 168 * 169 * @return the current value 170 */ 171 public LocalDate getValue() { 172 ComboBoxModel<LocalDate> model = getModel(); 173 Object selectedItem = model.getSelectedItem(); 174 if (selectedItem == null) { 175 return null; 176 } 177 return LocalDate.parse(selectedItem.toString());//value; 178 } 179 180 /** 181 * Sets the current value, adjusted to be within any specified min/max range. 182 * 183 * @param value The value to set 184 */ 185 public void setValue(LocalDate value) { 186 if (getSelectedItem() == null) { 187 setModel(comboModel); 188 } else { 189 if (getSelectedItem().equals(value)) { 190 return; 191 } 192 } 193 194 if (min != null && value.isBefore(min)) { 195 value = min; 196 } 197 if (max != null && value.isAfter(max)) { 198 value = max; 199 } 200 comboModel.removeAllElements(); 201 comboModel.addElement(value); 202 if (DptMonthView.getValue() != null) { 203 if (!DptMonthView.getValue().equals(value)) { 204 DptMonthView.setValue(value); 205 } 206 } 207 } 208 209 /** 210 * Returns the minimum value (earliest date), or <CODE>null</CODE> if no limit is set. 211 * 212 * @return The earliest date that can be selected. 213 */ 214 public LocalDate getMin() { 215 return min; 216 } 217 218 /** 219 * Sets the minimum value (earliest date). Call this method with a <CODE>null</CODE> value for no 220 * limit. 221 * <p> 222 * This class does not attempt to verify that min <= value <= max. It is the responsibility of 223 * client code to supply a sane value. 224 * 225 * @param min The earliest date that can be selected, or <CODE>null</CODE> for no limit. 226 */ 227 public void setMin(LocalDate min) { 228 this.min = min; 229 DptMonthView.setMin(min); 230 } 231 232 /** 233 * Returns the maximum value (latest date), or <CODE>null</CODE> if no limit is set. 234 * 235 * @return The latest date that can be selected. 236 */ 237 public LocalDate getMax() { 238 return max; 239 } 240 241 /** 242 * Sets the maximum value (latest date). Call this method with a <CODE>null</CODE> value for no 243 * limit. 244 * <p> 245 * This class does not attempt to verify that min <= value <= max. It is the responsibility of 246 * client code to supply a sane value. 247 * 248 * @param max The latest date that can be selected, or <CODE>null</CODE> for no limit. 249 */ 250 public void setMax(LocalDate max) { 251 this.max = max; 252 DptMonthView.setMax(max); 253 } 254 255 @Override 256 public void updateUI() { 257 super.updateUI(); 258 setUI(new MyFlatComboBoxUI()); 259 if (popupMenu != null) { 260 SwingUtilities.updateComponentTreeUI(popupMenu); 261 } 262 } 263 264 private class MyFlatComboBoxUI extends FlatComboBoxUI { 265 public MyFlatComboBoxUI() { 266 super(); 267 } 268 269 270 @Override 271 protected JButton createArrowButton() { 272 JButton button = new JButton(); 273 button.setOpaque(false); 274 button.setBorderPainted(false); 275 button.setContentAreaFilled(false); 276 button.setFocusPainted(false); 277 button.setBackground(new Color(1f, 1f, 1f, 0f)); 278 button.setIcon(new FlatSVGIcon("icons/rili.svg", 16, 16)); 279 return button; 280 } 281 } 282 }
1 /** 2 * @(#)MonthView.java 1.0 2015/02/18 3 */ 4 package com.example.dpt.component; 5 6 import com.example.dpt.component.swing.DptYearMonthSpinner; 7 8 import javax.swing.*; 9 import javax.swing.event.ChangeEvent; 10 import javax.swing.event.ListSelectionListener; 11 import javax.swing.table.*; 12 import java.awt.*; 13 import java.awt.event.*; 14 import java.awt.font.TextAttribute; 15 import java.time.DayOfWeek; 16 import java.time.LocalDate; 17 import java.time.YearMonth; 18 import java.time.format.DateTimeFormatter; 19 import java.time.format.FormatStyle; 20 import java.time.format.TextStyle; 21 import java.time.temporal.ChronoUnit; 22 import java.time.temporal.TemporalAdjusters; 23 import java.util.Locale; 24 import java.util.Map; 25 import java.util.function.Function; 26 27 /** 28 * @see LocalDate 29 */ 30 public class DptMonthView extends JPanel { 31 32 private static final DateTimeFormatter LABEL_FORMATTER 33 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); 34 private final JPanel north; 35 private TodayLinkLabel todayLinkLabel; 36 private volatile boolean programmaticChange; 37 private YearMonth yearMonth; 38 private LocalDate startDate; 39 private LocalDate value; 40 private LocalDate min; 41 private LocalDate max; 42 private final DptYearMonthSpinner yearMonthSpinner; 43 private final JTable table = new JTable(new MonthViewTableModel()); 44 45 /** 46 * Constructs a MonthView with the value set to the current date, with no upper or lower limits. 47 */ 48 public DptMonthView() { 49 this(LocalDate.now()); 50 } 51 52 /** 53 * Constructs a MonthView with the value set to the supplied date, with no lower or upper limits. 54 * 55 * @param initialDate The date to set 56 */ 57 public DptMonthView(LocalDate initialDate) { 58 this(initialDate, null, null); 59 } 60 61 /** 62 * Constructs a MonthView with the value set to the supplied date, with the supplied lower 63 * (earliest) and upper (latest) dates. 64 * <p> 65 * Dates outside the specified range are not displayed. 66 * <p> 67 * This class does not attempt to verify that minDate <= value <= maxDate. It is the 68 * responsibility of client code to supply sane values. 69 * 70 * @param initialDate The date to set 71 * @param minDate The minimum value (earliest date); <CODE>null</CODE> for no limit. 72 * @param maxDate The maximum value (latest date); <CODE>null</CODE> for no limit. 73 */ 74 public DptMonthView(LocalDate initialDate, LocalDate minDate, LocalDate maxDate) { 75 this(initialDate, minDate, maxDate, false); 76 } 77 78 /** 79 * Constructs a MonthView with the value set to the supplied date, with the supplied lower 80 * (earliest) and upper (latest) dates, and, optionally, a link to set the value to the current 81 * date. 82 * <p> 83 * Dates outside the specified range are not displayed. 84 * <p> 85 * This class does not attempt to verify that minDate <= value <= maxDate. It is the 86 * responsibility of client code to supply sane values. 87 * 88 * @param initialDate The date to set 89 * @param minDate The minimum value (earliest date); <CODE>null</CODE> for no limit. 90 * @param maxDate The maximum value (latest date); <CODE>null</CODE> for no limit. 91 * @param showtoday true to show a link to the current date, false otherwise 92 */ 93 public DptMonthView(LocalDate initialDate, LocalDate minDate, LocalDate maxDate, boolean showtoday) { 94 super(new BorderLayout(7, 0)); 95 min = minDate; 96 max = maxDate; 97 yearMonth = YearMonth.from(initialDate); 98 startDate = yearMonth.atDay(1).minusDays(yearMonth.atDay(1).getDayOfWeek().getValue()); 99 100 YearMonth firstMonth = null; 101 if (minDate != null) { 102 firstMonth = YearMonth.from(minDate); 103 } 104 YearMonth lastMonth = null; 105 if ((maxDate != null)) { 106 lastMonth = YearMonth.from(maxDate); 107 } 108 yearMonthSpinner = new DptYearMonthSpinner(yearMonth, firstMonth, lastMonth); 109 yearMonthSpinner.addChangeListener((ChangeEvent ce) -> { 110 if (!programmaticChange) { 111 setYearMonth((YearMonth) yearMonthSpinner.getValue()); 112 } 113 }); 114 115 setValue(initialDate); 116 117 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 118 table.setShowGrid(false); 119 // table.setBorder(BorderFactory.createLineBorder(table.getForeground())); 120 table.setCellSelectionEnabled(true); 121 table.setRowHeight(40); 122 table.setDefaultRenderer(LocalDate.class, new MonthViewTableCellRenderer()); 123 TableColumnModel columnModel = table.getColumnModel(); 124 for (int i = 0; i < columnModel.getColumnCount(); i++) { 125 columnModel.getColumn(i).setPreferredWidth(40); 126 } 127 JTableHeader header = table.getTableHeader(); 128 header.setBorder(table.getBorder()); 129 header.setReorderingAllowed(false); 130 header.setResizingAllowed(false); 131 header.setDefaultRenderer(new MonthViewTableHeaderCellRenderer(header.getDefaultRenderer())); 132 133 Timer selectionTimer = new Timer(50, ae -> { 134 LocalDate newValue = (LocalDate) table.getValueAt(table.getSelectedRow(), 135 table.getSelectedColumn()); 136 if (newValue.equals(value)) { 137 return; 138 } 139 boolean needSetTableSelection = false; 140 if (min != null && newValue.isBefore(min)) { 141 newValue = min; 142 needSetTableSelection = true; 143 } 144 if (max != null && newValue.isAfter(max)) { 145 newValue = max; 146 needSetTableSelection = true; 147 } 148 firePropertyChange("Value", value, newValue); 149 value = newValue; 150 if (needSetTableSelection) { 151 setTableSelection(); 152 } 153 setYearMonth(YearMonth.from(newValue)); 154 }); 155 selectionTimer.setRepeats(false); 156 157 ListSelectionListener selectionListener = lse -> { 158 if (!programmaticChange && !lse.getValueIsAdjusting()) { 159 if (selectionTimer.isRunning()) { 160 selectionTimer.restart(); 161 } else { 162 selectionTimer.start(); 163 } 164 } 165 }; 166 ListSelectionModel rowSelectionModel = table.getSelectionModel(); 167 rowSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 168 rowSelectionModel.addListSelectionListener(selectionListener); 169 170 ListSelectionModel columnSelectionModel = columnModel.getSelectionModel(); 171 columnSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 172 columnSelectionModel.addListSelectionListener(selectionListener); 173 table.addMouseWheelListener(new MouseAdapter() { 174 175 @Override 176 public void mouseWheelMoved(MouseWheelEvent mwe) { 177 if (mwe.getWheelRotation() < 0) { 178 yearMonthSpinner.setValue(yearMonthSpinner.getValue().plusMonths(1)); 179 } else { 180 yearMonthSpinner.setValue(yearMonthSpinner.getValue().minusMonths(1)); 181 } 182 } 183 }); 184 setTableSelection(); 185 186 changeTableAction(KeyEvent.VK_LEFT, ae -> { 187 if (min != null && value.equals(min)) { 188 return false; 189 } 190 if (table.getSelectedColumn() == 0) { 191 LocalDate newValue = value.minusDays(1); 192 DptMonthView.this.firePropertyChange("Value", value, newValue); 193 setValue(newValue); 194 return false; 195 } 196 return true; 197 }); 198 changeTableAction(KeyEvent.VK_UP, ae -> { 199 if (min != null && value.isBefore(min.plusDays(7))) { 200 DptMonthView.this.firePropertyChange("Value", value, min); 201 setValue(min); 202 return false; 203 } 204 if (table.getSelectedRow() == 0) { 205 LocalDate newValue = value.minusDays(7); 206 DptMonthView.this.firePropertyChange("Value", value, newValue); 207 setValue(newValue); 208 return false; 209 } 210 return true; 211 }); 212 changeTableAction(KeyEvent.VK_RIGHT, ae -> { 213 if (max != null && value.equals(max)) { 214 return false; 215 } 216 if (table.getSelectedColumn() == 6) { 217 LocalDate newValue = value.plusDays(1); 218 DptMonthView.this.firePropertyChange("Value", value, newValue); 219 setValue(newValue); 220 return false; 221 } 222 return true; 223 }); 224 changeTableAction(KeyEvent.VK_DOWN, ae -> { 225 if (max != null && value.isAfter(max.minusDays(7))) { 226 DptMonthView.this.firePropertyChange("Value", value, max); 227 setValue(max); 228 return false; 229 } 230 if (table.getSelectedRow() == 5) { 231 LocalDate newValue = value.plusDays(7); 232 DptMonthView.this.firePropertyChange("Value", value, newValue); 233 setValue(newValue); 234 return false; 235 } 236 return true; 237 }); 238 changeTableAction(KeyEvent.VK_ENTER, ae -> { 239 DptMonthView.this.firePropertyChange("Confirm", null, value); 240 return false; 241 }); 242 table.addMouseListener(new MouseAdapter() { 243 244 @Override 245 public void mouseReleased(MouseEvent me) { 246 int selectedRow = table.getSelectedRow(); 247 int selectedColumn = table.getSelectedColumn(); 248 Object valueAt = table.getValueAt(selectedRow, selectedColumn); 249 if (DptMonthView.this.value == null && valueAt != null) { 250 LocalDate parseDate = LocalDate.parse(valueAt.toString()); 251 DptMonthView.this.value = parseDate; 252 setValue(parseDate); 253 } 254 DptMonthView.this.firePropertyChange("Confirm", null, DptMonthView.this.value); 255 DptMonthView.this.firePropertyChange("Value", null, DptMonthView.this.value); 256 } 257 }); 258 259 setFocusable(true); 260 261 north = new JPanel(new BorderLayout()); 262 north.setPreferredSize(new Dimension(0, 45)); 263 north.add(yearMonthSpinner, BorderLayout.CENTER); 264 north.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, UIManager.getColor("ColorPalette.border"))); 265 266 Box tableBox = Box.createVerticalBox(); 267 tableBox.add(header); 268 tableBox.add(table); 269 add(north, BorderLayout.NORTH); 270 add(tableBox, BorderLayout.CENTER); 271 todayLinkLabel = getTodayLinkLabel(); 272 if (showtoday) { 273 add(todayLinkLabel, BorderLayout.SOUTH); 274 } 275 276 } 277 278 @Override 279 public void updateUI() { 280 super.updateUI(); 281 282 if (north != null) { 283 north.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, UIManager.getColor("ColorPalette.border"))); 284 } 285 } 286 287 private void changeTableAction(int key, Function<ActionEvent, Boolean> function) { 288 InputMap ancestorMap = table.getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 289 InputMap windowMap = table.getInputMap(WHEN_IN_FOCUSED_WINDOW); 290 ActionMap actionMap = table.getActionMap(); 291 KeyStroke keyStroke = KeyStroke.getKeyStroke(key, 0); 292 Object actionKey = ancestorMap.get(keyStroke); 293 windowMap.put(keyStroke, actionKey); 294 295 Action oldAction = actionMap.get(actionKey); 296 Action replacement = new AbstractAction() { 297 298 @Override 299 public void actionPerformed(ActionEvent ae) { 300 if (function.apply(ae)) { 301 oldAction.actionPerformed(ae); 302 } 303 } 304 }; 305 actionMap.put(actionKey, replacement); 306 } 307 308 private void setYearMonth(YearMonth yearMonth) { 309 if (min != null && yearMonth.atEndOfMonth().isBefore(min)) { 310 yearMonth = YearMonth.from(min); 311 } 312 if (max != null && yearMonth.atDay(1).isAfter(max)) { 313 yearMonth = YearMonth.from(max); 314 } 315 if (yearMonth.equals(this.yearMonth)) { 316 //return; // no, we still have to setTableSelection 317 } 318 319 if (!yearMonth.equals(yearMonthSpinner.getValue())) { 320 programmaticChange = true; 321 yearMonthSpinner.setValue(yearMonth); 322 programmaticChange = false; 323 } 324 this.yearMonth = yearMonth; 325 startDate = yearMonth.atDay(1).with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)); 326 setTableSelection(); 327 repaint(); 328 } 329 330 /** 331 * Returns the current value 332 * 333 * @return the current value 334 */ 335 public LocalDate getValue() { 336 return value; 337 } 338 339 /** 340 * Sets the current value, adjusted to be within any specified min/max range. 341 * 342 * @param value The value to set 343 */ 344 public void setValue(LocalDate value) { 345 if (min != null && value.isBefore(min)) { 346 value = min; 347 } 348 if (max != null && value.isAfter(max)) { 349 value = max; 350 } 351 352 this.value = value; 353 if (value == null) { 354 return; 355 } 356 setYearMonth(YearMonth.from(value)); 357 getTodayLinkLabel().setText("今天: " + LocalDate.now().format(LABEL_FORMATTER)); 358 } 359 360 private void setTableSelection() { 361 programmaticChange = true; 362 if (value == null || value.isBefore(startDate) 363 || value.isAfter(startDate.plusDays(41))) { 364 table.clearSelection(); 365 } else { 366 int days = (int) ChronoUnit.DAYS.between(startDate, value); 367 table.setRowSelectionInterval(days / 7, days / 7); 368 table.setColumnSelectionInterval(days % 7, days % 7); 369 } 370 programmaticChange = false; 371 } 372 373 /** 374 * Returns the minimum value (earliest date), or <CODE>null</CODE> if no limit is set. 375 * 376 * @return The earliest date that can be selected. 377 */ 378 public LocalDate getMin() { 379 return min; 380 } 381 382 /** 383 * Sets the minimum value (earliest date). Call this method with a <CODE>null</CODE> value for no 384 * limit. 385 * <p> 386 * This method does not attempt to verify that min <= value <= max. It is the responsibility of 387 * client code to supply a sane value. 388 * 389 * @param min The earliest date that can be selected, or <CODE>null</CODE> for no limit. 390 */ 391 public void setMin(LocalDate min) { 392 this.min = min; 393 } 394 395 /** 396 * Returns the maximum value (latest date), or <CODE>null</CODE> if no limit is set. 397 * 398 * @return The latest date that can be selected. 399 */ 400 public LocalDate getMax() { 401 return max; 402 } 403 404 /** 405 * Sets the maximum value (latest date). Call this method with a <CODE>null</CODE> value for no 406 * limit. 407 * <p> 408 * This method does not attempt to verify that min <= value <= max. It is the responsibility of 409 * client code to supply a sane value. 410 * 411 * @param max The latest date that can be selected, or <CODE>null</CODE> for no limit. 412 */ 413 public void setMax(LocalDate max) { 414 this.max = max; 415 } 416 417 private class MonthViewTableModel<T extends LocalDate> extends AbstractTableModel { 418 419 @Override 420 public Class<?> getColumnClass(int column) { 421 return LocalDate.class; 422 } 423 424 @Override 425 public String getColumnName(int column) { 426 String week = DayOfWeek.of((column + 6) % 7 + 1) 427 .getDisplayName(TextStyle.SHORT_STANDALONE, Locale.getDefault()); 428 return week.substring(2); 429 } 430 431 @Override 432 public int getRowCount() { 433 return 6; 434 } 435 436 @Override 437 public int getColumnCount() { 438 return 7; 439 } 440 441 @Override 442 public Object getValueAt(int row, int column) { 443 return startDate.plusDays(row * 7 + column); 444 } 445 } 446 447 private class MonthViewTableHeaderCellRenderer implements TableCellRenderer { 448 449 private final TableCellRenderer defaultRenderer; 450 451 private MonthViewTableHeaderCellRenderer(TableCellRenderer defaultRenderer) { 452 this.defaultRenderer = defaultRenderer; 453 454 } 455 456 @Override 457 public Component getTableCellRendererComponent(JTable table, Object value, 458 boolean isSelected, boolean hasFocus, int row, int column) { 459 JComponent c = (JComponent) defaultRenderer.getTableCellRendererComponent(table, value, 460 hasFocus, hasFocus, row, row); 461 c.setPreferredSize(new Dimension(40, 40)); 462 c.setBorder(BorderFactory.createEmptyBorder()); 463 c.setForeground(column == 0 ? Colors.normalSunday 464 : column == 6 ? Colors.normalSaturday 465 : getForeground()); 466 return c; 467 } 468 469 } 470 471 472 private class MonthViewTableCellRenderer extends DefaultTableCellRenderer { 473 474 private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd"); 475 476 private MonthViewTableCellRenderer() { 477 setHorizontalAlignment(CENTER); 478 479 } 480 481 @Override 482 public Component getTableCellRendererComponent(JTable table, Object value, 483 boolean isSelected, boolean hasFocus, int row, int column) { 484 485 if (value == null) { 486 return this; 487 } 488 LocalDate dateValue = (LocalDate) value; 489 490 boolean isSunday = dateValue.getDayOfWeek() == DayOfWeek.SUNDAY; 491 boolean isSaturday = dateValue.getDayOfWeek() == DayOfWeek.SATURDAY; 492 if (dateValue.getMonth() == yearMonth.getMonth()) { 493 setForeground(isSunday ? Colors.normalSunday : isSaturday ? Colors.normalSaturday : table.getForeground()); 494 } else { 495 Color fadedc = UIManager.getColor("ColorPalette.dis") == null ? new Color(0x8b8b8c) : UIManager.getColor("ColorPalette.dis"); 496 setForeground(isSunday ? Colors.fadedSunday : isSaturday ? Colors.fadedSaturday : fadedc); 497 } 498 hasFocus = true; 499 if (min != null && dateValue.isBefore(min) 500 || max != null && dateValue.isAfter(max)) { 501 setForeground(Colors.blank); 502 isSelected = false; 503 hasFocus = false; 504 } 505 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 506 setText(formatter.format(dateValue)); 507 setBorder(BorderFactory.createEmptyBorder()); 508 return this; 509 } 510 } 511 512 public TodayLinkLabel getTodayLinkLabel() { 513 if (todayLinkLabel == null) { 514 todayLinkLabel = new TodayLinkLabel(); 515 } 516 return todayLinkLabel; 517 } 518 519 private static class Colors { 520 521 public static final Color faded = UIManager.getColor("ColorPalette.dis") == null ? new Color(0x8b8b8c) : UIManager.getColor("ColorPalette.dis"); 522 public static final Color normalSunday = new Color(0xF56C6C); 523 public static final Color fadedSunday = new Color(0xfbc4c4); 524 public static final Color normalSaturday = new Color(0x67C23A); 525 public static final Color fadedSaturday = new Color(0xc2e7b0); 526 public static final Color blank = new Color(0x8b8b8c); 527 528 529 } 530 531 private class TodayLinkLabel extends JLabel { 532 533 private final Font normalFont; 534 private final Font underlinedFont; 535 536 private TodayLinkLabel() { 537 super("今天: " + LocalDate.now().format(LABEL_FORMATTER)); 538 setHorizontalAlignment(CENTER); 539 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 540 541 normalFont = getFont(); 542 Map attributes = normalFont.getAttributes(); 543 attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); 544 underlinedFont = normalFont.deriveFont(attributes); 545 // setPreferredSize(new Dimension(0, 40)); 546 addMouseListener(new MouseAdapter() { 547 548 @Override 549 public void mouseEntered(MouseEvent e) { 550 setFont(underlinedFont); 551 } 552 553 @Override 554 public void mouseExited(MouseEvent e) { 555 setFont(normalFont); 556 } 557 558 @Override 559 public void mouseClicked(MouseEvent me) { 560 LocalDate newValue = LocalDate.now(); 561 DptMonthView.this.firePropertyChange("Value", value, newValue); 562 setValue(newValue); 563 // MonthView.this.firePropertyChange("Confirm", null, newValue); 564 } 565 }); 566 } 567 } 568 }
1 package com.example.dpt.component.swing; 2 3 import javax.swing.*; 4 import javax.swing.event.ChangeListener; 5 import java.awt.*; 6 import java.time.YearMonth; 7 import java.time.format.DateTimeFormatter; 8 import java.time.temporal.ChronoUnit; 9 10 /** 11 * YearMonthSpinner is a composite of two spinners, one for the month and the other for the year, 12 * used to select a java.time.MonthYear. 13 * <P> 14 * <B>Note that compiling or using this class requires Java 8</B> 15 * 16 * @see YearMonth 17 * 18 */ 19 public class DptYearMonthSpinner extends JPanel { 20 21 private boolean programmaticChange; 22 private SpinnerTemporalModel<YearMonth> monthModel; 23 private SpinnerTemporalModel<YearMonth> yearModel; 24 private JSpinner monthSpinner; 25 private JSpinner yearSpinner; 26 27 /** 28 * Constructs a YearMonthSpinner with the current year and month, with no lower or upper limits. 29 * 30 */ 31 public DptYearMonthSpinner() { 32 this(YearMonth.now()); 33 } 34 35 /** 36 * Constructs a YearMonthSpinner with the supplied year and month, with no lower or upper limits. 37 * 38 * @param value the initial value 39 */ 40 public DptYearMonthSpinner(YearMonth value) { 41 this(value, null, null); 42 } 43 44 /** 45 * Constructs a YearMonthSpinner with the supplied year and month, lower (earliest) and upper 46 * (latest) dates. 47 * 48 * @param value the initial value 49 * @param min the minimum (earliest) value 50 * @param max the maximum (latest) value 51 */ 52 public DptYearMonthSpinner(YearMonth value, YearMonth min, YearMonth max) { 53 super(new FlowLayout(FlowLayout.CENTER, 7, 7)); 54 55 if (value == null) { 56 value = YearMonth.now(); 57 } 58 monthModel = new SpinnerTemporalModel<>(value, min, max, ChronoUnit.MONTHS); 59 monthSpinner = new JSpinner(monthModel) { 60 @Override 61 public ComponentOrientation getComponentOrientation() { 62 return ComponentOrientation.LEFT_TO_RIGHT; 63 } 64 }; 65 SpinnerTemporalEditor monthEditor = new SpinnerTemporalEditor(monthSpinner, 66 DateTimeFormatter.ofPattern("M 月")); 67 monthEditor.getTextField().setColumns(5); 68 monthSpinner.setEditor(monthEditor); 69 monthEditor.getTextField().setHorizontalAlignment(JTextField.CENTER); 70 monthEditor.getTextField().setFont( monthEditor.getTextField().getFont().deriveFont(18f)); 71 yearModel = new SpinnerTemporalModel<>(value, min, max, ChronoUnit.YEARS); 72 yearSpinner = new JSpinner(yearModel) { 73 @Override 74 public ComponentOrientation getComponentOrientation() { 75 return ComponentOrientation.LEFT_TO_RIGHT; 76 } 77 }; 78 SpinnerTemporalEditor yearEditor 79 = new SpinnerTemporalEditor(yearSpinner, DateTimeFormatter.ofPattern("yyyy 年")); 80 yearEditor.getTextField().setColumns(4); 81 yearEditor.getTextField().setHorizontalAlignment(JTextField.CENTER); 82 yearSpinner.setEditor(yearEditor); 83 84 yearEditor.getTextField().setFont( yearEditor.getTextField().getFont().deriveFont(18f)); 85 86 monthSpinner.addChangeListener(ce -> { 87 if (!programmaticChange) { 88 programmaticChange = true; 89 yearSpinner.setValue(monthSpinner.getValue());//value); 90 programmaticChange = false; 91 } 92 }); 93 yearSpinner.addChangeListener(ce -> { 94 if (!programmaticChange) { 95 programmaticChange = true; 96 monthSpinner.setValue(yearSpinner.getValue());//value); 97 programmaticChange = false; 98 } 99 }); 100 101 add(monthSpinner); 102 add(yearSpinner); 103 } 104 105 /** 106 * Adds a listener that is notified each time a change occurs. The source of the 107 * <code>ChangeEvent</code> will be the contained month spinner. 108 * 109 * @param listener the <code>ChangeListeners</code> to add 110 * @see #removeChangeListener 111 */ 112 public void addChangeListener(ChangeListener listener) { 113 monthSpinner.addChangeListener(listener); 114 } 115 116 /** 117 * Removes a <code>ChangeListener</code>. 118 * 119 * @param listener the <code>ChangeListener</code> to remove 120 */ 121 public void removeChangeListener(ChangeListener listener) { 122 monthSpinner.removeChangeListener(listener); 123 } 124 125 /** 126 * {@inheritDoc } 127 */ 128 @Override 129 public void setEnabled(boolean enabled) { 130 super.setEnabled(enabled); 131 monthSpinner.setEnabled(enabled); 132 yearSpinner.setEnabled(enabled); 133 } 134 135 /** 136 * Returns the current value 137 * 138 * @return the current value 139 */ 140 public YearMonth getValue() { 141 return monthModel.getTemporalValue(); 142 } 143 144 /** 145 * Sets the current value 146 * <P> 147 * This class does not attempt to verify that min <= value <= max. It is the responsibility of 148 * client code to supply a sane value. 149 * 150 * @param value The value to set 151 */ 152 public void setValue(YearMonth value) { 153 programmaticChange = true; 154 monthSpinner.setValue(value); 155 yearSpinner.setValue(value); 156 programmaticChange = false; 157 } 158 159 /** 160 * Returns the minimum value (earliest year/month), or <CODE>null</CODE> if no limit is set. 161 * 162 * @return The earliest year/month that can be selected. 163 */ 164 public YearMonth getMin() { 165 return monthModel.getMin(); 166 } 167 168 /** 169 * Sets the minimum value (earliest year/month). Call this method with a <CODE>null</CODE> value 170 * for no limit. 171 * <P> 172 * This class does not attempt to verify that min <= value <= max. It is the responsibility of 173 * client code to supply a sane value. 174 * 175 * @param min The earliest year/month that can be selected, or <CODE>null</CODE> for no limit. 176 */ 177 public void setMin(YearMonth min) { 178 monthModel.setMin(min); 179 yearModel.setMin(min); 180 } 181 182 /** 183 * Returns the maximum value (latest year/month), or <CODE>null</CODE> if no limit is set. 184 * 185 * @return The latest year/month that can be selected. 186 */ 187 public YearMonth getMax() { 188 return monthModel.getMax(); 189 } 190 191 /** 192 * Sets the maximum value (latest year/month). Call this method with a <CODE>null</CODE> value for 193 * no limit. 194 * <P> 195 * This class does not attempt to verify that min <= value <= max. It is the responsibility of 196 * client code to supply a sane value. 197 * 198 * @param max The latest year/month that can be selected, or <CODE>null</CODE> for no limit. 199 */ 200 public void setMax(YearMonth max) { 201 monthModel.setMax(max); 202 yearModel.setMax(max); 203 } 204 205 @Override 206 public void updateUI() { 207 super.updateUI(); 208 if (yearSpinner != null) { 209 yearSpinner.updateUI(); 210 monthSpinner.updateUI(); 211 } 212 } 213 }
标签:null,return,min,value,日期,组件,javaSwing,LocalDate,public From: https://www.cnblogs.com/haocp/p/17898495.html