java小闹钟

常量:

package clock;

/**
 * 常量
 * 
 * @author chenlun
 *
 */
public final class Constants {
	/**
	 * 响铃频率
	 */
	public static final String[] frequencies = new String[] { "仅一次", "每天", "每周" };
	public static final Integer[] hours = new Integer[24];
	public static final Integer[] minutes = new Integer[60];
	public static final String clockFile = "D:/MyDiary/clock/clocks.txt";
	// eclipse里有用,打包后无效
	// public static final String musicFile = "src/clock/music/Red.flac";
	public static final String musicFile = "D:/MyDiary/clock/Red.flac";
	public static final String logFile = "src/log4j.properties";
	// public static final String readMeFile = "src/clock/readMe.txt";
	public static final String readMeFile = "D:/MyDiary/clock/readMe.txt";
	static {
		for (int j = 0; j < 24; j++)
			hours[j] = j;
		for (int i = 0; i < 60; i++)
			minutes[i] = i;
	}
}

 播放器:

package clock;

import java.io.IOException;
import java.util.Date;

import javax.sound.sampled.LineUnavailableException;

import org.apache.log4j.Logger;
import org.kc7bfi.jflac.apps.Player;

/**
 * 播放jflac格式音乐文件,导入jflac包。播放主要步骤就2步
 * 
 * @author chenlun
 *
 */
public class FlacMusicPlayer {
	private static final Logger logger = Logger.getLogger(FlacMusicPlayer.class);
	private Thread t;

	public void playMusic(String filename) {
		t = new Thread(new Runnable() {
			@Override
			public void run() {
				// 1
				Player player = new Player();
				try {
					logger.info("响铃开始,时间为" + ThreadLocalDataFormat.getBasicDataFormat().format(new Date()));
					// 2
					player.decode(filename);
				} catch (IOException | LineUnavailableException e) {
					e.printStackTrace();
				}
			}
		});
		t.start();
	}

	@SuppressWarnings("deprecation")
	public void pause() {
		if (t != null) {
			t.stop();
			logger.info("手动关闭闹钟");
		}
	}
}

 包装闹钟记录:

package clock;

import java.util.Date;

/**
 * 日期格式:2019-01-01 00:00:00	每天/周
 * 包装日期字符串,按顺序排
 * 
 * @author chenlun
 *
 */
public class ClockTime implements Comparable<ClockTime> {

	String value;

	public ClockTime(String value) {
		this.value = value;
	}

	@Override
	public int compareTo(ClockTime o) {
		String time = value.split("\t")[0];
		String time2 = o.value.split("\t")[0];
		return compareInternal(time.trim(), time2.trim());
	}

	private int compareInternal(String s1, String s2) {

		Date date1 = null, date2 = null;
		date1 = ThreadLocalDataFormat.parse(s1);
		date2 = ThreadLocalDataFormat.parse(s2);

		if (s1.equals(s2)) {
			return 0;
		} else if (date1.before(date2)) {
			return -1;
		} else
			return 1;
	}
}

本地线程DateFormat:

package clock;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * SimpleDateFormat 非线程安全
 * 
 * @author chenlun
 *
 */
public class ThreadLocalDataFormat {
	private static final ThreadLocal<SimpleDateFormat> dataFormat = new ThreadLocal<>();

	public static SimpleDateFormat getBasicDataFormat() {
		SimpleDateFormat sdf = dataFormat.get();
		// 如果当前线程没有,新建一个实例,并设置到ThreadLocal
		if (null == sdf) {
			sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			dataFormat.set(sdf);
		}
		return sdf;
	}

	/**
	 * 将字符串转换成日期
	 * @param s
	 * @return
	 */
	public static Date parse(String s) {
		Date date = null;
		try {
			date = getBasicDataFormat().parse(s);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;
	}
}

时间处理:

package clock;

import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

public class TimeUtil {
	/**
	 * 将时分形式时间补全
	 * 如13:59——2019-01-01 13:59:00
	 * 
	 * @param time
	 * @return
	 * @throws ParseException
	 */
	public static String fullTime(String time) throws ParseException {
		String nowtime = ThreadLocalDataFormat.getBasicDataFormat().format(new Date());
		int index = nowtime.indexOf(":");
		return nowtime.substring(0, index - 2) + time + ":00";
	}

	/**
	 * 时间+频率
	 * 
	 * @param hour
	 * @param minute
	 * @param frequencyIndex
	 * @return
	 * @throws ParseException
	 */
	public static String[] fullTimeWithFrequency(int hour, int minute, int frequencyIndex) throws ParseException {
		String[] timeAndFrequency = new String[2];
		timeAndFrequency[0] = fullTime(hour + ":" + minute);
		timeAndFrequency[1] = Constants.frequencies[frequencyIndex];
		return timeAndFrequency;
	}

