dyerac  
          dyerac In Java
          公告

          日歷
          <2006年4月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456
          統計
          • 隨筆 - 36
          • 文章 - 10
          • 評論 - 94
          • 引用 - 0

          導航

          常用鏈接

          留言簿(5)

          隨筆分類(49)

          隨筆檔案(36)

          文章分類(11)

          文章檔案(10)

          相冊

          dyerac

          搜索

          •  

          積分與排名

          • 積分 - 79328
          • 排名 - 706

          最新隨筆

          最新評論

          閱讀排行榜

          評論排行榜

           

          先把以前寫的轉過來,呵呵



          圖形界面開發對于Java來說并非它的長項,開發者經常會碰到各種各樣的限制,比如,如何打造一款任意形狀的窗口如何可以透過窗口顯示它覆蓋下的內容

          考慮到Java并沒有被設計成支持以上的功能,所以,你能得到的永遠是方方正正的窗口,毫無新意,當然,我們可以通過JNI調用本地代碼來完成,但是這就失去了java可移植性的意義,那么,用純粹的java代碼如何實現以上兩種功能呢?

          下文提供了一個實現的參考

          預備知識
          1.java.awt.Robot,這個類是一個功能非常強大的類,通過它我們可以控制鼠標和鍵盤的響應,不過這里,我們只需要用到它的一個功能--截屏,也就是得到當前屏幕的快照(screenshot)
          2.我們可以通過調用一個swing組件的paintComponent(Graphics g)方法用特定的Graphics為其定制特殊的外觀

          首先聲明的一點是,本文中的實現方法只是一個欺騙的手法,因為要想實現前文中的功能,我們幾乎的重寫一套Swing出來,這里,簡便的做法是,我們通過robot類來獲得當前屏幕的快照,然后貼在我們需要的窗口上,這樣,不就實現了透明的功能了嗎?順便提一下,幾年前日本發明的隱形衣也是依靠這種機制,這種衣服在身后附帶一套攝像機,然后即時的將拍下的內容顯示在衣服的正面,因此,當別人看過來時,仿佛就通過人和衣服看到了身后的場景^_^

          另外,還要感謝Joshua Chris leniz的Swing Hack一書,以上充滿創新的方法正來自他的書中

          好咯,讓我們具體看一下細節的處理:
          第一,我們要得到當前屏幕的快照,保存到一個Image對象[color=Red]background[/color]中:
          ? public void updateBackground() {
          ??try {
          ???Robot rbt = new Robot();
          ???Toolkit tk = Toolkit.getDefaultToolkit();
          ???Dimension dim = tk.getScreenSize();
          ???[color=Red]background [/color]= rbt.createScreenCapture(new Rectangle(0, 0, (int) dim
          ?????.getWidth(), (int) dim.getHeight()));
          ??} catch (Exception ex) {
          ?? }
          ?}

          第二,我們需要讓窗口顯示這個圖像,也就是說,讓窗口的背景圖像是這副圖像,以達到透明的欺騙效果:

          public void paintComponent(Graphics g) {
          ??Point pos = this.getLocationOnScreen();
          ??Point offset = new Point(-pos.x, -pos.y);
          ??g.drawImage([color=Red]background[/color], offset.x, offset.y, null);
          ?}

          在swing hack一書中,作者也給出了他的實現,然而,運行結果表明該實現存在很大的問題:窗口經常無法即時更新,往往背景變了,窗口里顯示的卻還是以前的背景。仔細研究了他的代碼,我發現了問題的根結,同時也是這種實現技術的關鍵要素--如何讓窗口在正確的時機里更新顯示,下面,我們會討論這一點

          第三,更新窗口外觀
          前兩步的操作,只能得到一副當前屏幕的快照,一旦背景變化了,或者窗口移動了,變化大小了,那么我們制作的窗口將永遠無法和和屏幕背景聯合成整體,也就失去了透明的效果;同時,我們也不可能每時每刻都調用
          updateBackground() 方法獲得最新的背景,可行的方法是,通過對事件的監聽來選擇適當的時間來更新外觀

          我們應該可以達到這三點共識
          1。窗口移動或改變大小時,背景圖像一般是不會發生變化的,我們不需要得到新的屏幕快照,只用將以前得到的背景中適當的部分貼到窗口上,調用repaint()方法就足已
          2。要獲得最新的屏幕快照,必須先將窗口隱藏起來,調用updateBackground() 得到圖像后再把窗口顯示,我們可以用refresh方法來表示
          ?refresh(){
          ??? frame.hide();
          ??? updateBackground() ;
          ?? frame.show();
          }
          3。如果背景改變了,那么一定是別的windows程序獲得了或失去了事件焦點,也就是說我們關注的窗口將觸發焦點得失事件,這個時候需要調用refresh() 得到最新的屏幕快照


          看到這里,你或許認為已經沒有技術難點了,然而,此時才是我們[color=Red]最需要關注的地方[/color]:
          參看第三點,我們需要在窗口得失焦點時調用refresh() 方法;參看第一點,我們調用refresh() 方法時需要先將窗口隱藏,然后再顯示。于是問題來了,在隱藏和顯示窗口時,同樣會觸發得失焦點事件,得失焦點事件又將觸發新的隱藏和顯示窗口事件(為了得到新的屏幕快照),這就使程序陷入了死循環中,我們必須加以控制,使得第二次觸發焦點得失事件時不調用refresh()方法

          作者的辦法是加一個線程來控制,通過判斷時間間隔長短來決定是否調用refresh()方法,可是,這個條件是程序非常的不穩定,因為往往調用時間會根據系統繁忙度而改變,使得需要更新時不能更新,不需要更新的時候反而更新了
          因此,我決定采取新的解決方案,能不能隱藏/顯示窗口時不觸發得失焦點事件呢?
          解決方法很簡單,我拋開了傳統的setVisible()或者show(),hide()方法,而是使用setLocation()方法,因為調用setLocation()方法時窗口不會失去焦點,同時,只要用類似setLocation(-2000,-2000)方法也同樣可以輕松的讓窗口在屏幕中消失
          下面是我的全部代碼:

          import java.awt.BorderLayout;
          import java.awt.Dimension;
          import java.awt.Graphics;
          import java.awt.Image;
          import java.awt.Point;
          import java.awt.Rectangle;
          import java.awt.Robot;
          import java.awt.Toolkit;
          import java.awt.event.ComponentEvent;
          import java.awt.event.ComponentListener;
          import java.awt.event.WindowEvent;
          import java.awt.event.WindowFocusListener;

          import javax.swing.JButton;
          import javax.swing.JComponent;
          import javax.swing.JFrame;
          import javax.swing.JLabel;
          import javax.swing.UIManager;

          import org.jvnet.substance.SubstanceLookAndFeel;
          import org.jvnet.substance.theme.SubstanceLightAquaTheme;

          import ch.randelshofer.quaqua.QuaquaLookAndFeel;

          public class TestEvent extends JComponent implements ComponentListener,
          ??WindowFocusListener {
          ?private JFrame frame;

          ?private Boolean isHiding = false, isShowing = false, start = false;

          ?private Image background;

          ?private Point p;
          ????????????????????
          ??????????????????? [color=Red]//獲得當前屏幕快照[/color]
          ?public void updateBackground() {
          ??try {
          ???Robot rbt = new Robot();
          ???Toolkit tk = Toolkit.getDefaultToolkit();
          ???Dimension dim = tk.getScreenSize();
          ???background = rbt.createScreenCapture(new Rectangle(0, 0, (int) dim
          ?????.getWidth(), (int) dim.getHeight()));
          ??} catch (Exception ex) {
          ???// p(ex.toString());
          ???// 此方法沒有申明過 ,因為無法得知上下文 。因為不影響執行效果 ,先注釋掉它 ex.printStackTrace();
          ??}

          ?}

          ?????????????????? [color=Red]? //將窗口掉離出屏幕以獲得純粹的背景圖象[/color]
          ?public void refresh() {
          ??if (start == true) {
          ???this.updateBackground();
          ???frame.setLocation(p);
          ???if (p.x < 0 || p.y < 0)
          ????frame.setLocation(0, 0);
          ???this.repaint();
          ??}
          ?}

          ?public void componentHidden(ComponentEvent e) {
          ??// TODO Auto-generated method stub
          ??System.out.println("Hidden");
          ?}

          ???????????????????? [color=Red] //窗口移動時[/color]
          ?public void componentMoved(ComponentEvent e) {
          ??// TODO Auto-generated method stub
          ??System.out.println("moved");
          ??this.repaint();
          ?}

          ??????????????????? [color=Red] //窗口改變大小時[/color]
          ?public void componentResized(ComponentEvent e) {
          ??// TODO Auto-generated method stub
          ??System.out.println("resized");
          ??this.repaint();
          ?}

          ?public void componentShown(ComponentEvent e) {
          ??// TODO Auto-generated method stub
          ??System.out.println("shown");
          ?}

          ??????????????????? [color=Red] //窗口得到焦點后,用refresh()方法更新界面[/color]
          ?public void windowGainedFocus(WindowEvent e) {
          ??// TODO Auto-generated method stub
          ??System.out.println("gainedFocus");
          ??refresh();
          ??start = false;
          ?}

          ??????????????????? [color=Red] //窗口失去焦點后,將其移出屏幕[/color]
          ?public void windowLostFocus(WindowEvent e) {
          ??// TODO Auto-generated method stub
          ??System.out.println("lostFocus");
          ??if (frame.isShowing() == true) {
          ???System.out.println("visible");
          ??} else {
          ???System.out.println("invisible");
          ??}
          ??start = true;
          ??p = frame.getLocation();
          ??frame.setLocation(-2000, -2000);
          ?}

          ?public TestEvent(JFrame frame) {
          ??super();
          ??this.frame = frame;
          ??updateBackground();
          ??this.setSize(200, 120);
          ??this.setVisible(true);
          ??frame.addComponentListener(this);
          ??frame.addWindowFocusListener(this);

          ??// TODO Auto-generated constructor stub
          ?}

          ??????????????????? //繪制外觀,注意,其中 pos,offset 是為了將特定部分的圖象貼到窗口上
          ?public void paintComponent(Graphics g) {
          ??Point pos = this.getLocationOnScreen();
          ??Point offset = new Point(-pos.x, -pos.y);
          ??g.drawImage(background, offset.x, offset.y, null);
          ?}

          ?/**
          ? * @param args
          ? */
          ?public static void main(String[] args) {
          ??// TODO Auto-generated method stub
          ??try {
          ???// UIManager.setLookAndFeel("org.fife.plaf.Office2003.Office2003LookAndFeel");
          ???// UIManager.setLookAndFeel("org.fife.plaf.OfficeXP.OfficeXPLookAndFeel");
          ???// UIManager.setLookAndFeel("org.fife.plaf.OfficeXP.OfficeXPLookAndFeel");
          ???UIManager.setLookAndFeel(new SubstanceLookAndFeel());
          ???//UIManager.setLookAndFeel(new SmoothLookAndFeel());
          ???//UIManager.setLookAndFeel(new QuaquaLookAndFeel());
          ???UIManager.put("swing.boldMetal", false);
          ???if (System.getProperty("substancelaf.useDecorations") == null) {
          ????JFrame.setDefaultLookAndFeelDecorated(true);
          ????//JDialog.setDefaultLookAndFeelDecorated(true);
          ???}
          ???System.setProperty("sun.awt.noerasebackground", "true");
          ???SubstanceLookAndFeel.setCurrentTheme(new SubstanceLightAquaTheme());

          ???// UIManager.setLookAndFeel("org.fife.plaf.VisualStudio2005.VisualStudio2005LookAndFeel");
          ??} catch (Exception e) {
          ???System.err.println("Oops!? Something went wrong!");
          ??}

          ??JFrame frame = new JFrame("Transparent Window");
          ??TestEvent t = new TestEvent(frame);
          ??t.setLayout(new BorderLayout());
          ??JButton button = new JButton("This is a button");
          ??t.add("North", button);
          ??JLabel label = new JLabel("This is a label");
          ??t.add("South", label);
          ??frame.getContentPane().add("Center", t);
          ??frame.pack();
          ??frame.setSize(150, 100);
          ??frame.show();
          ??frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          ??// t.start=true;
          ?}

          }


          當然,以上代碼還存在許多不足,比如在移動窗口時性能會有影響,應該可以通過線程來控制其更新的頻率來提升效率,希望大家能一起研究一下,改好了記得通知我哦

          ps:你也許還會說,e?怎么沒講任意形狀窗口如何打造?
          ?????? 呵呵,其實只要把窗口按上述方法設置成透明,再去掉邊框,再換上自己的邊框,不就完成了馬?
          ???? 相信聰明的各位一定很容易就辦到咯

          另外,我還在下面附帶了作者的大論,想了解更多的同學也可以去看看

          http://www.matrix.org.cn/resource/article/44/44186_Swing.html

          posted on 2006-04-03 18:15 dyerac in java... 閱讀(8967) 評論(13)  編輯  收藏 所屬分類: 原創文章JavaSE
          評論:
          • # re: 用java打造任意形狀窗口和透明窗口  nake Posted @ 2006-04-03 20:32
            不錯
              回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  fanse Posted @ 2006-04-05 00:19
            好文章,我要充電了

            歡迎訪問 http://www.shuangzixing.net 雙子星Java開源技術門戶  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  jazzy Posted @ 2006-04-06 16:21
            呵呵,用SWT實現就很簡單啦  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  -_=~~ Posted @ 2006-08-07 11:54
            不錯不錯,多謝

            swt....作者不是要跨平臺么..hoho  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  Tony[匿名] Posted @ 2006-12-04 23:19
            很感謝您發表自己的感想

            其實這是沒用的小技巧
            試想:使用者不可能不去移動這個視窗,一移動,就露馬腳了

            很久以前我用過這個技巧
            卻在內部RD就被否決掉

            原因就在:無法預測試用者的操作方式  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  dyerac in java... Posted @ 2006-12-05 12:38
            @Tony[匿名]
            不啊,移動起來是沒有問題的
            只是在隱藏后再顯示時會出現一個瞬移的現象,但是基本上用戶是注意不到的  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  pc Posted @ 2007-09-20 11:47
            看到你的src里有一行
            //UIManager.setLookAndFeel(new QuaquaLookAndFeel());
            這個是windows下的L&F嗎?
            能不能給我發一份?hongmocai at 126.com
            Thank you.  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  ki_bum Posted @ 2008-02-24 17:56
            偶..最近..也在看Hack..
            但..L&F..MS講的有點少...
            呵~~~
            只有一個...小問題...
            如果...是雙屏顯示器(3屏..4屏~~~)
            在非主屏上...
            程序..就會認為是windowLostFocus
            就會顯示L&F的background(==!)  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  evilz Posted @ 2009-02-13 14:28
            哈哈,這個東西我曾經也實現過。樓上某位說不能移動,這個很好解決,自寫窗體事件,它只要一動,馬上重新截屏重新繪制窗體。  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  hello Posted @ 2009-05-13 21:10
            題目說的太大了。。你只是做了“透明”效果。。但可沒有做到使窗體呈任意形狀。。。我就沖這個任意形狀來的。。。結果。。。。。。  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  dyerac Posted @ 2009-05-14 14:36
            @hello
            仔細看完再說咯:
            ------
            ps:你也許還會說,e?怎么沒講任意形狀窗口如何打造?
            呵呵,其實只要把窗口按上述方法設置成透明,再去掉邊框,再換上自己的邊框,不就完成了馬?
            相信聰明的各位一定很容易就辦到咯  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  第一次 Posted @ 2009-06-21 01:21
            我很少留言的,這次如果不說聲謝謝我都過意不去了!  回復  更多評論   

          • # re: 用java打造任意形狀窗口和透明窗口  月下幽杳 Posted @ 2013-06-05 12:35
            根本沒必要搞這么復雜,
            this.setUndecorated(true);
            AWTUtilities.setWindowOpaque(this, false);
            然后調用一個JLabel來顯示圖片(png部分透明圖片),然后作為底層容器,可以實現任意窗口  回復  更多評論   

           
          Copyright © dyerac in java... Powered by: 博客園 模板提供:滬江博客
          主站蜘蛛池模板: 浮梁县| 隆子县| 定州市| 凌云县| 兴业县| 万载县| 石河子市| 东乡县| 囊谦县| 卓资县| 揭阳市| 红原县| 宁夏| 伊川县| 衡南县| 连州市| 宜宾县| 独山县| 龙州县| 吉首市| 莱州市| 海伦市| 柳林县| 淳化县| 稻城县| 贵溪市| 阿拉善右旗| 海宁市| 扶风县| 成武县| 耿马| 阳谷县| 江华| 衡山县| 新闻| 常宁市| 普定县| 涞水县| 锡林浩特市| 平昌县| 桐梓县|