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新實例也無法關閉前一個窗口。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章