网站首页
JSP空间
动态资讯
开源项目
技术文档
资源下载
J2EE资源
客户论坛
在线支付
 
  技术文档>>JAVA>>新手入门>>基础入门>查看文档  
  编写一个基于java robot类的屏幕捕获工具     
  文章作者:未知  文章来源:水木森林  
  查看:80次  录入:管理员--2007-11-17  
 

    java fun and games(java娱乐和游戏)提供了通过java的robot类捕获主屏幕设备的功能,并且可以将整个屏幕或者选定的一部分保存为jpeg文件。这篇文章以swing应用的形式实现了屏幕捕获工具。

    java fun and games(java娱乐和游戏)提供了通过java的robot类捕获主屏幕设备的功能,并且可以将整个屏幕或者选定的一部分保存为jpeg文件。

    注意:现在你可以使用在线开发工具devsquare编译和运行java fun and games中提供的applet。devsquare入门请阅读资源中提供的用户向导。

    java.awt.robot类为娱乐功能提供了一些有用的方法。其中一个包括了建立屏幕捕获工具的功能。java fun and games给出了一个使用robot捕获主屏幕设备内容的工具。

    这一部分从我以前的几部分中分离出来了,因为它并不是集中在applet实现上。这篇文章以swing应用的形式实现了屏幕捕获工具。从gui观点介绍完这个应用之后,我将解释实现的关键部分。

版权声明:任何获得matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:jeff friesen;mydeman
原文:http://www.javaworld.com/javaworld/jw-04-2006/jw-0424-funandgames.html
matrix:http://www.matrix.org.cn/resource/article/2006-09-15/java+robot_f9598e5e-445b-11db-af0b-0f766c077b58.html
关键字:java robot;捕获屏幕

应用程序gui
    我的capture程序提供了一个图形用户界面(gui,graphic user interface),通过它你可以选择捕获图像的一部分,修剪图像到选择内容,以及将结果图像保存为jpeg文件。图1显示了包含一个捕获示例的capture的gui。

编写一个基于java robot类的屏幕捕获工具(图一)
图 1. 红白相间的虚线所形成的矩形表示了当前选中的区域

    capture的gui由菜单栏和显示捕获图像的可滚动窗口组成。如图1所示,选择矩形(通过拖拽鼠标)表示了捕获图形的一个矩形区域。

    菜单栏提供了file和capture菜单:
---file提供save as…(另存为)和exit(退出)菜单项,可以通过文件选择器保存当前捕获为一个jpeg文件,和退出capture。尽管你可以直接选择这些菜单项,但是你会发现使用它们的快捷键alt-s和alt-x会更加方便。
---capture提供capture(捕获)和crop(修剪)菜单项,可以捕获当前主屏幕设备的内容和修剪一个图像为选择矩形的内容。和file菜单项一样,这些菜单项也有它们自己的方便的快捷键:capture(alt-c)和crop(alt-k)。

应用实现

    有三个源文件来描述capture的gui:capture.java(启动应用程序和构造gui)、imagearea.java( 描述了一个用来显示捕获的内容的组件,你也可以在其中选择捕获的一部分或修剪捕获的内容)和imagefilefilter.java(限制文件选择器的选择是文件夹和jpeg文件)。在这一部分下面,我从这些源文件中摘录了一些代码片断来说明capture的工作过程。

机器人屏幕捕获
    为了使用robot类捕获屏幕,capture必须先创建一个robot对象。capture类的public static void main(string [] args)方法尝试调用robot的public robot()构造函数来创建这个对象。如果创建成功,就会返回一个针对主屏幕设备坐标系的robot引用。如果平台不支持低级控制(在没有屏幕设备的环境这是成立的),将会抛出java.awt.awtexception。如果平台不允许创建robot对象就会抛出java.lang.securityexception。但愿你不会再遇到其他异常。

    假设robot对象已被创建,main()调用capture类的构造函数创建一个gui。作为gui创建的一部分,capture通过调用dimscreensize = toolkit.getdefaulttoolkit().getscreensize();获得主屏幕设备的尺寸。因为用来显示屏幕捕获的内容的robot的public bufferedimage createscreencapture(rectangle screenrect)方法,需要一个java.awt.rectangle参数,所以构造函数通过rectscreensize = new rectangle(dimscreensize);将java.awt.dimension对象转换为一个rectangle对象。当capture菜单项的动作监听器被调用时,下面摘录的capture.java片断就会调用createscreencapture()。

// hide capture's main window so that it does not appear in
// the screen capture.

setvisible (false);

// perform the screen capture.

bufferedimage biscreen;
biscreen = robot.createscreencapture (rectscreensize);

// show capture's main window for continued user interaction.

setvisible (true);

// update imagearea component with the new image and adjust
// the scrollbars.

