美文网首页
Java Swing 写的一个单时间日期选择插件

Java Swing 写的一个单时间日期选择插件

作者: 想象之中丶意料之外 | 来源:发表于2021-08-09 14:42 被阅读0次

Java Swing 写的一个单时间日期选择插件。

代码有参考 网上大神写的:《DateChooser》
package util;

import com.sun.deploy.util.StringUtils;
import sun.security.ssl.HandshakeOutStream;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.function.Consumer;

/**
 * @ClassName DateTimePicker
 * @Author guokai2012
 * @Date 2021/8/1 17:53
 * @Description 时间选择插件
 */
public class DateTimePicker {
    /**
     * 时间选择组件,即在哪个组件上 操作 时间选择
     */
    private JComponent component;

    /**
     * 时间格式,默认时 2021-08-01 17:20,没有秒
     */
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    private SimpleDateFormat sdfYm = new SimpleDateFormat("yyyy年MM月");

    /**
     * 日期,默认是当前时间
     */
    private Date select = new Date();

    /**
     * 时间 选择插件大小
     */
    private int width = 200;

    private int height = 200;

    /**
     * 底层 菜单,在该菜单上 绘制功能
     */
    private JPopupMenu popup;
    /**
     * 弹出框,利用弹出框,实现点击其他位置,关闭时间选择
     */
    private JDialog dialog;
    /**
     * 是否启用
     */
    private boolean isEnable = true;
    /**
     * 显示 年月面板
     */
    private TopPanel topPanel;
    /**
     * 日历
     */
    private final Calendar calendar = Calendar.getInstance();
    /**
     * 用于对比使用时的日历
     */
    private final Calendar nowCalendar = Calendar.getInstance();
    /**
     * 显示 日期 面板
     */
    private CenterPanel centerPanel;
    /**
     * 显示 时间面板
     */
    private BottomPanel bottomPanel;
    /**
     * 鼠标 事件(被绑定的组件)
     */
    private MouseAdapter componentMostListener;
    /**
     * 回调函数
     */
    private Consumer<DateTimePicker> callFun;
    private boolean isTimeEnable = true;

    /**
     * 构造方法
     */
    public DateTimePicker() {
    }

    public DateTimePicker(String sdf) {
        this.sdf = new SimpleDateFormat(sdf);
    }

    public DateTimePicker(String sdf, int w, int h) {
        this.sdf = new SimpleDateFormat(sdf);
        this.width = w;
        this.height = h;
    }

    /**
     * textField 注册插件
     */
    public void register(JTextField textField) {
        this.component = textField;
        initDateTimePicker();
    }

    /**
     * Label 注册插件
     */
    public void register(JLabel label) {
        this.component = label;
        initDateTimePicker();
    }

    /**
     * 获取选中的时间
     */
    public String getSelect() {
        return this.sdf.format(select);
    }

    /**
     * 设置 默认选中的时间
     */
    public DateTimePicker setSelect(Date select) {
        this.select = select;
        this.calendar.setTime(this.select);
        return this;
    }

    /**
     * 获取是否启用
     */
    public boolean isEnable() {
        return isEnable;
    }

    /**
     * 设置启用,不启用
     */
    public DateTimePicker setEnable(boolean enable) {
        isEnable = enable;
        reInitDateTimePicker();
        return this;
    }

    /**
     * 获取 是否启用 时间输入框
     */
    public boolean isTimeEnable() {
        return isTimeEnable;
    }

    /**
     * 设置 是否启用时间 输入框
     */
    public DateTimePicker setTimeEnable(boolean timeEnable) {
        isTimeEnable = timeEnable;
        reInitDateTimePicker();
        return this;
    }

    /**
     * 选中后的 回调函数
     */
    public DateTimePicker changeDateEvent(Consumer<DateTimePicker> consumer) {
        this.callFun = consumer;
        return this;
    }

