摘要:本文展示了如何使用Java的Robot類創(chuàng)建一個(gè)能夠捕獲主屏幕內(nèi)容的屏幕捕捉程序。本程序處理捕捉屏幕外,還能夠?qū)⒄麄€(gè)屏幕或者屏幕的一部分保存成.jpg文件。
java.awt.Robot類真的很好玩。玩Robot會(huì)給你帶來(lái)很多樂(lè)趣。下面讓我們看看用Robot是怎么創(chuàng)建你自己的屏幕捕捉程序的。
程序界面
下圖是我的屏幕捕捉程序的界面。你可以用它來(lái)捕捉屏幕,裁剪選取的圖片內(nèi)容,并將結(jié)果保存為.jpg文件。

屏幕捕捉程序的界面很簡(jiǎn)單,包括一個(gè)菜單和一個(gè)用于顯示捕捉圖像的滾動(dòng)窗口。在窗口上按住鼠標(biāo)左鍵拖動(dòng)鼠標(biāo),你會(huì)看到一個(gè)紅色的虛線矩形,這就是圖像選取框,選取圖片以后選擇裁剪菜單,就可以對(duì)圖像進(jìn)行裁減。見(jiàn)下圖:

系統(tǒng)實(shí)現(xiàn)
本程序包括3個(gè)源文件:
Capture.java:?jiǎn)?dòng)應(yīng)用程序,構(gòu)建GUI;
ImageArea.java:顯示和選取屏幕圖片的組件;
ImageFileFilter.java:文件選擇過(guò)濾器,限制只能選擇JPEG文件。
下面我將結(jié)合代碼片斷介紹如何實(shí)現(xiàn)屏幕捕捉程序。
屏幕捕獲
要想使用Robot類創(chuàng)建屏幕捕捉程序,首先要?jiǎng)?chuàng)建Robot對(duì)象。在Capture類的
public static void main(String [] args)方法中用Robot的public Robot()創(chuàng)建Robot對(duì)象。如果創(chuàng)建成功,就會(huì)返回配置主屏幕坐標(biāo)系統(tǒng)的Robot引用。如果平臺(tái)不支持低級(jí)控制,就會(huì)拋出java.awt.AWTException。如果平臺(tái)不允許創(chuàng)建Robot,就會(huì)拋出java.lang.SecurityException。希望在你的平臺(tái)上不會(huì)拋出上面兩個(gè)異常。
假設(shè)Robot對(duì)象已經(jīng)創(chuàng)建,main()會(huì)調(diào)用Capture類的構(gòu)造函數(shù)創(chuàng)建GUI。在創(chuàng)建GUI的同時(shí),Capture調(diào)用dimScreenSize = Toolkit.getDefaultToolkit ().getScreenSize ();來(lái)獲得屏幕的尺寸信息。因?yàn)镽obot中用于執(zhí)行屏幕捕獲的public BufferedImage createScreenCapture(Rectangle screenRect)方法需要一個(gè)java.awt.Rectangle參數(shù)。構(gòu)造函數(shù)通過(guò)rectScreenSize = new Rectangle (dimScreenSize);將java.awt.Dimension轉(zhuǎn)換成Rectangle 。下面的代碼片斷展示了當(dāng)菜單中捕捉菜單項(xiàng)按下后執(zhí)行的屏幕捕捉動(dòng)作。
//?執(zhí)行屏幕捕捉時(shí)隱藏屏幕捕捉程序,使其不顯示在桌面上.

setVisible?(false);

//?執(zhí)行屏幕捕捉.

BufferedImage?biScreen;
biScreen?=?robot.createScreenCapture?(rectScreenSize);

//?完成屏幕捕捉后顯示屏幕捕捉程序窗口.

setVisible?(true);

//?用捕獲的屏幕圖片刷新ImageArea組件并相應(yīng)調(diào)整滾動(dòng)條.

ia.setImage?(biScreen);

jsp.getHorizontalScrollBar?().setValue?(0);
jsp.getVerticalScrollBar?().setValue?(0);

