實際上,若是百度「字幕遮擋器,很能夠找到一些結果,但多半是不透明的,不符合個人使用須要,再者本身寫這種小工具是頗有趣的學習過程。這個學習過程當中有一些心得,不得不記錄一二。java
對字幕進行遮擋(學習英語用)git
在網頁中對一段文字加上底色,提升閱讀時的注意力程度(我的需求)編程
兩種用途以下圖所示ide
根據程序的用途能夠看出,該程序必須實現如下功能:函數
半透明窗體工具
總在最前post
大小調整學習
拖動this
關閉spa
後三個功能實際上都是由於無標題,因此須要本身實現。此外,還有一些錦上添花的功能:
顏色選擇(包括不透明度的調節)
切換是否總在最前
記憶顏色與位置
防止窗口縮得太小而沒法找到
後來在編程實現的過程當中也會按這些功能來描述。
語言: Java
程序的功能並不複雜,所以結構上也偷了些懶。一個主類Cover
,其中調用起繼承自JFrame
的CoverFrame
。
這裏都是一些很定式的寫法,沒什麼特別的
public class Cover { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { CoverFrame frame = new CoverFrame(); ... } }); } } class CoverFrame extends JFrame { ... }
程序的各項主要功能基本上是在CoverFrame
中實現的,具體將在後文一一敘述。
半透明窗體的關鍵是先要去掉窗體的標題和邊框,而後給該Frame設置半透明背景色便可。
class CoverFrame extends JFrame { ... private Color color = new Color(0, 0, 0, 200); //半透明 ... public CoverFrame() { ... setUndecorated(true); //去掉邊框 ... setBackground(color); ... getContentPane().setBackground(color); } }
這部分比較簡單,不過值得一說的是,若是窗體將保持半透明,即窗體不會被設置成不透明色的話(由於後面加入了顏色選擇,用戶徹底可能選擇徹底不透明的顏色),只要對CoverFrame
對象setBackground(color)
便可。
但一旦選擇了徹底不透明的顏色(Alpha值爲255),窗口則會變爲默認的灰色。避免這種狀況,就須要把Frame的ContentPane也設置成相同顔色getContentPane().setBackground(color)
。
想要窗體總在最前也比較簡單,有一個現成的函數setAlwaysOnTop
來控制。要讓程序能切換是否總在最前,也只要在CoverFrame中設置一個布爾型的state,做爲是否總在最前的開關,並添加一個JCheckBoxMenuItem
到窗口的JPopupMenu
中去。
class CoverFrame extends JFrame { ... private JPopupMenu popupMenu = new JPopupMenu(); //右鍵菜單 private Color color = new Color(0, 0, 0, 200); private boolean onTop = true; //默認總在最前 ... public CoverFrame() { setAlwaysOnTop(onTop); ... CoverFrame that = this; // set up popup menus ... JMenuItem topItem = new JCheckBoxMenuItem("Always On Top", true); //帶勾選框,默認勾選 topItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { onTop = !onTop; setAlwaysOnTop(onTop); //改變當前狀態 } }); popupMenu.add(topItem); ... } }
這兩個功能原本應該是一個窗體固有的,但由於把標題與邊框都去掉了,如今都須要本身來實現。由於兩個功能的代碼犬牙交錯,因此放在一塊兒來說了。
拖動的原理是,點擊時記錄點擊位置(相對於窗口原點),拖動時得到鼠標在屏幕上的絕對位置,這一位置減去以前記錄的相對點擊位置,就是新的窗口的位置了,這樣就實現了窗口的拖動功能。
大小調整的思路則是判斷點擊位置是處於窗口的邊緣位置,若是是,改變鼠標指針。若是位置處於左側或者上方,拖動裏改變寬/高,並改變窗口位置,若是在右側或者正文,拖動時改變窗口的寬/高。
class CoverFrame extends JFrame { private Point point = new Point(0, 0); //用於保存點擊位置 ... public CoverFrame() { ... addMouseListener(new MouseAdapter() { //監聽鼠標點擊 public void mousePressed(MouseEvent event) { // record the point where you begin to drag point = event.getPoint(); //記錄點擊位置 popupEvent(event); //右鍵菜單 } public void mouseReleased(MouseEvent event) { popupEvent(event); } private void popupEvent(MouseEvent event) { if (event.isPopupTrigger()) { popupMenu.show(event.getComponent(), event.getX(), event.getY()); //在右鍵位置顯示菜單 } } } ); addMouseMotionListener(new MouseMotionListener() { // 用來標識點擊區域(上下左右) private boolean top = false; private boolean down = false; private boolean left = false; private boolean right = false; final private int GAP = 3; public void mouseMoved(MouseEvent event) { //窗體的寬高 int width = getWidth(); int height = getHeight(); //點擊位置(相對) int x = event.getX(); int y = event.getY(); top = false; down = false; left = false; right = false; if (Math.abs(y) <= GAP) { top = true; } else if (Math.abs(y-height) <=GAP) { down = true; } if (Math.abs(x) <= GAP) { left = true; } else if (Math.abs(x-width) <=GAP) { right = true; } //若是判斷在邊緣就改變鼠標指針 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); if (top || down) setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); if (left || right) setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); if ((left && top) || (right && down)) setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); if ((right && top) || (left && down)) setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); } public void mouseDragged(MouseEvent event) { bounds = getBounds(); if (!(top || down || left || right)) { // 在中間拖動窗口 Point absPoint = event.getLocationOnScreen(); // set the location of window relate to where you click absPoint.translate(-(int)point.getX(), -(int)point.getY()); setLocation(absPoint); } else { //在四角縮放窗體 if (top) { bounds.setLocation((int)bounds.getX(), (int)bounds.getY() + event.getY()); bounds.setSize((int)bounds.getWidth(), (int)bounds.getHeight() - event.getY()); } if (down) { bounds.setSize((int)bounds.getWidth(), event.getY()); } if (left) { bounds.setLocation((int)bounds.getX() + event.getX(), (int)bounds.getY()); bounds.setSize((int)bounds.getWidth() - event.getX(), (int)bounds.getHeight()); } if (right) { bounds.setSize(event.getX(),(int)bounds.getHeight()); } validateBounds(); setBounds(bounds); } } } );
關閉原本實際上是沒啥說的,就算是沒有標題欄,可是由於把顏色和位置信息記錄下來,仍是有一些要注意的地方。
通常經過按鍵關閉窗口,會調用dispose
函數,可是這樣的話,並不會觸發windowClose的事件。要主動發出這一事件才能夠。
首先先設置關閉裏的默認動做,並監聽關閉事件,在保存設置後再退出。程序中設置的保存就很簡單的在文本文件中存了幾個數字,這裏就不細說了。
public class Cover { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { CoverFrame frame = new CoverFrame(); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); //本身處理關閉 frame.setVisible(true); frame.addWindowListener(new WindowAdapter(){ @Override public void windowClosing(WindowEvent e) { super.windowClosing(e); frame.writeCfg(); // 保存設置 System.out.println("window is closed!"); System.exit(0); } }); } }); } }
可是問題來了,若是按Alt+F4
關閉,那麼設置會被保存,但直接dispose
關閉程序,則不保存。這是由於沒有觸發關閉的事件。主動把一個關閉事件添加到事件隊列中,而後再dispose
就解決了這一問題
class CoverFrame extends JFrame { ... private JPopupMenu popupMenu = new JPopupMenu(); ... public CoverFrame() { ... // set up popup menus JMenuItem closeItem = new JMenuItem("Close"); closeItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { // make sure close event is catched WindowEvent wev = new WindowEvent(that, WindowEvent.WINDOW_CLOSING); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); //主動把一個關閉事件添加到事件隊列中 dispose(); // close window } }); popupMenu.add(closeItem); ... } }
這個功能其實也不難,主要就利用了Java中自帶的選色窗口。不細說了,給代碼片斷了。一樣要注意的是,同時設置CoverFrame
的底色和ContentPane
的底色。
class CoverFrame extends JFrame { ... private JPopupMenu popupMenu = new JPopupMenu(); ... public CoverFrame() { ... JMenuItem pickItem = new JMenuItem("Pick Color"); pickItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { Color newColor; setAlwaysOnTop(false); newColor = JColorChooser.showDialog(null, "pick your color", color); setAlwaysOnTop(onTop); if(newColor != null) { color = newColor; setBackground(color); getContentPane().setBackground(color); repaint(); } } }); popupMenu.add(pickItem); ... } }
週五花了一個下午寫了這個程序,邊學邊作因此比較慢,又花了很多時間寫這些東西,不過越寫越感受,只是一種心得的記錄而已,應該沒有什麼參考價值。
完整的代碼放在了
http://git.oschina.net/macuss...
兩百多行不太多,思路這裏基本介紹了,功能吧基本能用。
原本想把參考的帖子都致敬一下的,不過有點麻煩,很差意思了。反正也是我的日記而已。