    /**
     * 初始化 组件
     */
    private void initDateTimePicker() {
        if (isEnable) {
            this.componentMostListener = new MouseAdapter() {
                // 鼠标按下抬起
                @Override
                public void mouseReleased(MouseEvent e) {
                    showBasePop();
                }

                // 鼠标划入
                @Override
                public void mouseEntered(MouseEvent e) {
                    component.setCursor(new Cursor(Cursor.HAND_CURSOR));
                }

                // 鼠标划出
                @Override
                public void mouseExited(MouseEvent e) {
                    component.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                }
            };
            this.component.addMouseListener(this.componentMostListener);
            // 初始化时,将值赋值到 绑定的组件
            commit();
            initBasePop();
            initTopPanel();
            initCenterPanel();
            if (isTimeEnable) {
                initBottomPanel();
            }
        }
    }

    /**
     * 重新初始化组件
     */
    private void reInitDateTimePicker() {
        if (null != this.component) {
            this.component.removeMouseListener(this.componentMostListener);
            initDateTimePicker();
        }
    }

    /**
     * 初始化 年月显示面板
     */
    private void initTopPanel() {
        // TODO 这里 要做入参配置
        this.topPanel = new TopPanel(true, true);
        this.topPanel.setBackground(Color.RED);
        this.popup.add(topPanel, BorderLayout.NORTH);
        this.topPanel.updateDate();
    }

    /**
     * 初始化 日期显示面板
     */
    private void initCenterPanel() {
        this.centerPanel = new CenterPanel(1);
        this.centerPanel.setBackground(Color.YELLOW);
        this.popup.add(centerPanel, BorderLayout.CENTER);
    }

    /**
     * 初始化 时间面板
     */
    private void initBottomPanel() {
        this.bottomPanel = new BottomPanel();
        this.bottomPanel.setBackground(Color.MAGENTA);
        this.popup.add(bottomPanel, BorderLayout.SOUTH);
    }

    /**
     * 显示 插件
     */
    private void showBasePop() {
        // 先确定 point点
        Point point = new Point(0, this.component.getHeight());
        // 将 point点,相对与组件 component的点转化成屏幕的点
        SwingUtilities.convertPointToScreen(point, this.component);
        this.dialog.setLocation(point);
        this.dialog.setVisible(true);
        // 将 popup 显示在 dialog上。这样可以 在popup失去焦点时,自动关闭
        this.popup.show(this.dialog, 0, 0);
    }

    /**
     * 初始化 底层 菜单
     */
    private void initBasePop() {
        this.dialog = new JDialog();
        this.dialog.setSize(0, 0);
        // 设置为 没有 关闭按钮
        this.dialog.setUndecorated(true);

        // 创建 弹出菜单,并重写 即将隐藏方法。
        this.popup = new JPopupMenu() {
            @Override
            protected void firePopupMenuWillBecomeInvisible() {
                // 将 dialog也设置为 不可见
                dialog.setVisible(false);
            }
        };
        this.popup.setPopupSize(new Dimension(this.width, this.height));
        // TODO 颜色
        this.popup.setBackground(Color.RED);
        this.popup.setBorder(BorderFactory.createEmptyBorder());
        this.popup.setLayout(new BorderLayout());
    }

    /**
     * 刷新 日期组件
     */
    private void refresh() {
        this.topPanel.updateDate();
        this.centerPanel.updateDate();
        SwingUtilities.updateComponentTreeUI(this.popup);
    }

    /**
     * 选中日期方法
     */
    private void selectDate(int year, int month, int day) {
        Calendar tmp = Calendar.getInstance();
        if (isTimeEnable && null != bottomPanel) {
            int hour = Integer.parseInt(bottomPanel.hourText.getText());
            int min = Integer.parseInt(bottomPanel.minText.getText());
            tmp.set(year, month, day, hour, min);
        } else {
            tmp.set(year, month, day);
        }
        setSelect(tmp.getTime());
        commit();
        refresh();
    }