ia.setimage (biscreen);

jsp.gethorizontalscrollbar ().setvalue (0);
jsp.getverticalscrollbar ().setvalue (0);



    你不希望capture的gui遮住你想要捕获的任何内容。这就是为什么代码中隐藏capture gui优先级高于完成捕获。在获取了包含屏幕像素copy的java.awt.image.bufferedimage后,代码片断显示出gui,并且通过图像区域组件显示出bufferedimage的内容。

子图像选择
    当从一个捕获的图像中获取子图像时需要一个选择矩形。imagearea类提供代码来创建、操作和绘制选择矩形。如下面摘录的imagearea.java所示,这个类的构造函数以一个rectangle实例创建选择矩形,创建java.awt.basicstoke和java.awt.gradientpaint对象定义了矩形的轮廓外观(保持它与底层图像分离),注册鼠标和鼠标动作监听器让你能够操作选择矩形。

// create a selection rectangle. it's better to create o­ne rectangle
// here than a rectangle each time paintcomponent() is called, to reduce
// unnecessary object creation.

rectselection = new rectangle ();

// define the stroke for drawing selection rectangle outline.

bs = new basicstroke (5, basicstroke.cap_round, basicstroke.join_round,
                      0, new float [] { 12, 12 }, 0);

// define the gradient paint for coloring selection rectangle outline.

gp = new gradientpaint (0.0f, 0.0f, color.red, 1.0f, 1.0f, color.white,
                        true);

// install a mouse listener that sets things up for a selection drag.

mouselistener ml;
ml = new mouseadapter ()
     {
         public void mousepressed (mouseevent e)
         {
            // when you start capture, there is no captured image.
            // therefore, it makes no sense to try and select a sub-image.
            // this is the reason for the if (image == null) test.

            if (image == null)
                return;

            destx = srcx = e.getx ();
            desty = srcy = e.gety ();

            repaint ();
         }
     };
addmouselistener (ml);

// install a mouse motion listener to update the selection rectangle
// during drag operations.

mousemotionlistener mml;
mml = new mousemotionadapter ()
      {
          public void mousedragged (mouseevent e)
          {
             // when you start capture, there is no captured image.
             // therefore, it makes no sense to try and select a
             // sub-image. this is the reason for the if (image == null)
             // test.

             if (image == null)
                 return;

             destx = e.getx ();
             desty = e.gety ();

             repaint ();
          }
      };
addmousemotionlistener (mml);


    当按下鼠标时,鼠标事件处理器对相同的横向鼠标坐标设置destx和srcx,对于纵向鼠标坐标亦是如此。源变量和目标变量同样表示哪些显示的选择矩形应该被移除了。它通过调用repaint(),导致public void paintcomponent(graphics g)被调用。这个方法将srcx和srcy分别与destx和desty相比较,如果他们不同,就绘制一个选择矩形:
// draw the selection rectangle if present.

if (srcx != destx || srcy != desty)
{
    // compute upper-left and lower-right coordinates for selection
    // rectangle corners.

    int x1 = (srcx < destx) ? srcx : destx;
    int y1 = (srcy < desty) ? srcy : desty;

    int x2 = (srcx > destx) ? srcx : destx;
    int y2 = (srcy > desty) ? srcy : desty;

    // establish selection rectangle origin.

    rectselection.x = x1;
    rectselection.y = y1;

    // establish selection rectangle extents.

    rectselection.width = (x2-x1)+1;
    rectselection.height = (y2-y1)+1;

    // draw selection rectangle.

    graphics2d g2d = (graphics2d) g;
    g2d.setstroke (bs);
    g2d.setpaint (gp);
    g2d.draw (rectselection);
}


    在选择矩形绘制以前,它的左上和右下角必须对标示出来,用来确定矩形的原点和范围。以至于你可以在不同的方向拖拽出选择矩形(例如右下或者左上方向),srcx/destx和srcy/desty的最小值表示左上角,相似地,它们的最大值表示右下角。

图像修剪
    在选择子图像后,你想要修剪捕获的图像得到子图像。图像修剪启动crop中的菜单项的动作监听器,它请求imagearea将捕获的图像修剪为选择的子图像。若操作成果,监听器则重置imagearea的滚动条。反之,监听器通过对话框给出一个“out of bounds”错误信息。
// crop imagearea component and adjust the scrollbars if
// cropping succeeds.

if (ia.crop ())
{
    jsp.gethorizontalscrollbar ().setvalue (0);
    jsp.getverticalscrollbar ().setvalue (0);
}
else
    showerror ("out of bounds.");



    因为修剪操作不重置capture gui的大小,所以可以同时看到主窗口的背景和结果图像(初始修剪后的)。图2显示了选择图像的一部分时还可能选中背景的一部分。