	/**
	 * 明天的闹钟时刻
	 * 
	 * @param time
	 * @return
	 * @throws ParseException
	 */
	public static String nextDayTime(String time) {
		Date date=null;
		try {
			date = ThreadLocalDataFormat.getBasicDataFormat().parse(time);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		Calendar cal = Calendar.getInstance(Locale.CHINA);
		cal.setTime(date);
		cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 1);
		return ThreadLocalDataFormat.getBasicDataFormat().format(cal.getTime());
	}

	/**
	 * 下周的闹钟时刻
	 * 
	 * @param time
	 * @return
	 * @throws ParseException
	 */
	public static String nextWeekTime(String time) {
		Date date=null;
		try {
			date = ThreadLocalDataFormat.getBasicDataFormat().parse(time);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Calendar cal = Calendar.getInstance(Locale.CHINA);
		cal.setTime(date);
		cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 7);
		return ThreadLocalDataFormat.getBasicDataFormat().format(cal.getTime());
	}

	/**
	 * 判断是否是今天
	 * 
	 * @param string
	 * @return
	 * @throws ParseException
	 */
	public static boolean isToday(String string) {
		Calendar cal = Calendar.getInstance(Locale.CHINA);
		Date date=null;
		try{
			date=ThreadLocalDataFormat.getBasicDataFormat().parse(string);
		}catch(ParseException e){
			e.printStackTrace();
		}
		cal.setTime(date);
		int year = cal.get(Calendar.YEAR);
		int month = cal.get(Calendar.MONTH);
		int day = cal.get(Calendar.DAY_OF_MONTH);
		cal.setTime(new Date());
		int nowYear = cal.get(Calendar.YEAR);
		int nowMonth = cal.get(Calendar.MONTH);
		int nowDay = cal.get(Calendar.DAY_OF_MONTH);
		return year == nowYear && month == nowMonth && day == nowDay;
	}

}

调度类:

package clock;

import java.io.IOException;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

/**
 * 调度类
 * 
 * @author chenlun
 *
 */
public class ExecuteTask {

	private static final Logger log = Logger.getLogger(ExecuteTask.class);


	/**
	 * 停止响铃
	 * 
	 * @param player
	 */
	public static void pause(FlacMusicPlayer player) {
		player.pause();
	}

	/**
	 * 仅执行一次,不进行记录。这样闹钟文件里的都是每天或每周闹钟,每次修改过期时间即可
	 * 
	 * @param executor
	 * @param player
	 * @param executeDate
	 */
	public static void executeOnce(ScheduledExecutorService executor, FlacMusicPlayer player, Date executeDate) {
		Thread t = new Thread(new Runnable() {
			@Override
			public void run() {
				player.playMusic(Constants.musicFile);
			}
		});
		executor.schedule(t, executeDate.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
	}

	/**
	 * 对于每天和每周事件
	 * 
	 * @param executor
	 * @param player
	 * @param executeDate
	 * @param timeWithFrequency
	 */
	public static void execute(ScheduledExecutorService executor, FlacMusicPlayer player, Date executeDate,
			String[] timeWithFrequency, Set<ClockTime> set) {
		String time = timeWithFrequency[0];
		String fre = timeWithFrequency[1];
		Date date = new Date();
		// 如果闹钟需要当天执行,则先执行
		if (executeDate.after(date)) {
			executeOnce(executor, player, executeDate);
		}
		// 不管当天是否需要执行,添加到set,同时(只)写入下一次闹钟发生时刻防止忘了保存
		if (fre.equals("每天")) {
			try {
				set.add(new ClockTime(time + "\t" + Constants.frequencies[1] + "\r\n"));
				IOUtil.write(Constants.clockFile, time + "\t" + Constants.frequencies[1] + "\r\n",
						true);
			} catch (IOException e) {
				log.error("写入每天闹钟发生错误");
				e.printStackTrace();
			}
		} else {
			try {
				set.add(new ClockTime(time + "\t" + Constants.frequencies[2] + "\r\n"));
				IOUtil.write(Constants.clockFile,
						time + "\t" + Constants.frequencies[2] + "\r\n", true);
			} catch (IOException e) {
				log.error("写入每周闹钟发生错误");
				e.printStackTrace();
			}
		}
	}

	/**
	 * 读取文件到set,去除过期时间,执行当天未到时间闹钟任务
	 * 
	 * @param set
	 * @param executor
	 * @param player
	 */
	public static void initSet(Set<ClockTime> set, ScheduledExecutorService executor, FlacMusicPlayer player) {
		try {
			// 读入文件
			set = IOUtil.read(Constants.clockFile);
		} catch (IOException e2) {
			log.error("读入闹钟文件发生错误");
			e2.printStackTrace();
		}
		Date tempDate = new Date();
		// 先将今天过期的时间调整,未到的闹钟执行任务
		if (!set.isEmpty())
			for (ClockTime time : set) {
				String[] taf = time.value.split("\t");
				if (ThreadLocalDataFormat.parse(taf[0]).before(tempDate)) {
					if (taf[1].equals(Constants.frequencies[1])) {
						// 直到找到大于当前时间的闹钟
						while (ThreadLocalDataFormat.parse(taf[0]).before(tempDate)) {
							taf[0] = TimeUtil.nextDayTime(taf[0]);
						}
						time = new ClockTime(taf[0] + "\t" + taf[1]);
					} else {
						while (ThreadLocalDataFormat.parse(taf[0]).before(tempDate)) {
							taf[0] = TimeUtil.nextWeekTime(taf[0]);
						}
						time = new ClockTime(taf[0] + "\t" + taf[1]);
					}
				}
				// 大于当前时间则只找今天的闹钟,因为今天电脑要关机的。
				// 如果是今天,且时间未到,建立任务执行
				else {
					if (TimeUtil.isToday(taf[0])) {
						executeOnce(executor, player, ThreadLocalDataFormat.parse(taf[0]));
					}
				}
			}

		// 修改过期的时间,当天稍后过期的时间下一次清理。重新写入文件
		try {
			IOUtil.write(Constants.clockFile, set);
		} catch (IOException e2) {
			log.error("写入闹钟文件发生错误");
			e2.printStackTrace();
		}

	}

}

I/O工具类:

package clock;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Set;
import java.util.TreeSet;

/**
 * 读写工具
 * 
 * @author chenlun
 *
 */
public class IOUtil {
	/**
	 * 非追加
	 * 
	 * @param filename
	 * @param msg
	 * @throws IOException
	 */
	public static void write(String filename, String msg) throws IOException {
		write(filename, msg, false);
	}