我不希望屏幕捕捉程序窗口遮住主屏幕,因此在捕捉屏幕之前我先將其隱藏。當(dāng)獲取到保存有屏幕圖片象素的
java.awt.image.BufferedImage 后,屏幕捕捉程序窗口再次顯現(xiàn)出來(lái)并通過(guò)ImageArea組件顯示出
BufferedImage 。
圖像選取
首先我們需要一個(gè)矩形框來(lái)標(biāo)識(shí)我們想要裁剪的圖像區(qū)域。在ImageArea組建中,提供了創(chuàng)建、操作、繪制這個(gè)矩形框的代碼。下面的代碼片斷來(lái)自ImageArea.java,ImageArea類的構(gòu)造函數(shù)創(chuàng)建了選取矩形框的實(shí)例,并且創(chuàng)建了
java.awt.BasicStroke 和
java.awt.GradientPaint
對(duì)象來(lái)定義選取框的外觀,同時(shí)還注冊(cè)了鼠標(biāo)和鼠標(biāo)移動(dòng)監(jiān)聽(tīng),來(lái)幫助你操作矩形選取框。
//?創(chuàng)建矩形選取框.最好是只創(chuàng)建一個(gè)選取框,
???// 這樣比每次paintComponent()被調(diào)用時(shí)創(chuàng)建選取框好.
???// 減少了不必要的對(duì)象的創(chuàng)建

rectSelection?=?new?Rectangle?();

//?定義矩形選取框的輪廓.

bs?=?new?BasicStroke?(5,?BasicStroke.CAP_ROUND,?BasicStroke.JOIN_ROUND,

??????????????????????0,?new?float?[]?
{?12,?12?},?0);

//?定義矩形選取框的顏色

gp?=?new?GradientPaint?(0.0f,?0.0f,?Color.red,?1.0f,?1.0f,?Color.white,
????????????????????????true);

// 建立鼠標(biāo)監(jiān)聽(tīng).

MouseListener?ml;
ml?=?new?MouseAdapter?()

?????
{
?????????public?void?mousePressed?(MouseEvent?e)

?????????
{
????????????//?沒(méi)有捕獲的圖像時(shí)直接返回.

????????????if?(image?==?null)
????????????????return;

????????????destx?=?srcx?=?e.getX?();
????????????desty?=?srcy?=?e.getY?();

????????????repaint?();
?????????}
?????};
addMouseListener?(ml);

//?建立鼠標(biāo)移動(dòng)監(jiān)聽(tīng).

MouseMotionListener?mml;
mml?=?new?MouseMotionAdapter?()

??????
{
??????????public?void?mouseDragged?(MouseEvent?e)

??????????
{
?????????????//?沒(méi)有捕獲的圖像時(shí)直接返回.

?????????????if?(image?==?null)
?????????????????return;

?????????????destx?=?e.getX?();
?????????????desty?=?e.getY?();

?????????????repaint?();?
??????????}
??????};
addMouseMotionListener?(mml);

當(dāng)點(diǎn)擊鼠標(biāo)時(shí),鼠標(biāo)事件處理器將
destx?和
srcx都設(shè)置為鼠標(biāo)的橫坐標(biāo),縱坐標(biāo)業(yè)一樣。源坐標(biāo)和目的坐標(biāo)一樣意味著刪除當(dāng)前的矩形選取框。我們可以通過(guò)調(diào)用repaint(),repaint()會(huì)調(diào)用public void paintComponent(Graphics g)實(shí)現(xiàn)刪除矩形選取框。paintComponent()方法會(huì)比較srcx與destx
, srcy
與desty;如果他們不相等,就繪制一個(gè)新的矩形選取框。
//?繪制矩形選取框.

if?(srcx?!=?destx?||?srcy?!=?desty)


{
????//?計(jì)算左上和右下點(diǎn)的坐標(biāo).

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

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

????//?確定矩形的原點(diǎn).

????rectSelection.x?=?x1;
????rectSelection.y?=?y1;

????//?確定矩形的長(zhǎng)寬.

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

????//?繪制矩形.

????Graphics2D?g2d?=?(Graphics2D)?g;
????g2d.setStroke?(bs);
????g2d.setPaint?(gp);
????g2d.draw?(rectSelection);
}

