本站首页    管理页面    写新日志    退出


«August 2025»
12
3456789
10111213141516
17181920212223
24252627282930
31


公告
本博客在此声明所有文章均为转摘,只做资料收集使用。并无其他商业用途。

我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:
日志总数:210
评论数量:205
留言数量:-19
访问次数:918797
建立时间:2007年5月10日




[界面和模板语言]用java打造任意形状窗口和透明窗口
文章收藏,  网上资源,  软件技术,  电脑与网络

李小白 发表于 2007/7/19 10:05:13

  lhwork 发表于 2006-10-23 9:58:31 先把以前写的转过来,呵呵 500)this.width=500'> 图形界面开发对于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


阅读全文(2685) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.219 second(s), page refreshed 144783071 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号