	/**
	 * txt在windows编码总是会改成gb2312等
	 * 
	 * @param filename
	 * @param msg
	 * @param b
	 *            是否以追加形式写入
	 * @throws IOException
	 */
	public static void write(String filename, String msg, boolean b) throws IOException {
		FileOutputStream fos;

		if (b)
			fos = new FileOutputStream(filename, true);
		else
			fos = new FileOutputStream(filename);
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, "gb2312"));
		bw.write(msg);
		bw.flush();
		bw.close();
	}

	/**
	 * 第一行为标题:响铃时间——响铃频率
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public static Set<ClockTime> read(String filename) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "gb2312"));
		Set<ClockTime> times = new TreeSet<>();
		String len;
		br.readLine();
		while ((len = br.readLine()) != null) {
			if (!len.trim().isEmpty())
				times.add(new ClockTime(len));
		}
		br.close();
		return times;
	}

	/**
	 * 读取readme文件
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public static String readMe(String filename) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "utf-8"));
		StringBuilder sb = new StringBuilder();
		String len;
		while ((len = br.readLine()) != null) {
			if (!len.trim().isEmpty())
				sb.append(len + "\r\n");
		}
		br.close();
		return sb.toString();
	}

	/**
	 * 快捷写入
	 * 
	 * @param clockfile
	 * @param set
	 * @throws IOException
	 */
	public static void write(String clockfile, Set<ClockTime> set) throws IOException {
		write(clockfile, "【响铃时间】\t【响铃频率】" + "\r\n");
		for (ClockTime time : set) {
			write(clockfile, time.value + "\r\n", true);
		}
	}
}

入口main方法所在类:

package clock;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

/**
 * 删除后如何移除已被调度任务?
 * 
 * @author chenlun
 *
 */
public class MyClock {

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() { 

			@Override
			public void run() {
				ClockFrame frame = new ClockFrame();
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setVisible(true);
			}
		});

	}
}

class ClockFrame extends JFrame {
	private static final long serialVersionUID = 1L;
	private final Logger log = Logger.getLogger(MyClock.class);
	private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
	private final FlacMusicPlayer player = new FlacMusicPlayer();
	Set<ClockTime> set = new TreeSet<>();
	JTextPane area;
	final int WIDTH = 1000;
	final int HEIGHT = 800;
	JMenuBar menus = new JMenuBar();
	JMenu file = new JMenu("菜单");

	JMenuItem add = new JMenuItem("添加");
	JMenuItem findAll = new JMenuItem("查看所有");
	JMenuItem delete = new JMenuItem("删除");
	JMenuItem save = new JMenuItem("保存");
	JMenuItem stop = new JMenuItem("停止");
	JMenuItem readMe = new JMenuItem("ReadMe");