    /**
     * 确认 时间胡触发
     */
    private void commit() {
        if (this.component instanceof JTextField) {
            ((JTextField) this.component).setText(sdf.format(select));
        }
        if (this.component instanceof JLabel) {
            ((JLabel) this.component).setText(sdf.format(select));
        }
        if (null != this.callFun) {
            this.callFun.accept(this);
        }
        if (null != this.popup) {
            this.popup.setVisible(false);
        }
    }

    /**
     * 最上面的 面板,显示 年月日,并支持年月左右移动调整
     */
    private class TopPanel extends JPanel {
        private final JPanel leftP = new JPanel();
        private final JPanel rightP = new JPanel();
        // 年选择按钮
        private final boolean yearClick;
        // 月按钮
        private final boolean monthClick;
        private JLabel yearL;
        private JLabel yearR;
        private final JLabel center;
        private JLabel monthL;
        private JLabel monthR;

        private final MouseListener listener = new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                // 根据 点击事件,显示值
                JLabel source = (JLabel) e.getSource();
                switch (source.getName()) {
                    case "yearL":
                        lastYear();
                        break;
                    case "yearR":
                        nextYear();
                        break;
                    case "monthL":
                        lastMonth();
                        break;
                    case "monthR":
                        nextMonth();
                        break;
                    default:
                        break;
                }
                refresh();
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                if (yearClick) {
                    yearL.setCursor(new Cursor(Cursor.HAND_CURSOR));
                    yearR.setCursor(new Cursor(Cursor.HAND_CURSOR));
                }
                if (monthClick) {
                    monthL.setCursor(new Cursor(Cursor.HAND_CURSOR));
                    monthR.setCursor(new Cursor(Cursor.HAND_CURSOR));
                }
            }

