概要

JListを使用してカーソルキーで次の週や月に移動したり、週を跨いだLocalDate日付の範囲選択が可能なカレンダーを作成します。

サンプルコード

public final LocalDate realLocalDate = LocalDate.now();
public LocalDate currentLocalDate;
public final Dimension size = new Dimension(40, 26);

//...
JLabel yearMonthLabel = new JLabel("", SwingConstants.CENTER);
JList<LocalDate> monthList = new JList<LocalDate>(new CalendarViewListModel(realLocalDate)) {
  @Override public void updateUI() {
    setCellRenderer(null);
    super.updateUI();
    setLayoutOrientation(JList.HORIZONTAL_WRAP);
    setVisibleRowCount(CalendarViewListModel.ROW_COUNT); // ensure 6 rows in the list
    setFixedCellWidth(size.width);
    setFixedCellHeight(size.height);
    setCellRenderer(new CalendarListRenderer<>());
    getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
  }
};

//...
class CalendarViewListModel extends AbstractListModel<LocalDate> {
  public static final int ROW_COUNT = 6;
  private final LocalDate startDate;
  private final WeekFields weekFields = WeekFields.of(Locale.getDefault());
  protected CalendarViewListModel(LocalDate date) {
    super();
    LocalDate firstDayOfMonth = YearMonth.from(date).atDay(1);
    int dowv = firstDayOfMonth.get(weekFields.dayOfWeek()) - 1;
    startDate = firstDayOfMonth.minusDays(dowv);
  }
  @Override public int getSize() {
    return DayOfWeek.values().length * ROW_COUNT;
  }
  @Override public LocalDate getElementAt(int index) {
    return startDate.plusDays(index);
  }
}
view all

解説

カレンダーの週や日付モデルは、JTableにLocaleを考慮したLocalDateを適用してカレンダーを表示するとほぼ同じですが、上記のサンプルでは表示にJTableではなくJListを使用しているため、左右カーソルキーでの週を跨いだ日付移動などが可能になっています。

  • JList#setLayoutOrientation(JList.HORIZONTAL_WRAP)を設定し、セルが水平方向の次に垂直方向の順で並ぶレイアウトを使用
  • セルサイズは、JList#setFixedCellWidth(int)JList#setFixedCellHeight(int)メソッドで固定サイズを使用
  • 折り返しセル数は、JList#setVisibleRowCount(int)メソッドを使用して表示可能行数を設定することで指定
    • HORIZONTAL_WRAPを指定したJListの場合、列数を指定した折り返しはできないため、76行の固定サイズのカレンダーでJList#setVisibleRowCount(6)を設定することで常に7日分の列を表示する
  • HORIZONTAL_WRAPを指定したJListの場合、例えば右カーソルキーで土曜から日曜に移動できないため、デフォルトのselectNextColumnアクションではなく、以下のようなアクションを使用するようにマッピングを変更
    • 現在の月内ならHORIZONTAL_WRAPを無視して、単純に次の日付に選択状態を移動
    • 月表示を変更する必要がある場合は、移動先の月で次の日付に選択状態を移動
      monthList.getActionMap().put("selectNextIndex", new AbstractAction() {
        @Override public void actionPerformed(ActionEvent e) {
          int index = monthList.getLeadSelectionIndex();
          if (index < monthList.getModel().getSize() - 1) {
            monthList.setSelectedIndex(index + 1);
          } else {
            LocalDate d = monthList.getModel()
              .getElementAt(monthList.getModel().getSize() - 1)
              .plusDays(1);
            updateMonthView(currentLocalDate.plusMonths(1));
            monthList.setSelectedValue(d, false);
          }
        }
      });
      

参考リンク

コメント