	public ClockFrame() {
		super("clock-1.0");
		PropertyConfigurator.configure(Constants.logFile);
		file.setFont(new Font("楷体", Font.BOLD, 20));
		menus.add(file);
		file.add(add);
		file.addSeparator();
		file.add(findAll);
		file.addSeparator();
		file.add(delete);
		file.addSeparator();
		file.add(save);
		file.addSeparator();
		file.add(stop);
		file.addSeparator();
		file.add(readMe);

		// 设置快捷操作
		add.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_UP, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		delete.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		findAll.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		save.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		stop.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_P, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
		readMe.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_M, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));

		area = new JTextPane();
		area.setBackground(new Color(245, 255, 250));
		area.setFont(new Font("楷体", Font.BOLD, 24));
		JScrollPane scrollPane = new JScrollPane(area);// 滚动条
		Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
		int width = (int) screen.getWidth();
		int height = (int) screen.getHeight();
		// 左上角在屏幕的位置
		setLocation(width / 4, height / 20);
		setSize(WIDTH, HEIGHT);
		// 添加完后设置菜单栏
		setJMenuBar(menus);
		getContentPane().add(scrollPane, BorderLayout.CENTER);
		setResizable(true);
		ExecuteTask.initSet(set, executor, player);
		add.addActionListener(e -> {
			int hour = (int) JOptionPane.showInputDialog(this, "选择时", "响铃时间", JOptionPane.PLAIN_MESSAGE, null,
					Constants.hours, Constants.hours[12]);
			int minute = (int) JOptionPane.showInputDialog(this, "选择分", "响铃时间", JOptionPane.PLAIN_MESSAGE, null,
					Constants.minutes, Constants.minutes[30]);
			int index = JOptionPane.showOptionDialog(this, "选择频率", "响铃频率", JOptionPane.OK_CANCEL_OPTION,
					JOptionPane.QUESTION_MESSAGE, null, Constants.frequencies, Constants.frequencies[0]);
			String[] timeWithFrequency = null;
			try {
				timeWithFrequency = TimeUtil.fullTimeWithFrequency(hour, minute, index);
				Date executeDate = ThreadLocalDataFormat.parse(TimeUtil.fullTime(hour + ":" + minute));
				// 如果只执行一次
				if (index == 0) {
					// 且时间已经过了
					if (executeDate.before(new Date())) {
						// do nothing
					} else {
						ExecuteTask.executeOnce(executor, player, executeDate);
						JOptionPane.showMessageDialog(this, "闹钟已添加");
					}
				}
				// 每天或每周的
				else {
					ExecuteTask.execute(executor, player, executeDate, timeWithFrequency, set);
					JOptionPane.showMessageDialog(this, "闹钟已添加");
					log.info("添加闹钟成功,时间为:" + timeWithFrequency[0] + "--" + timeWithFrequency[1]);
				}
			} catch (ParseException e1) {
				log.error("添加闹钟发生错误");
				e1.printStackTrace();
			}
		});
		delete.addActionListener(e -> {
			//空无操作
			if(set.isEmpty()){
				JOptionPane.showMessageDialog(this, "当前无闹钟");
			}
			else{
			List<String> list = new ArrayList<>();
			for (ClockTime time : set) {
				list.add(time.value);
			}
			String time2 = (String) JOptionPane.showInputDialog(this, "闹钟时间", "删除闹钟", JOptionPane.INFORMATION_MESSAGE,
					null, list.toArray(), list.get(0));
			// 移除set中的
			for (ClockTime time : set) {
				if (time.value.equals(time2)) {
					set.remove(time);
					break;
				}
			}
			// 保存改变,但是已经调度的任务如何取消?或者如何重启?
			try {
				IOUtil.write(Constants.clockFile, set);
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			JOptionPane.showMessageDialog(this, "删除成功");
			log.info("删除闹钟:" + time2);
		}});
		findAll.addActionListener(e -> {
			StringBuilder sb = new StringBuilder();
			for (ClockTime time : set) {
				sb.append(time.value + "\r\n");
			}
			area.setText(sb.toString());
		});
		save.addActionListener(e -> {
			try {
				IOUtil.write(Constants.clockFile, set);
			} catch (IOException e1) {
				log.error("保存文件发生错误");
				e1.printStackTrace();
			}
		});
		stop.addActionListener(e -> {
			ExecuteTask.pause(player);
		});
		readMe.addActionListener(e -> {
			try {
				String readme = IOUtil.readMe(Constants.readMeFile);
				area.setText(readme);
			} catch (IOException e1) {
				log.error("读取readme文件发生错误");
				e1.printStackTrace();
			}
		});
	}
}

最后疑问:不知道该咋样取消已被调度的任务,重新赋值frame新实例也无法关闭前一个窗口。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值