编写一个基于java robot类的屏幕捕获工具(图二)
图 2. 尝试选择多于这个图像

    主窗口的背景像素不是捕获的图像的一部分;就不可能把它们包含在修剪的图片内。因此,无论何时把背景像素包含在修剪区域内,操作都会失败,并且会给出一个“out of bounds”错误信息。

    修剪操作由imagearea的public boolean crop()方法处理。如果完成了修剪或者没有选择子图像(当没有选中内容时调用这个方法是非常方便的)该方法(如下所示)返回true。如果在选择区域中包含了背景像素则返回false。

public boolean crop ()
{
   // there is nothing to crop if the selection rectangle is o­nly a single
   // point.

   if (srcx == destx && srcy == desty)
       return true;

   // assume success.

   boolean succeeded = true;

   // compute upper-left and lower-right coordinates for selection rectangle
   // corners.

   int x1 = (srcx < destx) ? srcx : destx;
   int y1 = (srcy < desty) ? srcy : desty;

   int x2 = (srcx > destx) ? srcx : destx;
   int y2 = (srcy > desty) ? srcy : desty;

   // compute width and height of selection rectangle.

   int width = (x2-x1)+1;
   int height = (y2-y1)+1;

   // create a buffer to hold cropped image.

   bufferedimage bicrop = new bufferedimage (width, height,
                                             bufferedimage.type_int_rgb);
   graphics2d g2d = bicrop.creategraphics ();

   // perform the crop operation.

   try
   {
       bufferedimage bi = (bufferedimage) image;
       bufferedimage bi2 = bi.getsubimage (x1, y1, width, height);
       g2d.drawimage (bi2, null, 0, 0);
   }
   catch (rasterformatexception e)
   {
      succeeded = false;
   }

   g2d.dispose ();

   if (succeeded)
       setimage (bicrop); // implicitly remove selection rectangle.
   else
   {
       // prepare to remove selection rectangle.

       srcx = destx;
       srcy = desty;

       // explicitly remove selection rectangle.

       repaint ();
   }

   return succeeded;
}


    crop()方法调用bufferedimage的public bufferedimage getsubimage(int x, int y, int w, int h)方法摘取选择区域内的子图像。如果该方法的参数没有指定bufferedimage内的图像,它就会抛出一个java.awt.image.rasterformatexception,因此就会返回false。

图像保存
    capture允许你把捕获的图像保存为一个jpeg文件。你通过一个保存文件选择器指定文件名,选择器由capture类的构造函数创建:
// construct a save file-chooser. initialize the starting directory to
// the current directory, do not allow the user to select the "all files"
// filter, and restrict the files that can be selected to those ending
// with .jpg or .jpeg extensions.

final jfilechooser fcsave = new jfilechooser ();
fcsave.setcurrentdirectory (new file (system.getproperty ("user.dir")));
fcsave.setacceptallfilefilterused (false);
fcsave.setfilefilter (new imagefilefilter ());


    为了限制文件选择器的选择是文件夹或者是以.jpg或.jpeg为后缀的文件,就使用了imagefilefilter类的一个实例作为保存时文件选择器的文件过滤器。该方法对于任何非文件夹和后缀名非.jpg/.jpeg的文件都返回false:
public boolean accept (file f)
{
   // allow the user to select directories so that the user can navigate the
   // file system.

   if (f.isdirectory ())
       return true;

   // allow the user to select files ending with a .jpg or a .jpeg
   // extension.

   string s = f.getname ();
   int i = s.lastindexof ('.');

   if (i > 0 && i < s.length ()-1)
   {
       string ext = s.substring (i+1).tolowercase ();

       if (ext.equals ("jpg") || ext.equals ("jpeg"))
           return true;
   }

   // nothing else can be selected.

   return false;
}


    当你选择了save as…菜单项时,它的监听器就会显示一个保存文件选择器。假定你没有退出选择器,监听器就会确保你选择的文件名是以.jpg或.jpeg为后缀名。继续,监听器会确定文件是否存在,这样你就不会无意中覆盖一个存在的文件。
// present the "save" file-chooser without any file selected.
// if the user cancels this file-chooser, exit this method.

fcsave.setselectedfile (null);
if (fcsave.showsavedialog (capture.this) !=
    jfilechooser.approve_option)
    return;

// obtain the selected file. validate its extension, which
// must be .jpg or .jpeg. if extension not present, append
// .jpg extension.

file file = fcsave.getselectedfile ();
string path = file.getabsolutepath ().tolowercase ();
if (!path.endswith (".jpg") && !path.endswith (".jpeg"))
    file = new file (path += ".jpg");

// if the file exists, inform the user, who might not want
// to accidentally overwrite an existing file. exit method
// if the user specifies that it is not okay to overwrite
// the file.
                  
