java怎么写桌面日历_极客编程日历桌面版开发笔记

#背景介绍

图灵教育推出的 限量款编程日历2018 ,因为简约大气的设计和每周一个编程语言的介绍,在程序员中广受欢迎。

20180210193411_15.jpg

图灵教育推出的编程日历实体版

不幸的是由于限量1000款,除去赠品的300多套,真正在售的只有600多套,很快就被抢购一空。值得欣慰的是,前天下午作者将pdf版本的日历公开 下载 。

在简书中也无意间看到有人用python片段将壁纸与当周的日历进行了融合,这个想法让我受到了启发,从该文章下面的评论看到很多用户(特别是mac用户)反映在 macOS 下,Wand 库有点小问题,GitHub 有人提到了这个 issue 。

我一直在使用的一款软件Blotter,吸附在桌面上的日历和待办事项,于是就萌生了一个将该pdf吸附在桌面上,并根据当前日期展示相应日期的应用,于是我花半天做了TuringCalendar这款应用, 开源地址 。欢迎有能力的开发者改进这款应用。

20180210193412_845.jpg

Blotter截图

TuringCalendar的现状

由于时间仓促,这款软件有一些缺点需要后续解决。

现在的默认将日历页放置在右上角,因为左上角被Blotter占了,后面需要做成可配置的。

现在是白底的,在浅色背景的桌面上会比较美观,在深色背景中就不那么美观了。关于这点我在简书上问过python代码的作者,他告诉我用 通道混合 来解决,目前尚在研究中。

20180210193412_132.jpg

TuringCalendar截图

TuringCalendar开发过程

将窗口固定在桌面上

macOS管理窗口的类是NSWindow,将窗口固定在桌面上是通过继承该类,并override 其中的某些方法做到的。

override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) {

super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag)

self.level = NSWindow.Level(rawValue: NSWindow.Level.RawValue(CGWindowLevelForKey(CGWindowLevelKey.desktopWindow) - 1))

self.collectionBehavior = (NSWindow.CollectionBehavior(rawValue: NSWindow.CollectionBehavior.RawValue(UInt8(NSWindow.CollectionBehavior.canJoinAllSpaces.rawValue) |

UInt8(NSWindow.CollectionBehavior.stationary.rawValue) |

UInt8(NSWindow.CollectionBehavior.ignoresCycle.rawValue)))

)

self.backgroundColor = NSColor.clear

self.isOpaque = false

}

override var canBecomeMain: Bool{

return false;

}

override var canBecomeKey: Bool{

return false;

}

init方法中,指定了窗口的层级为desktopWindow-1,并且指定了窗口的背景色和一些操作的影响,主要是 expose 操作的时候,该窗口不应该和其他普通窗口一样,收缩起来。同时override相应方法,让该窗口不可以成为Main窗口和Key窗口。

读取pdf

读取pdf是通过PDFView完成的,需要导入 Quartz 库。在StoryBoard中也有相关的组件,可以查到日历每页的宽高,在StoryBoard中指定为固定宽高即可。

@IBOutlet var calendarViewer: PDFView!

override func viewDidLoad() {

super.viewDidLoad()

let url = Bundle.main.url(forResource: "calendar", withExtension: "pdf")

let pdf = PDFDocument(url: url!)

let today = GetWeekByDate(date: Date())

calendarViewer.document = pdf

calendarViewer.go(to: (pdf?.page(at: today-1))!)

// Do any additional setup after loading the view.

}

这里发现一个坑,PDFView是会响应鼠标事件的,上下滑会在页与页之间切换,由于PDFView是NSView的子类,因此可以override hitTest方法,让PDFView不响应相关事件,使用了extension关键字。

extension PDFView{

open override func hitTest(_point: NSPoint) -> NSView? {

return nil

}

}

得到今天是今年的第几周

我将原作者提供的pdf文件进行了截取,只保留了我们需要的53个周的数据。通过下面的方法获取到当天是2018年的第几周,然后让PDFView跳到相应的页面。

func GetWeekByDate(date:Date) ->Int{

guard let calendar = NSCalendar(identifier: NSCalendar.Identifier.gregorian) else {

return 0

}

let components = calendar.components([.weekOfYear,.weekOfMonth,.weekday,.weekdayOrdinal], from: date)

return components.weekOfYear!;

}

将窗口固定在右上角

控制窗口这件事是由windowController完成的,获取到相应的window,并调用setFrameOrigin方法指定窗口的初始x,y坐标即可。需要注意的是屏幕的坐标左下角是(0,0)。

override func windowDidLoad() {

super.windowDidLoad()

if let window = window, let screen = window.screen {

let screenRect = screen.visibleFrame

let offsetFromLeft = CGFloat(screenRect.maxX - window.frame.width)

let offsetFromTop = CGFloat(0)

let offsetFromBottom = screenRect.maxY - window.frame.height - offsetFromTop

window.setFrameOrigin(NSPoint(x: offsetFromLeft, y: offsetFromBottom))

}

}

与Python版本相比的优点

相比于python版, TuringCalendar 也有自己的优势,那就是不需要手动的去生成壁纸,而且每周要定时更换;环境的配置可能有一些坑,很多人都在评论里说配置没有成功。

