如何在 JCalendar 中设置自定义周开始?

How to set custom week-start in JCalendar?

我在 JCalendar 中设置自定义一周的第一天时遇到问题。 如果我更改语言环境,一周的第一天会发生变化。 但是,更改基础日历中的一周的第一天没有任何效果。

这是一个简短的演示代码:

public class TestJChooser extends JFrame {

    /**
     *
     */
    public TestJChooser() {

        setLayout(new BorderLayout(5,5));
        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        Locale locale = Locale.forLanguageTag("de-DE");

        Calendar calendar = Calendar.getInstance(locale);
        calendar.setFirstDayOfWeek(Calendar.TUESDAY);

        JCalendar jCal = new JCalendar(calendar);
        jCal.setLocale(locale);
        jCal.setPreferredSize(new Dimension(500, 400));
        jCal.getDayChooser().setDayBordersVisible(true);
        jCal.setTodayButtonVisible(true);
        getContentPane().add(jCal,BorderLayout.CENTER);

        pack();
        setVisible(true);

    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        new TestJChooser();

    }
}

更改

的值
   calendar.setFirstDayOfWeek(Calendar.TUESDAY);

不更改 JCalendar 中一周的第一天,也不更改周末。

我找到了一种使用 swingx JXMonthView 实现所需功能的方法:

public class TestJXChooser extends JFrame {

    /**
     *
     */
    public TestJXChooser() {

        //set locale
        Locale locale = Locale.forLanguageTag("de-DE"); 

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        UIManager.put(CalendarHeaderHandler.uiControllerID, SpinningCalendarHeaderHandler.class.getName());
        UIManager.put(SpinningCalendarHeaderHandler.ARROWS_SURROUND_MONTH,  Boolean.TRUE);
        UIManager.put(SpinningCalendarHeaderHandler.FOCUSABLE_SPINNER_TEXT,  Boolean.TRUE);

        final JXMonthView monthView = new JXMonthView();
        //needed for the month change and year change arrows
        monthView.setZoomable(true);
        monthView.setLocale(locale);

        //set first day of week to Tuesday
        monthView.setFirstDayOfWeek(Calendar.TUESDAY);
        //set Tuesday color
        monthView.setDayForeground(Calendar.TUESDAY, Color.MAGENTA);

        getContentPane().add(monthView  );

        pack();
        setVisible(true);


    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        new TestJXChooser();

    }
}

我无法通过 com.toedter.calendar.JDateChooser.

实现它

为了使用 com.toedter.calendar.JDateChooser 实现我需要的功能,我必须扩展它。

先做一个演示:将星期日设置为一周的第一天(尽管它被语言环境设置为星期一)。测试class:

    public class TestJXChooser extends JFrame {

        /**
         *
         */
        public TestJXChooser(){

            setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

            getContentPane().setLayout(new GridLayout(0, 1, 0, 0));
            getContentPane().setLayout(new BorderLayout(5,5));

            //set locale and calendar
            Locale locale = Locale.forLanguageTag("de-DE"); 

            Calendar cal = Calendar.getInstance(locale);
            cal.setTime(new Date());
            //set first day of week
            int firstWeekDay = Calendar.SUNDAY;
            cal.setFirstDayOfWeek(firstWeekDay);

            //-- Toedter JCalendar

            JCalendar jCalendar = new JCalendarExt(null, locale, true, true, false);
            jCalendar.setCalendar(cal);
            jCalendar.setPreferredSize(new Dimension(120, 160));
            jCalendar.getDayChooser().setDayBordersVisible(true);
            jCalendar.setTodayButtonVisible(true);
            jCalendar.setWeekOfYearVisible(false);

            getContentPane().add(jCalendar,BorderLayout.CENTER);

            //-- Toedter JDateChooser
            JCalendar jCalendar2 = new JCalendarExt(null, locale, true, true, false);
            jCalendar2.setCalendar(cal);
            JDateChooser dateChooser = new JDateChooser(jCalendar2, null , "dd.mm.yyyy",null);
            dateChooser.setLocale(locale);

            getContentPane().add(dateChooser,BorderLayout.SOUTH);

            pack();
            setVisible(true);

        }

        /**
         * @param args
         */
        public static void main(String[] args) {

            new TestJXChooser();

        }

    }

结果如图所示


第二张图片演示了将一周的第一天设置为星期二。