if (file.exists ())
{
    int choice =  joptionpane.
                  showconfirmdialog (null,
                                     "overwrite file?",
                                     "capture",
                                     joptionpane.
                                     yes_no_option);
    if (choice == joptionpane.no_option)
        return;
}



 

    如果文件不存在或者你允许覆盖已经存在的文件,监听器就会将捕获的内容保存为一个选择的文件。为了完成这个任务,监听器使用java的imageio框架选择一个jpeg writer,指定文件作为writer的目标,设置writer的压缩品质为95%,然后把图像写入到文件中。

imagewriter writer = null;
imageoutputstream ios = null;

try
{
    // obtain a writer based o­n the jpeg format.

    iterator iter;
    iter = imageio.getimagewritersbyformatname ("jpeg");

    // validate existence of writer.

    if (!iter.hasnext ())
    {
        showerror ("unable to save image to jpeg file type.");
        return;
    }

    // extract writer.

    writer = (imagewriter) iter.next();

    // configure writer output destination.

    ios = imageio.createimageoutputstream (file);
    writer.setoutput (ios);

    // set jpeg compression quality to 95%.

    imagewriteparam iwp = writer.getdefaultwriteparam ();
    iwp.setcompressionmode (imagewriteparam.mode_explicit);
    iwp.setcompressionquality (0.95f);

    // write the image.

    writer.write (null,
                  new iioimage ((bufferedimage)
                                ia.getimage (), null, null),
                  iwp);
}
catch (ioexception e2)
{
    showerror (e2.getmessage ());
}
finally
{
    try
    {
        // cleanup.

        if (ios != null)
        {
            ios.flush ();
            ios.close ();
        }

        if (writer != null)
            writer.dispose ();
    }
    catch (ioexception e2)
    {
    }
}


    让代码自己清理一直是一个不错的主意。我把imageio的清理代码放在了finally子句中,以至于无论是正常结束还是抛出异常,它都可以执行。

总结

    capture限制了捕获的内容只能在主屏幕设备内。你可能想增强capture来捕获所有附加屏幕设备(或许是一个巨大的虚拟屏幕)的内容。增强之一,你需要包含下面的代码,它捕获所有屏幕的内容,将它和capture.java已经存在的代码集成。
graphicsenvironment graphenv = graphicsenvironment.getlocalgraphicsenvironment ();
graphicsdevice [] screens = graphenv.getscreendevices ();
bufferedimage [] captures = new bufferedimage [screens.length];

for (int i = 0; i < screens.length; i++)
{
    displaymode mode = screens [i].getdisplaymode ();
    rectangle bounds = new rectangle (0, 0, mode.getwidth (), mode.getheight ());
    captures [i] = new robot (screens [i]).createscreencapture (bounds);
}


    把以上代码放到capture菜单项的动作监听器内。然后先引入代码创建一个bigscreen要引用的足够大的bufferedimage,它可以保存被captures数组引用的所有bufferedimage内容;接着引入代码把它们的绘制到bigscreen中。capture现在成为了多屏幕捕获器就好像是一个单屏幕捕获器。

关于作者
jeff friesen是一个自由软件开发者和教育家,特别是在c、c++和java技术领域。

 
 
上一篇: 用java编程获得本机和服务器ip地址    下一篇: 用java applet保护网页的缺陷
  相关文档
利用缓冲技术提高jsp程序的性能和稳定性 03-31
使用技巧:j2me中程序优化的十个小方法 11-16
乱码问题之终极解决 11-17
简单介绍java语言中内存管理的几个技巧 11-16
理解ejb的参数传递 11-17
如何用 servlet 打开非 html 格式的文档 11-17
hibernate 入门(1) 11-17
java复杂数据类型用法 11-17
使用技巧:j2me中程序优化的十个小方法 11-17
workshop控件和扩展:第2部分 11-17
java进阶 java中具有实例缓存的不可变类 04-02
在j2ee web 应用中使用基于captcha 的授权模块 11-17
使用jspsmartupload上传下载全攻略详解 11-16
3d与人生 11-17
ajax使用技巧:如何处理书签和翻页按扭 11-17
手机上实现任意角度翻转算法[适用任何机型] 11-17
在java应用程序读取8位和24位windows位图 11-17
win2000下j2ee的安装及布署 11-20
用jdk自带包操作xml 11-17
java虚拟机学习笔记1 11-17
返回首页 | 关于我们 | J网章程 | JSP空间合租 | 客服中心 | 免责声明 | 常见问题 | 参观机房
本站主机空间代理至厦门市华众网络科技有限公司
《中华人民共和国增值电信业务经营许可证》
编号:闽B2-20050079
@2005-2008福建JSP技术网 版权所有 闽ICP备05000928号
技术电话:13616026886
邮箱:admin@fjjsp.com 站长QQ,点击这里给我发消息