`
sunxiang0918
  • 浏览: 46892 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

采用屏幕快照伪实现组件半透明

    博客分类:
  • J2SE
阅读更多

半透明窗口是大众对Swing最为渴求的特性之一. 也可以称之为定形窗口,这种窗口有一部分是透明的,可以透过它看到桌面背景和其它的程序.如果不通过JNI(Java Native Interface 本地接口)Java是无法为我们生成一个半透明的窗口的(即使我们可以那样做,还得本地操作平台好支持半透明窗口才行).然而这些现状无法阻止我们对半透明窗口的渴求,通过一个我最喜欢的手段screenshot,我们可以欺骗性地实现这个目的.

仿造这样一个的半透明窗口的过程,主要的通过以下几点:
1.在窗口显示之前,先获得一个screenshot;
2.把上一步获取的屏幕快照,作为窗口的背景图
3.调整位置,以便于我们捕获的screenshot和实际当前的屏幕完美结合,制造出一种半透明的假象.

刚刚说到的部分只是小儿科,重头戏在于,如何在移动或变化半透明窗口时,及时地更新screenshot,也就是及时更新半透明窗口的背景.

在开始我们的旅行之前,先生成一个类,让它继承 JPanel,我们用这个继承类来捕获屏幕,并把捕获的照片作为背景. 类的具体代码如下例6-1

例 6-1 。 半透明背景组件

public class TransparentBackground extends Jcomponent {
    private JFrame frame;
    private Image background;

public TransparentBackground(JFrame frame) {
    this.frame = frame;
    updateBackground( );
}
/**
  * @todo 获取屏幕快照后立即更新窗口背景
  */
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 paintComponent(Graphics g) {
    Point pos = this.getLocationOnScreen( );
    Point offset = new Point(-pos.x,-pos.y);
    g.drawImage(background,offset.x,offset.y,null);
}
}
 


首先,构造方法把一个reference保存到父的JFrame,然后调用updateBackground()方法,在这个方法中,我们可以利用 java.awt.Robot类捕获到整个屏幕,并把捕获到的图像保存到一个定义了的放置背景的变量中. paintComponent()方法可以帮助我们获得窗口在屏幕上的绝对位置,并用刚刚得到的背景作为panel的背景图,同时这个背景图会因为 panel位置的不同而作对应的移动,以使panel的背景和panel覆盖的那部分屏幕图像无缝重叠在一起,同时也就使panel和周围的屏幕关联起来.

我们可以通过下面这个main方法简单的运行一下,随便放置一些组件到panel上,再把panel放置到frame中显示.

public static void main(String[] args) {
    JFrame frame = new JFrame("Transparent Window");
    TransparentBackground bg = new TransparentBackground(frame);
    bg.setLayout(new BorderLayout( ));
    JButton button = new JButton("This is a button");
    bg.add("North",button);
        JLabel label = new JLabel("This is a label");
    bg.add("South",label);
    frame.getContentPane( ).add("Center",bg);
    frame.pack( );
    frame.setSize(150,100);
    frame.show( );
}
 


通过这段代码,运行出的效果如下图6-1所示:
图6-1 展示中的半透明窗口

这段代码相当简单,却带有两个不足之处。首先,如果移动窗口,panel中的背景无法自动的更新,而paintComponent()只在改变窗口大小时被调用;其次,如果屏幕曾经发生过变化,那么我们制作的窗口将永远无法和和屏幕背景联合成整体。

谁也不想时不时地跑去更新screenshot,想想看,要找到隐藏于窗口后的东西,要获得一份新的screenshot,还要时不时的用这些 screenshot来更新我们的半透明窗口,这些事情足以让用户无法安心工作。事实上,想要获取窗口之外的屏幕的变化几乎是不太可能的事,但多数变动都是发生在foreground窗口发生焦点变化或被移动之时。如果你接受这的观点(至少我接受这个观点),那么你可以只监控下面提到的几个事件,并只需在这几个事件被触发时,去更新screenshot。

public class TransparentBackground extends JComponent
        implements ComponentListener, WindowFocusListener,
        Runnable {
    private JFrame frame;
    private Image background;
    private long lastupdate = 0;
    public boolean refreshRequested = true;
    public TransparentBackground(JFrame frame) {
        this.frame = frame;
        updateBackground( );
        frame.addComponentListener(this);
        frame.addWindowFocusListener(this);
        new Thread(this).start( );
    }
    public void componentShown(ComponentEvent evt) { repaint( ); }
    public void componentResized(ComponentEvent evt) { repaint( ); }
    public void componentMoved(ComponentEvent evt) { repaint( ); }
    public void componentHidden(ComponentEvent evt) { }

    public void windowGainedFocus(WindowEvent evt) { refresh( ); }   
    public void windowLostFocus(WindowEvent evt) { refresh( ); }
 


首先,让我们的半透明窗口即panel实现ComponentListener接口,
WindowFocusListener接口和Runnable接口。Listener接口可以帮助我们捕获到窗口的移动,大小变化,和焦点变化。实现Runnable接口可以使得panel生成一个线程去控制定制的repaint()方法。

ComponentListener接口带有四个component开头的方法。它们都可以很方便地调用repaint()方法,所以窗口的背景也就可以随着窗口的移动,大小的变化而相应地更新。还有两个是焦点处理的,它们只调用refresh(),如下示意:

public void refresh( ) {
    if(frame.isVisible( )) {
        repaint( );
        refreshRequested = true;
        lastupdate = new Date( ).getTime( );
    }
}
public void run( ) {
    try {
        while(true) {
            Thread.sleep(250);
            long now = new Date( ).getTime( );
            if(refreshRequested &&
                ((now - lastupdate) > 1000)) {
                if(frame.isVisible( )) {
                    Point location = frame.getLocation( );
                    frame.hide( );
                    updateBackground( );
                    frame.show( );
                frame.setLocation(location);
                    refresh( );
                }
                lastupdate = now;
                refreshRequested = false;
                }
            }
        } catch (Exception ex) {
            p(ex.toString( ));
            ex.printStackTrace( );
        }
    } 
 


refresh()可以保证frame可见,并适时得调用repaint()。它也会对refreshRequest变量置真(true),同时保存当前时间值,现在所做的这些对接下来要做的事是非常重要的铺垫。

除了每四分之一秒被唤醒一次,用来检测是否有新的刷新的要求或者是否离上次刷新时间超过了一秒,方法run()一般地处于休眠状态。如果离上次刷新超过了一秒并且frame是可见的,那么run()将保存frame的位置,隐藏frame,获取一个screenshot,更新frame背景,再根据隐藏 frame时保存的位置信息,重新显示已经更新了背景的frame,接着调用refresh()方法。通过这样的控制,使得背景更新不至于比需要的多太多。

那么我们为什么要对用一个线程控制刷新如此长篇大论呢?一个词:递归。事件处理可以直接轻松地调用repaint(),但是隐藏和显示窗口已便于获取screenshot 却交替了很多“得焦”和“失焦”事件。所有这些都会触发一个新的背景更新,导致窗口再次被隐藏,如此往返,将导致永无止境的循环。一个新的“得焦”事件,将在执行refresh()几毫秒之后被调用,所以简单地检测isRecursing标志是无法阻止循环的继续。

另外,用户任意一个改变屏幕的动作,将会随之引出一堆的事件来,而不仅仅是简单一个。应该是最后一个事件去触发updateBackground(),而不是第一个。为了全面解决这些问题,代码产生一个线程,然后用这个线程去监控重画(repaint)要求,并保证当前的执行动作是发生在过去的1000毫秒内没有发生过此动作。如果一个客户每五秒不间断地产生事件(比如,寻找丢失的浏览窗口),那么只有在其它所有工作在一秒内完成才执行更新。这样就避免了,用户不至于在移动东西时,窗口却消失不见了的尴尬。

另一件烦恼的事就是,我们的窗口仍旧有边框,这条边框使得我们无法完美和背景融为一体。更为痛苦的是使用setUndecorated(true)移除边框时,我们的标题栏和窗口控制栏也跟着移除了。可是这也算不上是什么大问题,因为那类使用定形窗口的应用程序一般都具有可拖动的背景【Hack#34】

接下来,我们在下面这个简单的测试程序中把所讲的东西落实进去:

public static void main(String[] args) {
    JFrame frame = new JFrame("Transparent Window");
    frame.setUndecorated(true);
   
    TransparentBackground bg = new TransparentBackground(frame);
    bg.snapBackground( );
    bg.setLayout(new BorderLayout( ));

   JPanel panel = new JPanel( ) {
        public void paintComponent(Graphics g) {
            g.setColor(Color.blue);
            Image img = new ImageIcon("mp3.png").getImage( );
            g.drawImage(img,0,0,null);
        }
    };
    panel.setOpaque(false);

    bg.add("Center",panel);

    frame.getContentPane( ).add("Center",bg);
    frame.pack( );
    frame.setSize(200,200);
    frame.setLocation(500,500);
    frame.show( );
}
 


这段代码通过继承JPanel,加上一个透明的PNG格式图片,人工生成一个mp3播放器界面。注意使用了setUndecorated()来隐藏边框和标题栏。调用setOpaque(false),将隐藏默认的背景(一般为灰色),这样screenshot的背景就可以和图片中透明的部分合成一个整体,去配合程序窗口周围的屏幕背景。(如图6-2)通过一系列的努力,就可以看到图6-3的效果。是不是很让人惊诧?会不会感叹Java的新版本腾空出世?

分享到:
评论

相关推荐

    C++ Dump 内存快照的实现

    本篇文章将详细介绍如何在C++中实现内存快照,并通过分析dump文件来辅助调试。 首先,理解什么是内存快照。内存快照是程序在特定时间点的内存状态记录,包括已分配的内存块、它们的大小、分配位置以及相关联的信息...

    Java实现本机屏幕监控+源码

    首先,我们要理解Java中实现屏幕监控的关键技术。这通常涉及到Java的图形用户界面(GUI)组件和图像处理库。Java提供了丰富的API,如Java AWT(Abstract Window Toolkit)和Swing,用于创建和操作图形界面。对于屏幕...

    易语言源码易语言屏幕快照源码.rar

    3. **屏幕捕获技术**:实现屏幕快照的关键在于获取屏幕上的像素信息。易语言提供了相应的API调用来完成这一任务,如Windows API中的`BitBlt`函数,可以用来复制屏幕上的某个矩形区域到内存中的位图。 4. **图像处理...

    java 实现整张网页快照

    通过标题"java 实现整张网页快照"和描述,我们可以推断这个项目是通过Java中的JWebBrowser库来完成这个任务的,它能够捕获整个网页,而不是仅仅局限于屏幕可见部分。 首先,我们需要了解`JWebBrowser`组件。这是一...

    IP-guard和Ping32实时屏幕快照对比.docx

    然而,在屏幕快照的实时性上,IP-guard与Ping32存在差异,IP-guard的屏幕快照获取有间隔,而Ping32能够实时获取,无间隔。 两款软件在屏幕监控上的差异主要体现在实时性和操作便利性上。Ping32强调实时性和多任务...

    易语言屏幕快照

    易语言屏幕快照源码的实现原理可能包括以下几个关键步骤: 1. **捕获屏幕图像**:程序会调用系统API函数,如Windows API中的`BitBlt`或`StretchBlt`,来复制屏幕的显示内容到一个内存缓冲区,形成位图数据。 2. **...

    TIA博途中使用DB数据块的快照功能实现数据传递的具体方法.docx

    ### TIA博途中使用DB数据块的快照功能实现数据传递的具体方法 #### 一、引言 在工业自动化领域,TIA Portal(Totally Integrated Automation Portal)是西门子推出的一款集成化软件平台,旨在简化工程设计流程,...

    易语言API快照源码(带鼠标快照)

    然后,在程序运行时,通过具体的参数调用这些函数来实现屏幕快照。 在易语言代码大全中,可能会有详细的注解和示例,解释如何正确地使用这些API函数,包括如何创建位图对象、如何获取屏幕DC、如何调用`BitBlt`进行...

    易语言-易语言实现屏幕快照

    在本项目"易语言-易语言实现屏幕快照"中,我们关注的是如何利用易语言来实现屏幕截图的功能。屏幕快照,即截取电脑屏幕上当前显示的内容并保存为图片文件,是日常计算机操作中非常实用的一项功能。 易语言屏幕快照...

    matlab开发-屏幕捕获获取组件图的屏幕捕获

    "matlab开发-屏幕捕获获取组件图的屏幕捕获"这个主题就涉及到了如何在MATLAB环境中实现这一功能。在MATLAB中,可以利用内置函数或者自定义脚本来捕获屏幕快照,特别是对于GUI句柄或特定屏幕区域。 MATLAB提供了一个...

    VC实现屏幕变暗效果

    在VC++(Visual C++)编程环境中,实现屏幕变暗效果通常涉及到Windows API的使用,尤其是与图形设备接口(GDI)和用户界面相关的函数。屏幕变暗效果可以通过捕获屏幕快照,然后在其上应用灰度滤镜或降低整体亮度来...

    修改Mac屏幕快照的格式方法.docx

    在Mac操作系统中,屏幕快照是一种非常实用的功能,它允许用户快速捕捉屏幕上的任何内容并保存为图片文件。默认情况下,Mac的屏幕快照会保存为PNG格式,但根据个人需求,有时可能需要将快照格式更改为更小巧或更适合...

    Flex调用JavaServlet将组件快照导出成图片

    在本场景中,我们将讨论如何在Flex中捕获组件的快照,将其转换为ByteArray,并通过JavaServlet将其导出为图片。 首先,让我们深入了解Flex中的组件快照。在Flex中,我们可以使用BitmapData类来捕获组件的视觉表示,...

    【Android 组件化】使用 Gradle 实现组件化 ( 组件模式与集成模式切换 )

    【Android 组件化】使用 Gradle 实现组件化 ( 组件模式与集成模式切换 ) https://hanshuliang.blog.csdn.net/article/details/116810389 博客源码快照

    半透明ViewController

    为了实现半透明,我们需要将这个背景颜色设置为一种带有透明度的颜色,例如浅灰色,并将Alpha值设为小于1的数值,如0.5,这样就创建了一个基本的半透明背景。 然而,仅仅改变背景色并不能达到“玻璃效果”的模糊...

    PHP抓屏函数实现屏幕快照代码分享

    本文将详细介绍如何通过PHP编程语言来实现屏幕快照的功能,并分享相关的代码片段。 PHP是一种广泛应用于服务器端的脚本语言,它能够生成动态网页内容,并处理各种形式的数据。PHP的图形处理能力虽然不及专门的图形...

    Android获取webView快照与屏幕截屏的方法

    在这个活动中,你可以集成上述代码,实现获取WebView快照和屏幕截图,并展示或保存结果。 总结来说,Android应用中获取WebView快照和屏幕截图主要涉及到WebView的绘制缓存、屏幕尺寸的获取以及Bitmap的处理。通过...

    【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 )

    【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 ) https://hanshuliang.blog.csdn.net/article/details/116845118 博客源码快照

    e语言-易语言API快照源码(带鼠标快照)

    在“易语言API快照源码”中,我们主要探讨的是如何利用易语言调用操作系统级别的API函数来实现屏幕快照功能,即捕获电脑屏幕上当前显示的画面并保存为图像文件。 屏幕快照功能广泛应用于各种软件中,例如屏幕录制、...

    452328屏幕取词完全Delphi实现代码

    这涉及到Delphi的VCL组件,如`TForm`和`TMemo`,以及自定义绘制技巧来实现半透明效果和动态定位。 总结一下,这个"452328屏幕取词完全Delphi实现代码"项目涵盖了以下关键知识点: 1. Delphi编程基础,包括对象 ...

Global site tag (gtag.js) - Google Analytics