            @Override
            public void mouseExited(MouseEvent e) {
                if (yearClick) {
                    yearL.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    yearR.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                }
                if (monthClick) {
                    monthL.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    monthR.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                }
            }
        };


        public TopPanel(boolean yearClick, boolean monthClick) {
            this.yearClick = yearClick;
            this.monthClick = monthClick;
            setLayout(new BorderLayout());
            this.center = new JLabel(sdfYm.format(select), JLabel.CENTER);
            int hGap = ((width - 100) / 4) - 10;
            this.leftP.setLayout(new FlowLayout(FlowLayout.LEFT, hGap, 5));
            this.rightP.setLayout(new FlowLayout(FlowLayout.RIGHT, hGap, 5));
            intTopPanel();
        }

        private void intTopPanel() {
            if (yearClick) {
                this.yearL = new JLabel("<<", JLabel.CENTER);
                this.yearL.setToolTipText("上一年");
                this.yearL.setName("yearL");
                this.yearL.addMouseListener(this.listener);

                this.yearR = new JLabel(">>", JLabel.CENTER);
                this.yearR.setToolTipText("下一年");
                this.yearR.setName("yearR");
                this.yearR.addMouseListener(this.listener);
            }

            if (monthClick) {
                this.monthL = new JLabel("<", JLabel.CENTER);
                this.monthL.setToolTipText("上个月");
                this.monthL.setName("monthL");
                this.monthL.addMouseListener(this.listener);

                this.monthR = new JLabel(">", JLabel.CENTER);
                this.monthR.setToolTipText("下个月");
                this.monthR.setName("monthR");
                this.monthR.addMouseListener(this.listener);
            }

            if (yearClick && monthClick) {
                this.leftP.add(this.yearL);
                this.leftP.add(this.monthL);
                this.rightP.add(this.monthR);
                this.rightP.add(this.yearR);
                this.leftP.setVisible(true);
                this.rightP.setVisible(true);
            } else if (yearClick) {
                this.leftP.add(this.yearL);
                this.rightP.add(this.yearR);
                this.leftP.setVisible(true);
                this.rightP.setVisible(true);
            } else if (monthClick) {
                this.leftP.add(this.monthL);
                this.rightP.add(this.monthR);
                this.leftP.setVisible(true);
                this.rightP.setVisible(true);
            } else {
                this.leftP.setVisible(false);
                this.rightP.setVisible(false);
            }

            add(this.leftP, BorderLayout.WEST);
            add(this.center, BorderLayout.CENTER);
            add(this.rightP, BorderLayout.EAST);
            setVisible(true);
        }

        @Override
        public void setBackground(Color bg) {
            super.setBackground(bg);
            if (null != this.leftP) {
                this.leftP.setBackground(bg);
            }
            if (null != rightP) {
                this.rightP.setBackground(bg);
            }
        }

        // 年月 上下翻方法
        private void nextMonth() {
            calendar.add(Calendar.MONTH, 1);
        }

        private void lastMonth() {
            calendar.add(Calendar.MONTH, -1);
        }

        private void nextYear() {
            calendar.add(Calendar.YEAR, 1);
        }

        private void lastYear() {
            calendar.add(Calendar.YEAR, -1);
        }

        // 更新 年月 显示
        public void updateDate() {
            this.center.setText(sdfYm.format(calendar.getTime()));
        }
    }

    /**
     * 日期 插件,中间选择日期功能
     */
    private class CenterPanel extends JPanel {
        // 默认 星期天,为一周的第一天
        private final int startWeek;
        // 默认 星期日为第一天,即0下标
        private final String[] weekName = {"日", "一", "二", "三", "四", "五", "六"};
        private final List<MyLabel> myLabelList = new ArrayList<>();

        public CenterPanel(int startWeek) {
            this.startWeek = startWeek > 7 ? 6 : (Math.max(startWeek, 0));
            setLayout(new GridLayout(7, 7, 5, 5));
            initCenterPanel();
        }

        private void initCenterPanel() {
            // 表格第一行,显示 星期
            // 重切数组
            cutArrays();
            for (String s : this.weekName) {
                add(new JLabel(s, JLabel.CENTER));
            }
            updateDate();
            setVisible(true);
        }

        // 绘制 JLable 日期组件
        private void updateDate() {
            removeAllJLabel();
            Date temp = calendar.getTime();
            Calendar tmpCalendar = Calendar.getInstance();
            tmpCalendar.setTime(temp);
            tmpCalendar.set(Calendar.DAY_OF_MONTH, 1);
            int index = tmpCalendar.get(Calendar.DAY_OF_WEEK);
            // 先通过 计算,将星期和日期能够在表格中对其
            int sum = (index == 1 ? 8 : index) - startWeek;
            // 再处理一下,对于特殊值的问题
            if (sum == 1) {
                sum = 8;
            } else if (sum < 0) {
                sum += 7;
            } else if (sum == 0) {
                sum = 7;
            }
            tmpCalendar.add(Calendar.DAY_OF_MONTH, -sum);
            for (int i = 0; i < 42; i++) {
                tmpCalendar.add(Calendar.DAY_OF_MONTH, 1);
                MyLabel myLabel = new MyLabel(tmpCalendar.get(Calendar.YEAR),
                        tmpCalendar.get(Calendar.MONTH), tmpCalendar.get(Calendar.DAY_OF_MONTH));
                myLabel.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseReleased(MouseEvent e) {
                        MyLabel myLabel = (MyLabel) e.getSource();
                        selectDate(myLabel.year, myLabel.month, myLabel.day);
                    }

                    @Override
                    public void mouseEntered(MouseEvent e) {
                        myLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
                        myLabel.isHover = true;
                    }

                    @Override
                    public void mouseExited(MouseEvent e) {
                        myLabel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                        myLabel.isHover = false;
                    }
                });
                myLabelList.add(myLabel);
                add(myLabel);
            }
        }

        // 删除 日期 JLable组件
        private void removeAllJLabel() {
            for (MyLabel myLabel : myLabelList) {
                remove(myLabel);
            }
        }


        @Override
        public void setBackground(Color bg) {
            super.setBackground(bg);
        }

        // 处理数组,按照 指定的开始星期处理
        private void cutArrays() {
            String[] e = Arrays.copyOfRange(this.weekName, this.startWeek, this.weekName.length);
            String[] s = Arrays.copyOfRange(this.weekName, 0, this.startWeek);
            System.arraycopy(e, 0, this.weekName, 0, e.length);
            System.arraycopy(s, 0, this.weekName, e.length, s.length);
        }

        /**
         * 自定义 JLabel
         */
        private class MyLabel extends JLabel {
            private final int year;
            private final int month;
            private final int day;
            private boolean opaque = false;
            private Border currBorder = null;
            public boolean isHover;

            public MyLabel(int year, int month, int day) {
                super("" + day, JLabel.CENTER);
                this.year = year;
                this.month = month;
                this.day = day;
                nowCalendar.set(year, month, day);

                // 当月的天设置为黑色,否则设置为 亮灰色
                if (this.month == calendar.get(Calendar.MONTH)) {
                    // 将当月 周六、周日 标记为红色
                    if (nowCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || nowCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                        this.setForeground(Color.RED);
                    } else {
                        this.setForeground(Color.BLACK);
                    }
                } else {
                    this.setForeground(Color.LIGHT_GRAY);
                }

                // 设置选择的日期
                nowCalendar.setTime(select);
                if (this.day == nowCalendar.get(Calendar.DAY_OF_MONTH) && this.month ==
                        nowCalendar.get(Calendar.MONTH) && this.year == nowCalendar.get(Calendar.YEAR)) {
                    this.opaque = true;
                    setOpaque(this.opaque);
                    this.setBackground(Color.CYAN);
                    this.setForeground(Color.WHITE);
                }

                // 设置当前日期,给出红色边框
                // 先把日历重置回来
                nowCalendar.setTime(new Date());
                if (this.year == nowCalendar.get(Calendar.YEAR) && this.month == nowCalendar.get(Calendar.MONTH) &&
                        this.day == nowCalendar.get(Calendar.DAY_OF_MONTH)) {
                    this.currBorder = BorderFactory.createLineBorder(Color.RED);
                    this.setBorder(this.currBorder);
                }
            }

            @Override
            protected void paintComponent(Graphics g) {
                // 如果被选中了就画出一个虚线框出来
                if (isHover) {
                    Stroke s = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE,
                            BasicStroke.JOIN_BEVEL, 1.0f,
                            new float[]{2.0f, 2.0f}, 1.0f);
                    Graphics2D gd = (Graphics2D) g;
                    gd.setStroke(s);
                    // 设置虚线颜色
                    gd.setColor(Color.BLACK);
                    Polygon p = new Polygon();
                    p.addPoint(0, 0);
                    p.addPoint(getWidth() - 1, 0);
                    p.addPoint(getWidth() - 1, getHeight() - 1);
                    p.addPoint(0, getHeight() - 1);
                    gd.drawPolygon(p);
                    // 针对 选中日期 & 当前日期 已存在边框的,做特殊处理
                    if (null != this.currBorder) {
                        this.setBorder(null);
                    }
                    if (this.opaque) {
                        this.setOpaque(false);
                    }
                } else {
                    this.setBorder(this.currBorder);
                    this.setOpaque(this.opaque);
                }
                super.paintComponent(g);
                this.repaint();
            }
        }
    }

    /**
     * 最地下,设置事件
     */
    private class BottomPanel extends JPanel {
        private JTextField hourText;
        private JTextField minText;

        public BottomPanel() {
            super();
            initBottomPanel();
        }

        // 初始化 组件
        private void initBottomPanel() {
            setLayout(new FlowLayout(FlowLayout.CENTER));
            int col = Math.floorDiv(((width - 70) >> 1), 11);
            JLabel title = new JLabel("时间", JLabel.CENTER);

            // 小时文本框
            this.hourText = new JTextField(null, col);
            // 设置 小时显示
            this.hourText.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
            this.hourText.setHorizontalAlignment(SwingConstants.CENTER);
            // 设置 自定义Document,主要用户控制输入和删除问题
            MyDocument docHour = new MyDocument(23);
            this.hourText.setDocument(docHour);
            // 设置好document后,才可以设置默认text,否则无法显示
            docHour.insertString(0, String.valueOf(calendar.get(Calendar.HOUR_OF_DAY)), null);
            // 禁止赋值/粘贴
            this.hourText.setTransferHandler(null);

            // 时间分隔符
            JLabel timeSeparator = new JLabel(":", JLabel.CENTER);
            timeSeparator.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));

            // 分钟文本
            this.minText = new JTextField(null, col);
            this.minText.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
            this.minText.setHorizontalAlignment(SwingConstants.CENTER);
            // 设置 自定义Document,主要用户控制输入和删除问题。
            MyDocument docMin = new MyDocument(59);
            this.minText.setDocument(docMin);
            docMin.insertString(0, String.valueOf(calendar.get(Calendar.MINUTE)), null);
            // 禁止赋值/粘贴
            this.minText.setTransferHandler(null);

            add(title);
            add(hourText);
            add(timeSeparator);
            add(minText);
            setVisible(true);
        }

        // 设置 自定义Document,主要用户控制输入和删除问题
        private class MyDocument extends PlainDocument {
            private int max;
            private DecimalFormat dcf = new DecimalFormat("00");

            public MyDocument(int max) {
                this.max = max;
            }

            @Override
            public void insertString(int offs, String str, AttributeSet a) {
                int i = checkInput(offs, str);
                if (i < 0 || i > max) {
                    return;
                }
                try {
                    remove(0, getLength());
                    String s = dcf.format(i);
                    super.insertString(0, s, a);
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
            }

            /**
             * 输入验证
             * 文本框已输入的值,不能超过max值。
             */
            private int checkInput(int offs, String str) {
                try {
                    StringBuilder text = new StringBuilder(getText(0, getLength())).insert(offs, str);
                    String s = StringUtils.trimWhitespace(text.toString());
                    return Integer.parseInt(s);
                } catch (NumberFormatException | ArrayIndexOutOfBoundsException | BadLocationException e) {
                    e.printStackTrace();
                    return -1;
                }
            }
        }
    }


    public static void main(String[] args) {

        DateTimePicker dateTimePicker = new DateTimePicker("yyyy-MM-dd HH:mm", 160, 200);
        JTextField showDate1 = new JTextField("单击选择日期");
        dateTimePicker.setEnable(true).setSelect(new Date()).changeDateEvent(new Consumer<DateTimePicker>() {
            @Override
            public void accept(DateTimePicker o) {
                System.out.println(o.getSelect());
            }
        }).register(showDate1);

        DateTimePicker dateTimePicker1 = new DateTimePicker("yyyy-MM-dd", 200, 200);
        JLabel label = new JLabel("单击选中日期");
        dateTimePicker1.setEnable(true).setTimeEnable(false).changeDateEvent(new Consumer<DateTimePicker>() {
            @Override
            public void accept(DateTimePicker o) {
                System.out.println(o.getSelect());
            }
        }).register(label);

        JFrame jf = new JFrame("测试日期选择器");
        jf.add(showDate1, BorderLayout.NORTH);
        jf.add(label, BorderLayout.SOUTH);
        jf.setBounds(500, 500, 500, 500);
        jf.setLocationRelativeTo(null);
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

相关文章

网友评论

      本文标题:Java Swing 写的一个单时间日期选择插件

      本文链接:https://www.haomeiwen.com/subject/kbnxiktx.html