<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    小魚的空氣

    記錄我所思

    用java打造任意形狀窗口和透明窗口



    圖形界面開發對于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 com.birosoft.liquid.LiquidLookAndFeel;

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

    ?private boolean start = false;

    ?private Image background;

    ?private Point p;

    ?// 獲得當前屏幕快照
    ?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();
    ??}

    ?}

    ?// 將窗口掉離出屏幕以獲得純粹的背景圖象
    ?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) {
    ??System.out.println("Hidden");
    ?}

    ?// 窗口移動時
    ?public void componentMoved(ComponentEvent e) {
    ??System.out.println("moved");
    ??this.repaint();
    ?}

    ?// 窗口改變大小時
    ?public void componentResized(ComponentEvent e) {
    ??System.out.println("resized");
    ??this.repaint();
    ?}

    ?public void componentShown(ComponentEvent e) {
    ??System.out.println("shown");
    ?}

    ?// 窗口得到焦點后,用refresh()方法更新界面
    ?public void windowGainedFocus(WindowEvent e) {
    ??System.out.println("gainedFocus");
    ??refresh();
    ??start = false;
    ?}

    ?// 窗口失去焦點后,將其移出屏幕
    ?public void windowLostFocus(WindowEvent e) {
    ??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);

    ?}

    ?// 繪制外觀,注意,其中 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) {
    ??try {
    ???// UIManager.setLookAndFeel("org.fife.plaf.Office2003.Office2003LookAndFeel");
    ???// UIManager.setLookAndFeel("org.fife.plaf.OfficeXP.OfficeXPLookAndFeel");
    ???// UIManager.setLookAndFeel("org.fife.plaf.OfficeXP.OfficeXPLookAndFeel");
    ???UIManager.setLookAndFeel("com.birosoft.liquid.LiquidLookAndFeel");
    ???LiquidLookAndFeel.setLiquidDecorations(true);
    ???// LiquidLookAndFeel.setLiquidDecorations(true, "mac");
    ???// 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-10-23 17:03 小魚 閱讀(991) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導航

    統計

    常用鏈接

    留言簿(3)

    我參與的團隊

    隨筆檔案

    文章檔案

    搜索

    最新評論

    主站蜘蛛池模板: 亚洲性无码av在线| 国产亚洲综合成人91精品 | 亚洲国产黄在线观看| 亚洲AV成人无码网站| 日本一道在线日本一道高清不卡免费| 亚洲成人福利网站| 天天影视色香欲综合免费| 亚洲色欲或者高潮影院| 91九色老熟女免费资源站| 亚洲人成在线免费观看| 我要看WWW免费看插插视频| 亚洲欧美在线x视频| 亚洲国产婷婷香蕉久久久久久| xvideos永久免费入口| 亚洲级αV无码毛片久久精品| 久久久久成人精品免费播放动漫| 亚洲av无码不卡一区二区三区| 精品无码无人网站免费视频| 亚洲国产成人精品激情| 永久免费看bbb| 国产黄色片免费看| 亚洲尹人九九大色香蕉网站 | 国内大片在线免费看| 免费在线观看自拍性爱视频| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 日韩一区二区免费视频| 一本大道一卡二大卡三卡免费| 亚洲av无码一区二区三区网站| 国产成人福利免费视频| 337P日本欧洲亚洲大胆艺术图| 亚洲无人区一区二区三区| 永久黄色免费网站| 天天综合亚洲色在线精品| 国产成人精品日本亚洲| 色婷婷7777免费视频在线观看| 国产精品亚洲专一区二区三区| 亚洲国产精品成人久久| 大陆一级毛片免费视频观看i| 福利免费在线观看| 亚洲一卡二卡三卡四卡无卡麻豆| 亚洲AV无码一区二区三区在线观看|