扩展了两个 classes。 class 扩展 JCalendar:

    /**
     * Extended to gain control on week-first-day.
     * It also enables the option to display in different color the
     * last-day-of-week, rather than <code>JCalendar</code> default which is
     * always display Sunday in a different color.
     *
     * @version
     * $Log: JCalendarExt.java,v $
     *
     *
     * @author Ofer Yuval
     * 27 Nov 2015
     *
     */
    public class JCalendarExt extends JCalendar {

        /**
         *
         * @param date
         * @param locale
         * @param monthSpinner
         * @param weekOfYearVisible
         * @param colorWeekend
         *      <br>When false, week-first-day will be painted in red, as in  <code>JDayChooser</code>.
         *      <br>When true, week-last-day will be painted in red.
         */

        public JCalendarExt(Date date, Locale locale, boolean monthSpinner, boolean weekOfYearVisible,
                boolean colorWeekend) {

            super(date, locale, monthSpinner, weekOfYearVisible);

            remove(dayChooser);

            //add the extended date chooser
            dayChooser = new JDayChooserExt(weekOfYearVisible) ;
            dayChooser.addPropertyChangeListener(this);
            ((JDayChooserExt) dayChooser).setColorWeekend(colorWeekend);

            monthChooser.setDayChooser(dayChooser);
            yearChooser.setDayChooser(dayChooser);

            add(dayChooser, BorderLayout.CENTER);

        }

        @Override
        public void setCalendar(Calendar c) {

            getDayChooser().setCalendar(c);
            super.setCalendar(c);
        }

    }

和 class 扩展 JDayChooser:

    /**
     *
     * @version
     * $Log: JDayChooserExt.java,v $
     *
     *
     * @author Ofer Yuval
     * 27 Nov 2015
     *
     */
    public class JDayChooserExt extends JDayChooser {

        /**
         * When false, week-first-day will be painted in red, as in  <code>JDayChooser</code>.
         * When true, week-last-day will be painted in red.
         */
        private boolean isColorWeekend = false;

        /**
         * @param weekOfYearVisible
         */
        public JDayChooserExt(boolean weekOfYearVisible) {

            super(weekOfYearVisible);
        }

        /**
         * Initializes the locale specific names for the days of the week.
         */
        @Override
        protected void init() {

            JButton testButton = new JButton();
            oldDayBackgroundColor = testButton.getBackground();
            selectedColor = new Color(160, 160, 160);
            drawDayNames();
            drawDays();

        }

        /**
         * Draws the day names of the day columns.
         */
        private void drawDayNames() {

            DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(locale);
            dayNames = dateFormatSymbols.getShortWeekdays();

            int Day = calendar.getFirstDayOfWeek();//firstDayOfWeek;

            int coloredDay = (isColorWeekend ) ?  Day -1 : Day;
            if(coloredDay <= 0) {
                coloredDay += 7;
            }


            for (int i = 0; i < 7; i++) {
                if ((maxDayCharacters > 0) && (maxDayCharacters < 5)) {
                    if (dayNames[Day].length() >= maxDayCharacters) {
                        dayNames[Day] = dayNames[Day]
                                .substring(0, maxDayCharacters);
                    }
                }

                days[i].setText(dayNames[Day]);

                if (Day == coloredDay) {
                    days[i].setForeground(sundayForeground);
                } else {
                    days[i].setForeground(weekdayForeground);
                }

                if (Day < 7) {
                    Day++;
                } else {
                    Day -= 6;
                }
            }
        }

        /**
         * @param isColorWeekend the isColorWeekend to set
         */
        public void setColorWeekend(boolean isColorWeekend) {
            this.isColorWeekend = isColorWeekend;
        }


        // ///////////////////////////////////////////////////////////
        // ////////////// DecoratorButton class //////////////////////
        // ///////////////////////////////////////////////////////////

        class DecoratorButton extends JButton {
            private static final long serialVersionUID = -5306477668406547496L;

            public DecoratorButton() {
                setBackground(decorationBackgroundColor);
                setContentAreaFilled(decorationBackgroundVisible);
                setBorderPainted(decorationBordersVisible);
            }

            @Override
            public void addMouseListener(MouseListener l) {
            }

            @Override
            public boolean isFocusable() {
                return false;
            }

            @Override
            public void paint(Graphics g) {
                if ("Windows".equals(UIManager.getLookAndFeel().getID())) {
                    // this is a hack to get the background painted
                    // when using Windows Look & Feel
                    if (decorationBackgroundVisible) {
                        g.setColor(decorationBackgroundColor);
                    } else {
                        g.setColor(days[7].getBackground());
                    }
                    g.fillRect(0, 0, getWidth(), getHeight());
                    if (isBorderPainted()) {
                        setContentAreaFilled(true);
                    } else {
                        setContentAreaFilled(false);
                    }
                }
                super.paint(g);
            }
        };
    }

这里是使用反射的肮脏方式,你只需要重写JCalendar:

private class MyJCalendar extends JCalendar {

  MyJCalendar(Calendar c) {
    super(c);
  }

  public void setFirstDayOfWeek(int firstdayofweek) {
   try {

    // Dirty hack to set first day of week :-)

    Field f = JDayChooser.class.getDeclaredField("calendar");
    f.setAccessible(true);

    Calendar c = (Calendar) f.get(dayChooser);
    c.setFirstDayOfWeek(firstdayofweek);

    Method m = JDayChooser.class.getDeclaredMethod("drawDayNames");
    m.setAccessible(true);
    m.invoke(dayChooser, (Object[])null);
    m = JDayChooser.class.getDeclaredMethod("drawDays");
    m.setAccessible(true);        
    m.invoke(dayChooser, (Object[])null);

   } catch (Exception ex) {
     ex.printStackTrace();
   }
  }
}