在繪制矩形之前,要先確定矩形的左上和右下角,以便算出矩形的原點(diǎn)和長(zhǎng)寬。因此你可以任意拖動(dòng)鼠標(biāo)繪制選取框,最后
srcx/
destx
和
srcy
/
desty中的最小值確定左上角,同理,最大值確定右下角。
圖像裁剪
選取完圖像以后,我們自然要裁剪它。當(dāng)點(diǎn)擊菜單中的“裁剪”菜單項(xiàng)時(shí),ImageArea組件會(huì)將選中的圖像裁減下來(lái)。如果裁剪成功,ImageArea的滾動(dòng)條會(huì)重新設(shè)置,否則程序會(huì)彈出“超出范圍”對(duì)話框。
//?如果裁剪成功,重新設(shè)置滾動(dòng)條.

if?(ia.crop?())


{
????jsp.getHorizontalScrollBar?().setValue?(0);
????jsp.getVerticalScrollBar?().setValue?(0);
}
else
????showError?("超出范圍");
也許你會(huì)問(wèn)為什么會(huì)出現(xiàn)“超出范圍”呢?請(qǐng)看下圖

由于裁剪后GUI窗口不會(huì)改變大小,因此可能裁減后的圖片比程序的GUI窗口小,GUI窗口的背景就顯露出來(lái)。如果選取框包含了GUI窗口的背景象素,那么在裁剪時(shí)就會(huì)報(bào)“超出范圍”錯(cuò)誤。
裁剪功能是由ImageArea的
public boolean crop() 方法實(shí)現(xiàn)的。當(dāng)裁剪成功或者沒(méi)有可裁減圖像時(shí)返回true,如果選取框包含有GUI窗口背景象素,就返回false。下面是代碼:
public?boolean?crop?()


{
???//?如果選取框只是一個(gè)點(diǎn),返回true

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

???//?默認(rèn)返回true.

???boolean?succeeded?=?true;

???//?計(jì)算選取框的左上角和右下角坐標(biāo).

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

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

???//?計(jì)算選取框的尺寸.

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

???//?創(chuàng)建保存裁剪圖像的圖像緩沖.

???BufferedImage?biCrop?=?new?BufferedImage?(width,?height,
?????????????????????????????????????????????BufferedImage.TYPE_INT_RGB);
???Graphics2D?g2d?=?biCrop.createGraphics?();

???//?執(zhí)行裁剪操作.

???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);?
???else

???
{
???????//?準(zhǔn)備刪除選取框.

???????srcx?=?destx;
???????srcy?=?desty;

???????//?刪除選取框.

???????repaint?();
???}

???return?succeeded;
}

crop()會(huì)調(diào)用
BufferedImage的
public BufferedImage getSubimage(int x, int y, int w, int h)方法將選取框中的圖像從原圖像中裁剪下來(lái)。如果參數(shù)不是指定的BufferedImage區(qū)域,此方法會(huì)拋出java.awt.image.RasterFormatException 異常,返回fasle。
保存圖像
本程序允許保存圖像。你可以通過(guò)文件選擇存對(duì)話框?yàn)橐獔?bào)存的圖像取個(gè)名字。文件保存對(duì)話框在Capture類的構(gòu)造函數(shù)中定義。
final?JFileChooser?fcSave?=?new?JFileChooser?();
fcSave.setCurrentDirectory?(new?File?(System.getProperty?("user.dir")));
fcSave.setAcceptAllFileFilterUsed?(false);
fcSave.setFileFilter?(new?ImageFileFilter?());

為了約束文件選擇對(duì)話框所能保存只能保存JPEG文件,我們創(chuàng)建了一個(gè)ImageFileFilter類作為文件選擇對(duì)話框的文件過(guò)濾器。如果傳入方法public boolean accept (File f)的參數(shù)不是目錄或者以.jpg .jpeg為后綴的文件,那么返回false
public?boolean?accept?(File?f)


{
???//?允許用戶選擇文件夾.

???if?(f.isDirectory?())
???????return?true;

???//?允許用戶選擇以.jpg?或?.jpeg為后綴的文件

???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;
???}