最后,欢迎有能力的开发者改进 这款应用 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为你提供一个简单的桌面日历程序的实现思路和部分代码,你可以在此基础上进行修改和完善。 1. 界面设计 我们可以使用Java Swing库来实现程序的界面设计。桌面日历程序需要显示当前日期和月份的日历,还需要有添加、删除和编辑日程的功能。我们可以使用表格控件来显示日历,按钮控件来实现添加、删除和编辑功能。同时,我们还需要使用日期选择器控件来方便用户选择日期。 2. 实现日历显示 我们可以使用Calendar类来获取当前日期和月份,并根据这些信息来生成日历。具体实现过程如下: ```java // 获取当前日期和月份 Calendar cal = Calendar.getInstance(); int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH); // 获取当前月份的第一天和最后一天 cal.set(Calendar.DAY_OF_MONTH, 1); Date firstDayOfMonth = cal.getTime(); cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); Date lastDayOfMonth = cal.getTime(); // 生成日历数组 String[][] calendarData = new String[6][7]; cal.setTime(firstDayOfMonth); int row = 0, col = cal.get(Calendar.DAY_OF_WEEK) - 1; while (cal.getTime().before(lastDayOfMonth) || cal.getTime().equals(lastDayOfMonth)) { calendarData[row][col] = String.valueOf(cal.get(Calendar.DAY_OF_MONTH)); col++; if (col >= 7) { row++; col = 0; } cal.add(Calendar.DAY_OF_MONTH, 1); } ``` 在上述代码中,我们首先使用Calendar类获取当前日期和月份。接着,我们获取当前月份的第一天和最后一天,并使用一个二维数组来存储日历数据。最后,我们使用循环来生成日历数据,其中row和col表示当前单元格的行和列。 3. 实现添加、删除和编辑日程 我们可以使用JOptionPane来弹出对话框,让用户输入日程信息,并使用ArrayList来保存日程列表。具体实现过程如下: ```java // 添加日程 String schedule = JOptionPane.showInputDialog("请输入日程:"); if (schedule != null && !schedule.isEmpty()) { int dayOfMonth = Integer.parseInt(calendarData[row][col]); String date = String.format("%04d-%02d-%02d", year, month + 1, dayOfMonth); String time = JOptionPane.showInputDialog("请输入时间(格式:HH:mm):"); String dateTime = date + " " + time + ":00"; scheduleList.add(dateTime + " " + schedule); } // 删除日程 if (calendarData[row][col] != null) { ArrayList<String> selectedScheduleList = new ArrayList<>(); for (String item : scheduleList) { if (item.startsWith(date + " ")) { selectedScheduleList.add(item.substring(11)); } } if (!selectedScheduleList.isEmpty()) { String[] options = selectedScheduleList.toArray(new String[selectedScheduleList.size()]); int result = JOptionPane.showOptionDialog(null, "请选择要删除的日程:", "删除日程", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (result != JOptionPane.CLOSED_OPTION) { String selectedSchedule = options[result]; scheduleList.removeIf(item -> item.startsWith(date + " " + selectedSchedule)); } } } // 编辑日程 if (calendarData[row][col] != null) { ArrayList<String> selectedScheduleList = new ArrayList<>(); for (String item : scheduleList) { if (item.startsWith(date + " ")) { selectedScheduleList.add(item.substring(11)); } } if (!selectedScheduleList.isEmpty()) { String[] options = selectedScheduleList.toArray(new String[selectedScheduleList.size()]); int result = JOptionPane.showOptionDialog(null, "请选择要编辑的日程:", "编辑日程", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (result != JOptionPane.CLOSED_OPTION) { String selectedSchedule = options[result]; String newSchedule = JOptionPane.showInputDialog("请输入新的日程:", selectedSchedule); if (newSchedule != null && !newSchedule.isEmpty()) { scheduleList.replaceAll(item -> item.equals(date + " " + selectedSchedule) ? date + " " + newSchedule : item); } } } } ``` 在上述代码中,我们首先判断用户是否点击了添加、删除或编辑按钮。接着,我们根据当前单元格的日期来查询日程列表,并弹出相应的对话框。最后,我们将用户输入的日程信息保存到ArrayList中,并使用lambda表达式来实现日程的删除和编辑功能。 4. 实现日程提醒 我们可以使用ScheduledExecutorService来实现日程提醒功能。具体实现过程如下: ```java // 启动定时器 ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleAtFixedRate(() -> { Date now = new Date(); for (String item : scheduleList) { Date scheduleDate = null; try { scheduleDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(item.substring(0, 19)); } catch (ParseException e) { e.printStackTrace(); } if (scheduleDate != null && scheduleDate.after(now) && scheduleDate.getTime() - now.getTime() <= 60 * 1000) { String schedule = item.substring(20); JOptionPane.showMessageDialog(null, "日程提醒:" + schedule); } } }, 0, 1, TimeUnit.MINUTES); ``` 在上述代码中,我们首先创建一个ScheduledExecutorService对象,并使用scheduleAtFixedRate方法来执行定时任务。在定时任务中,我们首先获取当前时间,然后遍历日程列表,判断是否有日程需要提醒。如果找到需要提醒的日程,则弹出对话框显示提醒内容。 完整代码如下:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值