I'm programming a calendar for only one year in NetBeans and for the GUI I plan to have the typical calendar a spinner for the year. This combination will determine in which day of the week does de first and last day of the month goes.
The problem I have with the code I made is with using the Calendar day button's names as variables. Maybe there is a way to prepare a statement for NetBeans to read as the buttons name?
For this calendar, I made a JPanel with 49 buttons (to have 7 rows of weeks that is the maximum a calendar has). I tried to name every button "N(number)" (the number being from 1 to 49). This is so I can make a for function that makes so that the position of the day after the last day of a month will be the position for the first day of the following month.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@SuppressWarnings("serial")
public class ButtonFoo01 extends JPanel {
private static final String[] MONTHS = {
"January", "February", "March", "April", "May",
"June", "July", "August", "September", "October",
"November", "December" };
public ButtonFoo01() {
int gap = 3;
setLayout(new GridLayout(0, 4, gap, gap));
setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
for (String month : MONTHS) {
JButton button = new JButton(month);
button.addActionListener(e -> monthButtonListener(e));
add(button);
}
}
private void monthButtonListener(ActionEvent e) {
System.out.println("Button text: " e.getActionCommand());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
ButtonFoo01 mainPanel = new ButtonFoo01();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
You also can see another example of mine that uses a 2-dimensional array of Strings to help create a GUI calculator,
in my answer here:
Java JComponent paint --Almost working
But...
Having answered the direct question, one also has to consider the overall problem, that of creating an accurate calendar representation using a Swing GUI, a problem that involves calendar logic that is well-suited to use of the relatively new DateTime Java library. This library makes it fairly easy to create a combo box for months since one can use the library's Month enum as the data nucleus of the JComboBox:
private JComboBox<Month> monthCombo = new JComboBox<>(Month.values());
The year could be represented by a JSpinner, one that uses a SpinnerNumberModel that has been set to the desired starting and ending year:
private JSpinner yearSpinner = new JSpinner(new SpinnerNumberModel(2022, 1900, 3000, 1));
I would also change the spinner's editor so that it doesn't display a thousands comma delimiter:
JSpinner.NumberEditor editor = new JSpinner.NumberEditor(yearSpinner, "#");
yearSpinner.setEditor(editor);
Also, we can add the same listener to the JComboBox as to the JSpinner, one that takes both values and finds the correct month for the selected year:
yearSpinner.addChangeListener(ce -> dateChanged());
monthCombo.addItemListener(ie -> dateChanged());
with the dateChanged()
method:
private void dateChanged() {
int year = (int) yearSpinner.getValue();
Month month = (Month) monthCombo.getSelectedItem();
LocalDate date = LocalDate.of(year, month, 1);
// this notifies the JPanel that displays the dates of the selected month and year
daysOfMonthPanel.setDate(date);
}
An example program could look something like:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.util.Locale;
import javax.swing.*;
@SuppressWarnings("serial")
public class CalendarGui extends JPanel {
public static final String LOCAL_DATE = "local date";
private JSpinner yearSpinner = new JSpinner(new SpinnerNumberModel(2022, 1900, 3000, 1));
private JComboBox<Month> monthCombo = new JComboBox<>(Month.values());
private DaysOfTheMonthPanel daysOfMonthPanel = new DaysOfTheMonthPanel();
public CalendarGui() {
JSpinner.NumberEditor editor = new JSpinner.NumberEditor(yearSpinner, "#");
yearSpinner.setEditor(editor);
yearSpinner.addChangeListener(ce -> dateChanged());
monthCombo.addItemListener(ie -> dateChanged());
monthCombo.setRenderer(new MonthComboEditor());
yearSpinner.setMaximumSize(yearSpinner.getPreferredSize());
monthCombo.setMaximumSize(monthCombo.getPreferredSize());
JPanel topPanel = new JPanel();
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.LINE_AXIS));
topPanel.add(yearSpinner);
topPanel.add(Box.createHorizontalGlue());
topPanel.add(monthCombo);
JPanel middlePanel = new JPanel(new BorderLayout());
middlePanel.add(new DaysOfTheWeekPanel(), BorderLayout.PAGE_START);
middlePanel.add(daysOfMonthPanel);
int gap = 3;
setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
setLayout(new BorderLayout(gap, gap));
add(topPanel, BorderLayout.PAGE_START);
add(middlePanel);
LocalDate now = LocalDate.now();
yearSpinner.setValue(now.getYear());
monthCombo.setSelectedItem(now.getMonth());
}
private void dateChanged() {
int year = (int) yearSpinner.getValue();
Month month = (Month) monthCombo.getSelectedItem();
LocalDate date = LocalDate.of(year, month, 1);
// this notifies the JPanel that displays the dates of the selected month and year
daysOfMonthPanel.setDate(date);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
CalendarGui mainPanel = new CalendarGui();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
private static class MonthComboEditor extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
String newValue = (value == null) ? "" : ((Month) value).getDisplayName(TextStyle.FULL, Locale.US);
return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
}
}
private static class DaysOfTheWeekPanel extends JPanel {
private static final float FONT_SZ = 24;
public DaysOfTheWeekPanel() {
setLayout(new GridLayout(1, 7));
int numberDaysOfWk = DayOfWeek.values().length;
for (int i = 0; i < numberDaysOfWk; i ) {
int sundayIsZero = (i numberDaysOfWk - 1) % numberDaysOfWk;
JLabel wkDayLabel = new JLabel(DayOfWeek.values()[sundayIsZero].getDisplayName(TextStyle.SHORT, getLocale()));
wkDayLabel.setHorizontalAlignment(SwingConstants.CENTER);
Font font = wkDayLabel.getFont().deriveFont(Font.BOLD, FONT_SZ);
wkDayLabel.setFont(font);
add(wkDayLabel);
}
}
}
private class DaysOfTheMonthPanel extends JPanel {
public DaysOfTheMonthPanel() {
int gap = 2;
setLayout(new GridLayout(0, 7, gap, gap));
setBorder(BorderFactory.createLineBorder(Color.BLACK, gap));
setBackground(Color.BLACK);
}
public void setDate(LocalDate date) {
removeAll();
int year = date.getYear();
Month month = date.getMonth();
LocalDate firstOfMonth = LocalDate.of(year, month, 1);
DayOfWeek dayOfWeek = firstOfMonth.getDayOfWeek();
int daysInMonth = YearMonth.of(year, month).lengthOfMonth();
// fill empty slots with empty JLabels
for (int i = 0; i < dayOfWeek.getValue() % 7; i ) {
add(new JPanel());
}
for (int i = 1; i <= daysInMonth; i ) {
JLabel label = new JLabel(String.valueOf(i), SwingConstants.CENTER);
JPanel panel = new JPanel(new GridBagLayout());
final LocalDate localDate = LocalDate.of(year, month, i);
panel.putClientProperty(LOCAL_DATE, localDate);
panel.setBackground(Color.WHITE);
int topBtm = 25;
int side = 45;
panel.setBorder(BorderFactory.createEmptyBorder(topBtm, side, topBtm, side));
panel.add(label);
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
String format = "MMMM dd, yyyy";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
System.out.println("Date pressed: " localDate.format(formatter));
}
});
add(panel);
}
// fill in w/ jpanels until end of month
LocalDate lastDayOfMonth = LocalDate.of(year, month, daysInMonth);
int lastDayOfWeek = lastDayOfMonth.getDayOfWeek().getValue();
int daysLeft = (7 6 - lastDayOfWeek) % 7;
for (int i = 0; i < daysLeft; i ) {
add(new JPanel());
}
revalidate();
repaint();
}
}
}
CodePudding user response:
Why do you not store the buttons in an array or ArrayList so you can access them via a loop? I strongly believe you would be creating them via a loop anyway.