Swing/CalendarViewTable のバックアップの現在との差分(No.5)
- category: swing
folder: CalendarViewTable
title: JTableにLocaleを考慮したLocalDateを適用してカレンダーを表示する
tags: [JTable, LocalDate, Locale, Calendar]
author: aterai
pubdate: 2018-01-29T14:57:32+09:00
description: JTableに週の最初の曜日がLocaleに応じて変化するカレンダーを表示します。
image: https://drive.google.com/uc?export=view&id=1jXZtiYFaA5ABWsdaRBnPUKqS2_VBDkFqQA
image: https://drive.google.com/uc?id=1jXZtiYFaA5ABWsdaRBnPUKqS2_VBDkFqQA
hreflang:
href: https://java-swing-tips.blogspot.com/2018/01/apply-localdate-considering-locale-to.html lang: en
概要
JTable
に週の最初の曜日がLocale
に応じて変化するカレンダーを表示します。
Screenshot
Advertisement
Screenshot
Advertisement
サンプルコード
#spandel
class CalendarViewTableModel<T extends LocalDate> extends DefaultTableModel {
#spanend
#spanadd
class CalendarViewTableModel extends DefaultTableModel {
#spanend
private final LocalDate startDate;
private final WeekFields weekFields = WeekFields.of(Locale.getDefault());
public CalendarViewTableModel(T date) {
#spanadd
#spanend
public CalendarViewTableModel(LocalDate date) {
super();
LocalDate firstDayOfMonth = YearMonth.from(date).atDay(1);
int dowv = firstDayOfMonth.get(weekFields.dayOfWeek()) - 1;
startDate = firstDayOfMonth.minusDays(dowv);
}
#spanadd
#spanend
@Override public Class<?> getColumnClass(int column) {
return LocalDate.class;
}
#spanadd
#spanend
@Override public String getColumnName(int column) {
return weekFields.getFirstDayOfWeek().plus(column)
.getDisplayName(TextStyle.SHORT_STANDALONE, Locale.getDefault());
}
#spanadd
#spanend
@Override public int getRowCount() {
return 6;
}
#spanadd
#spanend
@Override public int getColumnCount() {
return 7;
}
#spanadd
#spanend
@Override public Object getValueAt(int row, int column) {
return startDate.plusDays(row * getColumnCount() + column);
}
#spanadd
#spanend
@Override public boolean isCellEditable(int row, int column) {
return false;
}
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、Java 8
からjava.time
パッケージに追加されたLocalDate
をJTable
のモデルとして使用し、月のカレンダーを表示しています。
- WeekFields#getFirstDayOfWeek()メソッドで
Locale
に応じた週の最初の曜日を取得して、JTable
の0
列目を設定- 例: フランスと
ISO-8601
標準では月曜日が週の最初の曜日になる-
java -Duser.language=fr -jar example.jar
-
- 例: フランスと
- WeekFields#getFirstDayOfWeek()メソッドで
Locale
に応じた週の最初の曜日を取得してJTable
の0
列目を設定- 例: 週の最初の曜日はロケールがフランスや
ISO-8601
標準の場合は月曜日、アラビアなら土曜日になる
- 例: 週の最初の曜日はロケールがフランスや
java -Duser.language=fr -jar example.jar
java -Duser.language=ar -jar example.jar
- 土曜と日曜の位置も
6
列目と0
列目固定ではないので、以下のようにLocalDate.getDayOfWeek()
メソッドで曜日を確認してその背景色を変更する必要がある
private class CalendarTableRenderer extends DefaultTableCellRenderer {
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean selected, boolean focused,
int row, int column) {
super.getTableCellRendererComponent(table, value, selected, focused, row, column);
setHorizontalAlignment(SwingConstants.CENTER);
if (value instanceof LocalDate) {
LocalDate d = (LocalDate) value;
setText(String.valueOf(d.getDayOfMonth()));
if (YearMonth.from(d).equals(YearMonth.from(currentLocalDate))) {
setForeground(Color.BLACK);
} else {
setForeground(Color.GRAY);
}
DayOfWeek dow = d.getDayOfWeek();
if (d.isEqual(realLocalDate)) {
setBackground(new Color(220, 255, 220));
} else if (dow == DayOfWeek.SUNDAY) {
setBackground(new Color(255, 220, 220));
} else if (dow == DayOfWeek.SATURDAY) {
setBackground(new Color(220, 220, 255));
} else {
setBackground(Color.WHITE);
}
}
return this;
}
}
- -
-
JTable
ではなく、JList
を使用すると週を跨いだ日付の範囲選択(上下キー)が可能
#spanend
#spandel
import java.awt.*;
#spanend
#spandel
import java.awt.event.*;
#spanend
#spandel
import java.time.*;
#spanend
#spandel
import java.time.format.*;
#spanend
#spandel
import java.time.temporal.*;
#spanend
#spandel
import java.util.*;
#spanend
#spandel
import javax.swing.*;
#spanend
#spandel
#spanend
#spandel
public class CalendarViewListTest {
#spanend
private final JLabel monthLabel = new JLabel("", SwingConstants.CENTER);
private final JList<LocalDate> monthList = new JList<>();
private final LocalDate realLocalDate = LocalDate.now();
private LocalDate currentLocalDate;
#spandel
#spanend
public JComponent makeUI() {
Dimension size = new Dimension(40, 20);
#spandel
#spanend
monthList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
monthList.setVisibleRowCount(6);
monthList.setFixedCellWidth(size.width);
monthList.setFixedCellHeight(size.height);
monthList.setCellRenderer(new CalendarListRenderer<LocalDate>());
monthList.getSelectionModel().setSelectionMode(
ListSelectionModel.SINGLE_INTERVAL_SELECTION);
#spandel
#spanend
InputMap imap = monthList.getInputMap(JComponent.WHEN_FOCUSED);
imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "selectNextIndex");
imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "selectPreviousIndex");
#spandel
#spanend
monthList.getActionMap().put("selectPreviousIndex", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
int index = monthList.getLeadSelectionIndex();
if (index >= 0 && index < monthList.getModel().getSize()) {
monthList.setSelectedIndex(index - 1);
}
}
});
monthList.getActionMap().put("selectNextIndex", new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
int index = monthList.getLeadSelectionIndex();
if (index >= 0 && index < monthList.getModel().getSize()) {
monthList.setSelectedIndex(index + 1);
}
}
});
#spandel
#spanend
Locale l = Locale.getDefault();
DefaultListModel<DayOfWeek> weekModel = new DefaultListModel<>();
DayOfWeek firstDayOfWeek = WeekFields.of(l).getFirstDayOfWeek();
for (int i = 0; i < 7; i++) {
weekModel.add(i, firstDayOfWeek.plus(i));
}
JList<DayOfWeek> header = new JList<>(weekModel);
header.setLayoutOrientation(JList.HORIZONTAL_WRAP);
header.setVisibleRowCount(0);
header.setFixedCellWidth(size.width);
header.setFixedCellHeight(size.height);
header.setCellRenderer(new DefaultListCellRenderer() {
@Override public Component getListCellRendererComponent(
JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, false, false);
setHorizontalAlignment(SwingConstants.CENTER);
if (value instanceof DayOfWeek) {
DayOfWeek dow = (DayOfWeek) value;
String s = dow.getDisplayName(TextStyle.SHORT_STANDALONE, l);
setText(s.substring(0, Math.min(2, s.length())));
setBackground(new Color(220, 220, 220));
}
return this;
}
});
updateMonthView(realLocalDate);
#spandel
#spanend
JButton prev = new JButton("<");
prev.addActionListener(e -> updateMonthView(currentLocalDate.minusMonths(1)));
#spandel
#spanend
JButton next = new JButton(">");
next.addActionListener(e -> updateMonthView(currentLocalDate.plusMonths(1)));
#spandel
#spanend
JPanel monthPanel = new JPanel(new BorderLayout());
monthPanel.add(monthLabel);
monthPanel.add(prev, BorderLayout.WEST);
monthPanel.add(next, BorderLayout.EAST);
#spandel
#spanend
JScrollPane scroll = new JScrollPane(monthList);
scroll.setColumnHeaderView(header);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
#spandel
#spanend
Box box = Box.createVerticalBox();
box.add(monthPanel);
box.add(Box.createVerticalStrut(2));
box.add(scroll);
#spandel
#spanend
JPanel p = new JPanel();
p.add(box);
return p;
}
private void updateMonthView(LocalDate localDate) {
currentLocalDate = localDate;
monthLabel.setText(localDate.format(DateTimeFormatter.ofPattern("yyyy / MM")
.withLocale(Locale.getDefault())));
WeekFields weekFields = WeekFields.of(Locale.getDefault());
LocalDate firstDayOfMonth = localDate.with(TemporalAdjusters.firstDayOfMonth());
int dowv = firstDayOfMonth.get(weekFields.dayOfWeek()) - 1;
int vlen = 7 * 6;
LocalDate date = firstDayOfMonth.minusDays(dowv);
DefaultListModel<LocalDate> calendarModel = new DefaultListModel<>();
for (int i = 0; i < vlen; i++) {
calendarModel.add(i, date.plusDays(i));
}
monthList.setModel(calendarModel);
}
class CalendarListRenderer<E extends LocalDate> implements ListCellRenderer<E> {
private final JLabel renderer = new JLabel("", SwingConstants.CENTER);
@Override public Component getListCellRendererComponent(
JList<? extends E> list, E value, int index,
boolean isSelected, boolean cellHasFocus) {
renderer.setOpaque(true);
if (value instanceof LocalDate) {
LocalDate d = (LocalDate) value;
renderer.setText(String.valueOf(d.getDayOfMonth()));
if (YearMonth.from(d).equals(YearMonth.from(currentLocalDate))) {
renderer.setForeground(Color.BLACK);
} else {
renderer.setForeground(Color.GRAY);
}
DayOfWeek dow = d.getDayOfWeek();
if (d.isEqual(realLocalDate)) {
renderer.setBackground(new Color(220, 255, 220));
} else if (dow == DayOfWeek.SUNDAY) {
renderer.setBackground(new Color(255, 220, 220));
} else if (dow == DayOfWeek.SATURDAY) {
renderer.setBackground(new Color(220, 220, 255));
} else {
renderer.setBackground(Color.WHITE);
}
if (isSelected) {
renderer.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY));
} else {
renderer.setBorder(BorderFactory.createEmptyBorder(0, 0, 1, 0));
}
}
return renderer;
}
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new CalendarViewListTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
#spandel
}
#spanend
#spandel
参考リンク
- WeekFields#getFirstDayOfWeek() (Java Platform SE 8)
- Local Date Combo « Java Tips Weblog
- JListで月のカーソルキー移動や、週を跨いた日付を範囲選択が可能なカレンダーを作成する
-
JTable
ではなく、JList
を使用すると上下カーソルキーで週を跨いだ日付の範囲選択が可能
-