- 主题:透明背景的文本显示器(自用摸鱼阅读器)
- 语言:java
- 环境:win7+jdk8+eclipse
- 额外jar包:
fastjson-1.2.83.jar
,jintellitype-1.3.9.jar
; - 额外的windows的dll文件:
JIntellitype.dll
,JIntellitype64.dll
- 功能:使用ArrayList对文本进行行存储,通过HashMap来存储文本的目录,通过将Java容器的背景设置透明色,实现只显示文字,通过监听鼠标的位置,来实现对容器的显示位置的移动,通过监听键盘实现对显示内容的切换。
- 待优化:
- 后续:加入使用快捷键显示所有阅读过的文本以及文本的相关信息。
快捷键
// alt + a:隐藏窗口
// alt + e:退出程序
// alt + q:显示窗口
// alt + z:自动翻页
// alt + c:停止自动翻页
// alt + s:下一页
// alt + w:上一页
// alt + d:下一章
// alt + u:上一章
- 优点:快捷键是全局监听,参数过多,占用资源
- 缺点:容易占用其他软件的快捷键,代码较乱不易阅读,会有一个缓存已经阅读的页数的文件在项目中但是缓存容易丢失
package jn.test;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.melloware.jintellitype.HotkeyListener;
import com.melloware.jintellitype.JIntellitype;
/**
* @author user
* @ClassName:NovelRead
* @Description:展示文本用于阅读
*/
public class NovelRead extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* @Fields:GLOBAL_HOT_KEY_*:设置热键
*/
public static final int GLOBAL_HOT_KEY_1 = 0;
public static final int GLOBAL_HOT_KEY_2 = 1;
public static final int GLOBAL_HOT_KEY_3 = 2;
public static final int GLOBAL_HOT_KEY_4 = 3;
public static final int GLOBAL_HOT_KEY_5 = 4;
public static final int GLOBAL_HOT_KEY_6 = 5;
public static final int GLOBAL_HOT_KEY_7 = 6;
public static final int GLOBAL_HOT_KEY_8 = 7;
public static final int GLOBAL_HOT_KEY_9 = 8;
/**
* @Fields:flag:是否进行自动翻页的标识
*/
public static boolean flag;
/**
* @Fields:rebot:自动按键的实例
*/
public static Robot rebot;
/**
* @Fields:filePath:配置文件路径,配置文件用来保存看到的页数
*/
public static String filePath;
/**
* @Fields:pageNum:记录已经看到的页数
*/
public static int pageNum = 0;
/**
* @Fields:x:鼠标位置
* @Fields:y:鼠标位置
*/
int x = 0;
int y = 0;
private static Map<Integer,String> hm=new LinkedHashMap<>();
public static NovelRead nr;
public static void main(String[] args) {
try {
nr = new NovelRead();
// 获取当前项目的运行地址
String path = nr.getClass().getResource("").getPath(); // /E:/sts/Test/bin/jn/test/
path = path.substring(1); // E:/sts/Test/bin/jn/test/
// 获取文件:TODO:修改小说文件路径,注意使用反斜杠
File file = new File("E:/Users/user/Desktop/asds.txt");
String name = file.getName();
// 生成与文件名称相同的隐形配置文件,目前配置文件是用来保存看到的页数
File conf = new File(path + "." + name);
// 判断文件是否存在,不存在就创建,存在就调用方法获取页数
if (!conf.exists()) {
conf.createNewFile();
} else {
readConf(conf);
}
// 获取配置文件地址,用于后面翻页是进行写入数据
filePath = conf.getPath();
// pageNum=25901;
// 调用方法显示界面
nr.showText(nr.readNovel(file, 30, "UTF-8"));
// 设置窗体是否可见
nr.setVisible(true);
} catch (Exception e) {
System.out.println("主函数出现错误:" + e);
}
}
/**
* @Title:readConf
* @Description:用于读取配置文件,返回已经读到的页数
* @param file 需要的读取的配置文件
* @return
* @return
* @throws
*/
public static void readConf(File file) {
// 初始化
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
String str;
if ((str = in.readLine()) != null) {
pageNum = Integer.parseInt(str);
} else {
}
} catch (Exception e) {
System.out.println("读文件报错:" + e);
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @Title:writeCondf
* @Description:用于写入已经看得页数到配置文件中
* @param: page 具体的int页数值
* @return void
* @throws
*/
public static void writeCondf(int page) {
PrintStream ps = null;
try {
ps = new PrintStream(filePath);
ps.print(page);
} catch (Exception e) {
System.out.println("写文件报错:" + e);
} finally {
ps.close();
}
}
/**
* @Title:readNovel
* @Description:用于读取文件内容,并使用arraylist进行存储
* @param:file 需要读取的文件
* @param:LINELENGTH 每行显示的字数
* @param:charSet 文件的编码
* @param:
* @return ArrayList<String>
* @throws
*/
public ArrayList<String> readNovel(File file, int LINELENGTH, String charSet) {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(file), Charset.forName(charSet)));
String str;
int i = 1;
int j = 0;
// StringBuilder sb;
ArrayList<String> al = new ArrayList<>();
String reg="^[第][0-9]*[章].*";
while ((str = in.readLine()) != null) {
// 去除空字符串的情况,像文本本身存在的空行
if (str.isEmpty())
continue;
if (i > 0 && i <= 630000000) {
// 将行内容取出进行长度判断,若是大于设置的长度值,就进行截取分为两段进行存入到ArrayList中
while (str.length() > LINELENGTH) {
al.add(str.substring(0, LINELENGTH));
// 截取字符串
str = str.substring(LINELENGTH);
//行号自增
j++;
}
//匹配章节的行数,并进行存储,便于跳转章节
if(str.matches(reg)){
hm.put(j,str);
}
j++;
al.add(str);
}
i++;
}
in.close();
return al;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @Title:showText
* @Description:用于展示界面和文本
* @param al 用于存储文本的ArrayList
* @return void
* @throws
*/
public void showText(final ArrayList<String> al) {
try {
// 设置窗体初始显示位置
int initx = 20;
int inity = 200;
// 设置窗体显示大小
int width = 500;
int height = 20;
// 获取屏幕的高宽
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
initx = (int) screensize.getWidth()/2-400;
inity = (int) screensize.getHeight()-62;
// 设置窗体局部管理器
this.setLayout(null);
// 设置窗体观感(皮肤、主题)
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
// 设置窗体标题
this.setTitle("阅读");
// 隐藏任务栏图标
this.setType(Type.UTILITY);
// 设置窗体组件是否可见
// this.setVisible(true);
// 设置窗体透明度
// this.setOpacity(0.6f);
// 设置窗体大小是否可调节
this.setResizable(false);
// 设置窗体是否总是在最顶层显示
this.setAlwaysOnTop(true);
// 设置背景颜色
// this.setBackground(Color.white);
// //不生效是因为在上层显示的并不是该图层,需要使用下面这个设置背景颜色
// this.getContentPane().setBackground(Color.white);
// 设置容器焦点
this.setFocusable(false);
// 设置窗体关闭就退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 添加鼠标监听用来获取鼠标当前位置
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
}
});
// 设置鼠标移动窗体事件
this.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
// 获取鼠标移动了多少距离
int xOnScreen = e.getXOnScreen();
int yOnScreen = e.getYOnScreen();
// 移动后鼠标的位置
int xx = xOnScreen - x;
int yy = yOnScreen - y;
// 移动窗体到鼠标位置
NovelRead.this.setLocation(xx, yy);
}
});
JPanel jPanel = new JPanel();// 得到窗口的容器
jPanel.setLayout(null);
jPanel.setBounds(0, 0, width, height);
// 设置组件是否背景透明,使用外层背景颜色
jPanel.setOpaque(true);
// 设置组件是否可见
jPanel.setVisible(true);
// 设置窗口的背景为透明色(显示文本层的所有外层的背景颜色都设置为new Color(0,0,0,0))
jPanel.setBackground(new Color(0, 0, 0, 0));
this.add(jPanel);
// jPanel.setSize(300, 20);
final JLabel l1 = new JLabel(); // 创建一个标签
// 设置显示的字体颜色
l1.setForeground(new Color(0,0,0,40));
// 设置字体样式
Font font = new Font("宋体", Font.PLAIN, 15);
l1.setFont(font);
l1.setBounds(0, 0, width, height);
jPanel.add(l1);
// 设置窗体位置,大小
this.setBounds(initx, inity, width, height);
// 是否去掉外边框修饰
this.setUndecorated(true);
// 设置窗口的背景为透明色(显示文本层的所有外层的背景颜色都设置为new Color(0,0,0,0))
this.setBackground(new Color(0, 0, 0, 0));
if (pageNum >= 0 && pageNum < al.size()) {
l1.setText(al.get(pageNum) + " " + pageNum + "/"+ (al.size() - 1));
} else if (pageNum >= al.size()) {
pageNum = al.size() - 1;
l1.setText(al.get(pageNum) + " " + pageNum + "/"+ (al.size() - 1));
} else {
l1.setText(al.get(pageNum) + " " + pageNum + "/"+ (al.size() - 1));
}
// 注册全局热键快捷键,当点击alt+S时调用GLOBAL_HOT_KEY_1
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_1,JIntellitype.MOD_ALT, (int) 'S');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_2,JIntellitype.MOD_ALT, (int) 'W');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_3,JIntellitype.MOD_ALT, (int) 'A');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_4,JIntellitype.MOD_ALT, (int) 'Q');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_5,JIntellitype.MOD_ALT, (int) 'E');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_6,JIntellitype.MOD_ALT, (int) 'Z');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_7,JIntellitype.MOD_ALT, (int) 'C');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_8,JIntellitype.MOD_ALT, (int) 'D');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_9,JIntellitype.MOD_ALT, (int) 'U');
// 监听按键,并对热键进行相关操作
// alt + a:隐藏窗口
// alt + e:退出程序
// alt + q:显示窗口
// alt + z:自动翻页
// alt + c:停止自动翻页
// alt + s:下一页
// alt + w:上一页
// alt + d:下一章
// alt + u:上一章
JIntellitype.getInstance().addHotKeyListener(new HotkeyListener() {
public void onHotKey(int markCode) {
switch (markCode) {
case GLOBAL_HOT_KEY_1:
if (pageNum < (al.size() - 1)) {
pageNum += 1;
// 由于背景色为透明的,每次更新文本时,并没有重新加载外层组件,就会导致更新文本后,文本重叠的情况,只需要更新文本内容时,更新外层组件
nr.repaint();
l1.setText(al.get(pageNum) + " " + pageNum + "/"+ (al.size() - 1));
writeCondf(pageNum);
} else {
nr.repaint();
l1.setText(al.get(al.size() - 1) + " " + pageNum+ "/" + (al.size() - 1));
}
break;
case GLOBAL_HOT_KEY_2:
if (pageNum >= 1) {
pageNum--;
nr.repaint();
l1.setText(al.get(pageNum) + " " + pageNum + "/"+ (al.size() - 1));
writeCondf(pageNum);
} else {
nr.repaint();
l1.setText(al.get(0) + " " + pageNum + "/"+ (al.size() - 1));
}
break;
case GLOBAL_HOT_KEY_3:
// 删除已经注册全局快捷键
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_1);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_2);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_3);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_5);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_6);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_7);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_8);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_9);
flag = false;
nr.setVisible(false);
break;
case GLOBAL_HOT_KEY_4:
// 注册全局快捷键
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_1, JIntellitype.MOD_ALT,(int) 'S');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_2, JIntellitype.MOD_ALT,(int) 'W');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_3, JIntellitype.MOD_ALT,(int) 'A');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_5, JIntellitype.MOD_ALT,(int) 'E');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_6, JIntellitype.MOD_ALT,(int) 'Z');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_7, JIntellitype.MOD_ALT,(int) 'C');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_8, JIntellitype.MOD_ALT,(int) 'D');
JIntellitype.getInstance().registerHotKey(GLOBAL_HOT_KEY_9, JIntellitype.MOD_ALT,(int) 'U');
nr.setVisible(true);
flag = true;
break;
case GLOBAL_HOT_KEY_5:
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_1);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_2);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_3);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_4);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_5);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_6);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_7);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_8);
JIntellitype.getInstance().unregisterHotKey(GLOBAL_HOT_KEY_9);
flag = false;
System.exit(0);
break;
case GLOBAL_HOT_KEY_6:
flag = true;
rebottest();
break;
case GLOBAL_HOT_KEY_7:
flag = false;
break;
case GLOBAL_HOT_KEY_8:
for (Integer i : hm.keySet()) {
if(i>pageNum){
pageNum=i;
nr.repaint();
l1.setText(al.get(pageNum) + " " + pageNum + "/"+ (al.size() - 1));
writeCondf(pageNum);
// System.out.println(i+":"+hm.get(i));
break;
}
}
break;
case GLOBAL_HOT_KEY_9:
int zj=0;
for (Integer i : hm.keySet()) {
if(i>=pageNum){
pageNum=zj;
nr.repaint();
l1.setText(al.get(pageNum) + " " + pageNum + "/"+ (al.size() - 1));
writeCondf(pageNum);
// System.out.println(i+":"+hm.get(i));
break;
}
zj=i;
}
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Title:rebottest
* @Description:用于自动翻页的
* @return void
* @throws
*/
public static void rebottest() {
try {
rebot = new Robot();
// 小说翻页
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (flag) {
try {
rebot.keyPress(KeyEvent.VK_ALT);
rebot.keyPress(KeyEvent.VK_S);
rebot.keyRelease(KeyEvent.VK_S);
rebot.keyRelease(KeyEvent.VK_ALT);
Thread.sleep(6000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
t1.start();
} catch (AWTException e) {
e.printStackTrace();
}
}
}