???//?沒(méi)有可以選擇的.

???return?false;
}

當(dāng)點(diǎn)擊菜單的“另存為”菜單項(xiàng)時(shí),調(diào)用文件選擇器。文件選擇器會(huì)確保你的文件保存為JPEG文件。如果你為文件氣的名字已經(jīng)被另一個(gè)文件使用,文件選擇器將詢問(wèn)你是否覆蓋原文件。
//?顯示文件選擇器,不選中任何文件.
//?如果用戶點(diǎn)擊cancel,則退出.

fcSave.setSelectedFile?(null);
if?(fcSave.showSaveDialog?(Capture.this)?!=
????JFileChooser.APPROVE_OPTION)
????return;

//?獲取選擇的文件.如果不是以.jpg 或.jpeg為后綴,
?//?添加.jpg后綴

File?file?=?fcSave.getSelectedFile?();
String?path?=?file.getAbsolutePath?().toLowerCase?();
if?(!path.endsWith?(".jpg")?&&?!path.endsWith?(".jpeg"))
????file?=?new?File?(path?+=?".jpg");

//?如果文件已存在,通知用戶
??????????????????
if?(file.exists?())


{
????int?choice?=??JOptionPane.
??????????????????showConfirmDialog?(null,
?????????????????????????????????????"Overwrite?file?",
?????????????????????????????????????"Capture",
?????????????????????????????????????JOptionPane.
?????????????????????????????????????YES_NO_OPTION);
????if?(choice?==?JOptionPane.NO_OPTION)
????????return;
}

如果文件不存在,或者你允許覆蓋已有文件,程序?qū)?huì)保存圖片。為了完成保存,我們使用了Java的ImageIO框架。代碼如下:
ImageWriter?writer?=?null;
ImageOutputStream?ios?=?null;

try


{
????//?獲得一個(gè)jpeg?類型的寫入器

????Iterator?iter;
????iter?=?ImageIO.getImageWritersByFormatName?("jpeg");

????//?驗(yàn)證寫入器是否存在

????if?(!iter.hasNext?())

????
{
????????showError?("Unable?to?save?image?to?jpeg?file?type.");
????????return;
????}.

????writer?=?(ImageWriter)?iter.next();


????//?獲取寫入器寫入目標(biāo)

????ios?=?ImageIO.createImageOutputStream?(file);
????writer.setOutput?(ios);

????//?設(shè)置jpeg壓縮率為?95%.

????ImageWriteParam?iwp?=?writer.getDefaultWriteParam?();
????iwp.setCompressionMode?(ImageWriteParam.MODE_EXPLICIT);
????iwp.setCompressionQuality?(0.95f);

????//?寫入圖像.

????writer.write?(null,
??????????????????new?IIOImage?((BufferedImage)
????????????????????????????????ia.getImage?(),?null,?null),
??????????????????iwp);
}
catch?(IOException?e2)


{
????showError?(e2.getMessage?());
}
finally


{
????try

????
{
????????//?清理.

????????if?(ios?!=?null)

????????
{
????????????ios.flush?();
????????????ios.close?();
????????}

????????if?(writer?!=?null)
????????????writer.dispose?();
????}
????catch?(IOException?e2)

????
{
????}
}

保存后的清理工作很有必要。我將清理代碼上到finally塊中,這樣不管是正常的保存成功還是意外地中止,都能執(zhí)行響應(yīng)的清理工作。
改進(jìn)
本文中的這個(gè)屏幕捕捉程序只能捕捉主屏幕設(shè)備的圖像。也許你希望捕獲所有屏幕的圖像。要實(shí)現(xiàn)這個(gè)功能,你可以將下面的代碼加入到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);
}

上面介紹了用Robot類制作Java屏幕捕捉程序的全過(guò)程。希望對(duì)大家能有所啟示。如果需要本程序的全部源代碼,請(qǐng)留下郵箱,我會(huì)及時(shí)發(fā)給你。
posted on 2006-05-17 11:30
學(xué)二的貓 閱讀(5530)
評(píng)論(72) 編輯 收藏 所屬分類:
Java禪機(jī)