欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 屏幕截屏方法汇总

程序员文章站 2024-02-22 10:38:58
1、直接使用getwindow().getdecorview().getrootview() 直接使用getwindow().getdecorview().getroot...

1、直接使用getwindow().getdecorview().getrootview()

直接使用getwindow().getdecorview().getrootview()是获取当前屏幕的activity。然而对于系统状态栏的信息是截不了,出现一条空白的。如下图:

 Android 屏幕截屏方法汇总

主要到没,有一条白色边就是系统状态栏。看一下代码,很简单都加了注释了。

//这种方法状态栏是空白,显示不了状态栏的信息 
private void savecurrentimage() 
{ 
//获取当前屏幕的大小 
int width = getwindow().getdecorview().getrootview().getwidth(); 
int height = getwindow().getdecorview().getrootview().getheight(); 
//生成相同大小的图片 
bitmap tembitmap = bitmap.createbitmap( width, height, config.argb_8888 ); 
//找到当前页面的跟布局 
view view = getwindow().getdecorview().getrootview(); 
//设置缓存 
view.setdrawingcacheenabled(true); 
view.builddrawingcache(); 
//从缓存中获取当前屏幕的图片 
tembitmap = view.getdrawingcache(); 
//输出到sd卡 
if (fileioutil.getexiststorage()) { 
fileioutil.getinstance().onfolderanalysis(fileioutil.getinstance().getfilepathandname()); 
file file = new file(fileioutil.getinstance().getfilepathandname()); 
try { 
if (!file.exists()) { 
file.createnewfile(); 
} 
fileoutputstream fostream = new fileoutputstream(file); 
tembitmap.compress(bitmap.compressformat.png, 100, fostream); 
fostream.flush(); 
fostream.close(); 
} catch (exception e) { 
log.i("show", e.tostring()); 
} 
} 
}

2、自定义view控件的截图

自定义view控件都是继承view的吗,那么就有可以获取宽度,高度。生成图片,把它绘制出来的。我拿了直接写的自定义随机验证码的例子来截图。

Android 屏幕截屏方法汇总

//保存自定义view的截图 
private void savecustomviewbitmap() { 
//获取自定义view图片的大小 
bitmap tembitmap = bitmap.createbitmap(mcodeview.getwidth(), mcodeview.getheight(), config.argb_8888); 
//使用canvas,调用自定义view控件的ondraw方法,绘制图片 
canvas canvas = new canvas(tembitmap); 
mcodeview.ondraw(canvas); 
//输出到sd卡 
if (fileioutil.getexiststorage()) { 
fileioutil.getinstance().onfolderanalysis(fileioutil.getinstance().getfilepathandname()); 
file file = new file(fileioutil.getinstance().getfilepathandname()); 
try { 
if (!file.exists()) { 
file.createnewfile(); 
} 
fileoutputstream fostream = new fileoutputstream(file); 
tembitmap.compress(bitmap.compressformat.png, 100, fostream); 
fostream.flush(); 
fostream.close(); 
toast.maketext(mainactivity.this, "截屏文件已保存至" + fileioutil.getinstance().getfilepathandname(), toast.length_long).show(); 
} catch (exception e) { 
log.i("show", e.tostring()); 
} 
} 
}

有人根据这种思路,自定义整个布局的view,然后来截图。也是够拼的,我有点不赞成这样使用。

3、基于android ddmlib截屏

这个是java写的一个类,入口是mian函数。那么这种实现方式是要android连接着电脑,还需要android设备调试。这种不多说,搞android开发都懂。太麻烦了,我们也不使用。

package com.example.screenshot; 
import java.io.ioexception; 
import java.text.simpledateformat; 
import java.util.date; 
import com.android.ddmlib.adbcommandrejectedexception; 
import com.android.ddmlib.androiddebugbridge; 
import com.android.ddmlib.idevice; 
import com.android.ddmlib.rawimage; 
import com.android.ddmlib.timeoutexception; 
public class screenshoddmlib { 
private bufferedimage image = null; 
/** 
* @param args 
*/ 
public static void main(string[] args) { 
// todo auto-generated method stub 
androiddebugbridge.init(false); // 
screenshoddmlib screenshot = new screenshoddmlib(); 
idevice device = screenshot.getdevice(); 
for (int i = 0; i < 10; i++) { 
date date=new date(); 
simpledateformat df=new simpledateformat("mm-dd-hh-mm-ss"); 
string nowtime = df.format(date); 
screenshot.getscreenshot(device, "robotium" + nowtime); 
try { 
thread.sleep(1000); 
} catch (interruptedexception e) { 
// todo auto-generated catch block 
e.printstacktrace(); 
} 
} 
} 
public void getscreenshot(idevice device,string filename) { 
rawimage rawscreen = null; 
try { 
rawscreen = device.getscreenshot(); 
} catch (timeoutexception e) { 
// todo auto-generated catch block 
e.printstacktrace(); 
} catch (adbcommandrejectedexception e) { 
// todo auto-generated catch block 
e.printstacktrace(); 
} catch (ioexception e) { 
// todo auto-generated catch block 
e.printstacktrace(); 
} 
if (rawscreen != null) { 
boolean landscape = false; 
int width2 = landscape ? rawscreen.height : rawscreen.width; 
int height2 = landscape ? rawscreen.width : rawscreen.height; 
if (image == null) { 
image = new bufferedimage(width2, height2, 
bufferedimage.type_int_rgb); 
} else { 
if (image.getheight() != height2 || image.getwidth() != width2) { 
image = new bufferedimage(width2, height2, 
bufferedimage.type_int_rgb); 
} 
} 
int index = 0; 
int indexinc = rawscreen.bpp >> 3; 
for (int y = 0; y < rawscreen.height; y++) { 
for (int x = 0; x < rawscreen.width; x++, index += indexinc) { 
int value = rawscreen.getargb(index); 
if (landscape) 
image.setrgb(y, rawscreen.width - x - 1, value); 
else 
image.setrgb(x, y, value); 
} 
} 
try { 
imageio.write((renderedimage) image, "png", new file("d:/" 
+ filename + ".jpg")); 
} catch (ioexception e) { 
// todo auto-generated catch block 
e.printstacktrace(); 
} 
} 
} 
/** 
* 获取得到device对象 
* @return 
*/ 
private idevice getdevice(){ 
idevice device; 
androiddebugbridge bridge = androiddebugbridge 
.createbridge("adb", true);//如果代码有问题请查看api,修改此处的参数值试一下 
waitdeviceslist(bridge); 
idevice devices[] = bridge.getdevices(); 
device = devices[0]; 
return device; 
} 
/** 
* 等待查找device 
* @param bridge 
*/ 
private void waitdeviceslist(androiddebugbridge bridge) { 
int count = 0; 
while (bridge.hasinitialdevicelist() == false) { 
try { 
thread.sleep(500); 
count++; 
} catch (interruptedexception e) { 
} 
if (count > 240) { 
system.err.print("等待获取设备超时"); 
break; 
} 
} 
}

4、使用adb命令

需要系统权限,在apk中调用“adb shell screencap -pfilepath” 命令

需要获得系统权限:

1、 在androidmanifest.xml文件中添加 <uses-permissionandroid:name="android.permission.read_frame_buffer"/>

2、修改apk为系统权限,将apk放到源码中编译, 修改android.mk local_certificate := platform
在这里我要说一下,搞过jni调用就知道android.mk的作用。此举也是麻烦,效果也不是很好。

public void takescreenshot(){ 
string msavedpath = environment.getexternalstoragedirectory()+file. separator + "screenshot.png" ; 
try { 
runtime. getruntime().exec("screencap -p " + msavedpath); 
} catch (exception e) { 
e.printstacktrace(); 
}

5、看一下系统截屏是怎样的

相信大家都知道,三星的机子是同时按下 home键 + 电源键 3秒截图。
如果没有home键的机子是按下 音量键向下那个 + 电源键 3秒截图的。
获取物理键盘按下的源码:phonewindowmanager.java

// handle special keys. 
switch (keycode) { 
case keyevent.keycode_volume_down: 
case keyevent.keycode_volume_up: 
case keyevent.keycode_volume_mute: { 
if (keycode == keyevent.keycode_volume_down) { 
if (down) { 
if (isscreenon && !mvolumedownkeytriggered 
&& (event.getflags() & keyevent.flag_fallback) == 0) { 
mvolumedownkeytriggered = true; 
mvolumedownkeytime = event.getdowntime(); 
mvolumedownkeyconsumedbyscreenshotchord = false; 
cancelpendingpowerkeyaction(); 
interceptscreenshotchord(); 
} 
} else { 
mvolumedownkeytriggered = false; 
cancelpendingscreenshotchordaction(); 
} 
} else if (keycode == keyevent.keycode_volume_up) { 
if (down) { 
if (isscreenon && !mvolumeupkeytriggered 
&& (event.getflags() & keyevent.flag_fallback) == 0) { 
mvolumeupkeytriggered = true; 
cancelpendingpowerkeyaction(); 
cancelpendingscreenshotchordaction(); 
} 
} else { 
mvolumeupkeytriggered = false; 
cancelpendingscreenshotchordaction(); 
} 
} 
if (down) { 
itelephony telephonyservice = gettelephonyservice(); 
if (telephonyservice != null) { 
try { 
if (telephonyservice.isringing()) { 
// if an incoming call is ringing, either volume key means 
// "silence ringer". we handle these keys here, rather than 
// in the incallscreen, to make sure we'll respond to them 
// even if the incallscreen hasn't come to the foreground yet. 
// look for the down event here, to agree with the "fallback" 
// behavior in the incallscreen. 
log.i(tag, "interceptkeybeforequeueing:" 
+ " volume key-down while ringing: silence ringer!"); 
// silence the ringer. (it's safe to call this 
// even if the ringer has already been silenced.) 
telephonyservice.silenceringer(); 
// and *don't* pass this key thru to the current activity 
// (which is probably the incallscreen.) 
result &= ~action_pass_to_user; 
break; 
} 
if (telephonyservice.isoffhook() 
&& (result & action_pass_to_user) == 0) { 
// if we are in call but we decided not to pass the key to 
// the application, handle the volume change here. 
handlevolumekey(audiomanager.stream_voice_call, keycode); 
break; 
} 
} catch (remoteexception ex) { 
log.w(tag, "itelephony threw remoteexception", ex); 
} 
} 
if (ismusicactive() && (result & action_pass_to_user) == 0) { 
// if music is playing but we decided not to pass the key to the 
// application, handle the volume change here. 
handlevolumekey(audiomanager.stream_music, keycode); 
break; 
} 
} 
break; 
} 
case keyevent.keycode_endcall: { 
result &= ~action_pass_to_user; 
if (down) { 
itelephony telephonyservice = gettelephonyservice(); 
boolean hungup = false; 
if (telephonyservice != null) { 
try { 
hungup = telephonyservice.endcall(); 
} catch (remoteexception ex) { 
log.w(tag, "itelephony threw remoteexception", ex); 
} 
} 
interceptpowerkeydown(!isscreenon || hungup); 
} else { 
if (interceptpowerkeyup(canceled)) { 
if ((mendcallbehavior 
& settings.system.end_button_behavior_home) != 0) { 
if (gohome()) { 
break; 
} 
} 
if ((mendcallbehavior 
& settings.system.end_button_behavior_sleep) != 0) { 
result = (result & ~action_wake_up) | action_go_to_sleep; 
} 
} 
} 
break; 
} 
case keyevent.keycode_power: { 
result &= ~action_pass_to_user; 
if (down) { 
if (isscreenon && !mpowerkeytriggered 
&& (event.getflags() & keyevent.flag_fallback) == 0) { 
mpowerkeytriggered = true; 
mpowerkeytime = event.getdowntime(); 
interceptscreenshotchord(); 
} 
itelephony telephonyservice = gettelephonyservice(); 
boolean hungup = false; 
if (telephonyservice != null) { 
try { 
if (telephonyservice.isringing()) { 
// pressing power while there's a ringing incoming 
// call should silence the ringer. 
telephonyservice.silenceringer(); 
} else if ((mincallpowerbehavior 
& settings.secure.incall_power_button_behavior_hangup) != 0 
&& telephonyservice.isoffhook()) { 
// otherwise, if "power button ends call" is enabled, 
// the power button will hang up any current active call. 
hungup = telephonyservice.endcall(); 
} 
} catch (remoteexception ex) { 
log.w(tag, "itelephony threw remoteexception", ex); 
} 
} 
interceptpowerkeydown(!isscreenon || hungup 
|| mvolumedownkeytriggered || mvolumeupkeytriggered); 
} else { 
mpowerkeytriggered = false; 
cancelpendingscreenshotchordaction(); 
if (interceptpowerkeyup(canceled || mpendingpowerkeyupcanceled)) { 
result = (result & ~action_wake_up) | action_go_to_sleep; 
} 
mpendingpowerkeyupcanceled = false; 
} 
break; 
}

响应截屏的方法:

private void interceptscreenshotchord() { 
if (mscreenshotchordenabled 
&& mvolumedownkeytriggered && mpowerkeytriggered && !mvolumeupkeytriggered) { 
final long now = systemclock.uptimemillis(); 
if (now <= mvolumedownkeytime + screenshot_chord_debounce_delay_millis 
&& now <= mpowerkeytime + screenshot_chord_debounce_delay_millis) { 
mvolumedownkeyconsumedbyscreenshotchord = true; 
cancelpendingpowerkeyaction(); 
mhandler.postdelayed(mscreenshotchordlongpress, getscreenshotchordlongpressdelay()); 
} 
} 
} 
private long getscreenshotchordlongpressdelay() { 
if (mkeyguardmediator.isshowing()) { 
// double the time it takes to take a screenshot from the keyguard 
return (long) (keyguard_screenshot_chord_delay_multiplier * 
viewconfiguration.getglobalactionkeytimeout()); 
} else { 
return viewconfiguration.getglobalactionkeytimeout(); 
} 
}

接受响应的服务

private final runnable mscreenshotchordlongpress = new runnable() { 
public void run() { 
takescreenshot(); 
} 
}; 
private void takescreenshot() { 
synchronized (mscreenshotlock) { 
if (mscreenshotconnection != null) { 
return; 
} 
componentname cn = new componentname("com.android.systemui", 
"com.android.systemui.screenshot.takescreenshotservice"); 
intent intent = new intent(); 
intent.setcomponent(cn); 
serviceconnection conn = new serviceconnection() { 
@override 
public void onserviceconnected(componentname name, ibinder service) { 
synchronized (mscreenshotlock) { 
if (mscreenshotconnection != this) { 
return; 
} 
messenger messenger = new messenger(service); 
message msg = message.obtain(null, 1); 
final serviceconnection myconn = this; 
handler h = new handler(mhandler.getlooper()) { 
@override 
public void handlemessage(message msg) { 
synchronized (mscreenshotlock) { 
if (mscreenshotconnection == myconn) { 
mcontext.unbindservice(mscreenshotconnection); 
mscreenshotconnection = null; 
mhandler.removecallbacks(mscreenshottimeout); 
} 
} 
} 
}; 
msg.replyto = new messenger(h); 
msg.arg1 = msg.arg2 = 0; 
if (mstatusbar != null && mstatusbar.isvisiblelw()) 
msg.arg1 = 1; 
if (mnavigationbar != null && mnavigationbar.isvisiblelw()) 
msg.arg2 = 1; 
try { 
messenger.send(msg); 
} catch (remoteexception e) { 
} 
} 
} 
@override 
public void onservicedisconnected(componentname name) {} 
}; 
if (mcontext.bindservice( 
intent, conn, context.bind_auto_create, userhandle.user_current)) { 
mscreenshotconnection = conn; 
mhandler.postdelayed(mscreenshottimeout, 10000); 
} 
} 
}

启动时这个服务 componentname cn = new componentname("com.android.systemui", "com.android.systemui.screenshot.takescreenshotservice");

package com.android.systemui.screenshot; 
import android.app.service; 
import android.content.intent; 
import android.os.handler; 
import android.os.ibinder; 
import android.os.message; 
import android.os.messenger; 
import android.os.remoteexception; 
public class takescreenshotservice extends service { 
private static final string tag = "takescreenshotservice"; 
private static globalscreenshot mscreenshot; 
private handler mhandler = new handler() { 
@override 
public void handlemessage(message msg) { 
switch (msg.what) { 
case 1: 
final messenger callback = msg.replyto; 
if (mscreenshot == null) { 
mscreenshot = new globalscreenshot(takescreenshotservice.this); 
} 
mscreenshot.takescreenshot(new runnable() { 
@override public void run() { 
message reply = message.obtain(null, 1); 
try { 
callback.send(reply); 
} catch (remoteexception e) { 
} 
} 
}, msg.arg1 > 0, msg.arg2 > 0); 
} 
} 
}; 
@override 
public ibinder onbind(intent intent) { 
return new messenger(mhandler).getbinder(); 
}

再往下就是底层库,需要编译出来才能看得到了。这些就先不研究了。

6、还有部分系统源码是截图的

/* 
* copyright (c) 2011 the android open source project 
* 
* licensed under the apache license, version 2.0 (the "license"); 
* you may not use this file except in compliance with the license. 
* you may obtain a copy of the license at 
* 
* http://www.apache.org/licenses/license-2.0 
* 
* unless required by applicable law or agreed to in writing, software 
* distributed under the license is distributed on an "as is" basis, 
* without warranties or conditions of any kind, either express or implied. 
* see the license for the specific language governing permissions and 
* limitations under the license. 
*/ 
package com.android.systemui.screenshot; 
import android.animation.animator; 
import android.animation.animatorlisteneradapter; 
import android.animation.animatorset; 
import android.animation.valueanimator; 
import android.animation.valueanimator.animatorupdatelistener; 
import android.app.notification; 
import android.app.notification.bigpicturestyle; 
import android.app.notificationmanager; 
import android.app.pendingintent; 
import android.content.contentresolver; 
import android.content.contentvalues; 
import android.content.context; 
import android.content.intent; 
import android.content.res.resources; 
import android.graphics.bitmap; 
import android.graphics.canvas; 
import android.graphics.colormatrix; 
import android.graphics.colormatrixcolorfilter; 
import android.graphics.matrix; 
import android.graphics.paint; 
import android.graphics.pixelformat; 
import android.graphics.pointf; 
import android.media.mediaactionsound; 
import android.net.uri; 
import android.os.asynctask; 
import android.os.environment; 
import android.os.process; 
import android.provider.mediastore; 
import android.util.displaymetrics; 
import android.view.display; 
import android.view.layoutinflater; 
import android.view.motionevent; 
import android.view.surface; 
import android.view.view; 
import android.view.viewgroup; 
import android.view.windowmanager; 
import android.view.animation.interpolator; 
import android.widget.imageview; 
import com.android.systemui.r; 
import java.io.file; 
import java.io.outputstream; 
import java.text.simpledateformat; 
import java.util.date; 
/** 
* pod used in the asynctask which saves an image in the background. 
*/ 
class saveimageinbackgrounddata { 
context context; 
bitmap image; 
uri imageuri; 
runnable finisher; 
int iconsize; 
int result; 
} 
/** 
* an asynctask that saves an image to the media store in the background. 
*/ 
class saveimageinbackgroundtask extends asynctask<saveimageinbackgrounddata, void, 
saveimageinbackgrounddata> { 
private static final string screenshots_dir_name = "screenshots"; 
private static final string screenshot_file_name_template = "screenshot_%s.png"; 
private static final string screenshot_file_path_template = "%s/%s/%s"; 
private int mnotificationid; 
private notificationmanager mnotificationmanager; 
private notification.builder mnotificationbuilder; 
private string mimagefilename; 
private string mimagefilepath; 
private long mimagetime; 
private bigpicturestyle mnotificationstyle; 
// workaround: we want the same notification across screenshots that we update so that we don't 
// spam a user's notification drawer. however, we only show the ticker for the saving state 
// and if the ticker text is the same as the previous notification, then it will not show. so 
// for now, we just add and remove a space from the ticker text to trigger the animation when 
// necessary. 
private static boolean mtickeraddspace; 
saveimageinbackgroundtask(context context, saveimageinbackgrounddata data, 
notificationmanager nmanager, int nid) { 
resources r = context.getresources(); 
// prepare all the output metadata 
mimagetime = system.currenttimemillis(); 
string imagedate = new simpledateformat("yyyy-mm-dd-hh-mm-ss").format(new date(mimagetime)); 
string imagedir = environment.getexternalstoragepublicdirectory( 
environment.directory_pictures).getabsolutepath(); 
mimagefilename = string.format(screenshot_file_name_template, imagedate); 
mimagefilepath = string.format(screenshot_file_path_template, imagedir, 
screenshots_dir_name, mimagefilename); 
// create the large notification icon 
int imagewidth = data.image.getwidth(); 
int imageheight = data.image.getheight(); 
int iconsize = data.iconsize; 
final int shortside = imagewidth < imageheight ? imagewidth : imageheight; 
bitmap preview = bitmap.createbitmap(shortside, shortside, data.image.getconfig()); 
canvas c = new canvas(preview); 
paint paint = new paint(); 
colormatrix desat = new colormatrix(); 
desat.setsaturation(0.25f); 
paint.setcolorfilter(new colormatrixcolorfilter(desat)); 
matrix matrix = new matrix(); 
matrix.posttranslate((shortside - imagewidth) / 2, 
(shortside - imageheight) / 2); 
c.drawbitmap(data.image, matrix, paint); 
c.drawcolor(0x40ffffff); 
bitmap croppedicon = bitmap.createscaledbitmap(preview, iconsize, iconsize, true); 
// show the intermediate notification 
mtickeraddspace = !mtickeraddspace; 
mnotificationid = nid; 
mnotificationmanager = nmanager; 
mnotificationbuilder = new notification.builder(context) 
.setticker(r.getstring(r.string.screenshot_saving_ticker) 
+ (mtickeraddspace ? " " : "")) 
.setcontenttitle(r.getstring(r.string.screenshot_saving_title)) 
.setcontenttext(r.getstring(r.string.screenshot_saving_text)) 
.setsmallicon(r.drawable.stat_notify_image) 
.setwhen(system.currenttimemillis()); 
mnotificationstyle = new notification.bigpicturestyle() 
.bigpicture(preview); 
mnotificationbuilder.setstyle(mnotificationstyle); 
notification n = mnotificationbuilder.build(); 
n.flags |= notification.flag_no_clear; 
mnotificationmanager.notify(nid, n); 
// on the tablet, the large icon makes the notification appear as if it is clickable (and 
// on small devices, the large icon is not shown) so defer showing the large icon until 
// we compose the final post-save notification below. 
mnotificationbuilder.setlargeicon(croppedicon); 
// but we still don't set it for the expanded view, allowing the smallicon to show here. 
mnotificationstyle.biglargeicon(null); 
} 
@override 
protected saveimageinbackgrounddata doinbackground(saveimageinbackgrounddata... params) { 
if (params.length != 1) return null; 
// by default, asynctask sets the worker thread to have background thread priority, so bump 
// it back up so that we save a little quicker. 
process.setthreadpriority(process.thread_priority_foreground); 
context context = params[0].context; 
bitmap image = params[0].image; 
resources r = context.getresources(); 
try { 
// save the screenshot to the mediastore 
contentvalues values = new contentvalues(); 
contentresolver resolver = context.getcontentresolver(); 
values.put(mediastore.images.imagecolumns.data, mimagefilepath); 
values.put(mediastore.images.imagecolumns.title, mimagefilename); 
values.put(mediastore.images.imagecolumns.display_name, mimagefilename); 
values.put(mediastore.images.imagecolumns.date_taken, mimagetime); 
values.put(mediastore.images.imagecolumns.date_added, mimagetime); 
values.put(mediastore.images.imagecolumns.date_modified, mimagetime); 
values.put(mediastore.images.imagecolumns.mime_type, "image/png"); 
uri uri = resolver.insert(mediastore.images.media.external_content_uri, values); 
intent sharingintent = new intent(intent.action_send); 
sharingintent.settype("image/png"); 
sharingintent.putextra(intent.extra_stream, uri); 
intent chooserintent = intent.createchooser(sharingintent, null); 
chooserintent.addflags(intent.flag_activity_clear_task 
| intent.flag_activity_new_task); 
mnotificationbuilder.addaction(r.drawable.ic_menu_share, 
r.getstring(com.android.internal.r.string.share), 
pendingintent.getactivity(context, 0, chooserintent, 
pendingintent.flag_cancel_current)); 
outputstream out = resolver.openoutputstream(uri); 
image.compress(bitmap.compressformat.png, 100, out); 
out.flush(); 
out.close(); 
// update file size in the database 
values.clear(); 
values.put(mediastore.images.imagecolumns.size, new file(mimagefilepath).length()); 
resolver.update(uri, values, null, null); 
params[0].imageuri = uri; 
params[0].result = 0; 
} catch (exception e) { 
// ioexception/unsupportedoperationexception may be thrown if external storage is not 
// mounted 
params[0].result = 1; 
} 
return params[0]; 
} 
@override 
protected void onpostexecute(saveimageinbackgrounddata params) { 
if (params.result > 0) { 
// show a message that we've failed to save the image to disk 
globalscreenshot.notifyscreenshoterror(params.context, mnotificationmanager); 
} else { 
// show the final notification to indicate screenshot saved 
resources r = params.context.getresources(); 
// create the intent to show the screenshot in gallery 
intent launchintent = new intent(intent.action_view); 
launchintent.setdataandtype(params.imageuri, "image/png"); 
launchintent.setflags(intent.flag_activity_new_task); 
mnotificationbuilder 
.setcontenttitle(r.getstring(r.string.screenshot_saved_title)) 
.setcontenttext(r.getstring(r.string.screenshot_saved_text)) 
.setcontentintent(pendingintent.getactivity(params.context, 0, launchintent, 0)) 
.setwhen(system.currenttimemillis()) 
.setautocancel(true); 
notification n = mnotificationbuilder.build(); 
n.flags &= ~notification.flag_no_clear; 
mnotificationmanager.notify(mnotificationid, n); 
} 
params.finisher.run(); 
} 
} 
/** 
* todo: 
* - performance when over gl surfaces? ie. gallery 
* - what do we say in the toast? which icon do we get if the user uses another 
* type of gallery? 
*/ 
class globalscreenshot { 
private static final int screenshot_notification_id = 789; 
private static final int screenshot_flash_to_peak_duration = 130; 
private static final int screenshot_drop_in_duration = 430; 
private static final int screenshot_drop_out_delay = 500; 
private static final int screenshot_drop_out_duration = 430; 
private static final int screenshot_drop_out_scale_duration = 370; 
private static final int screenshot_fast_drop_out_duration = 320; 
private static final float background_alpha = 0.5f; 
private static final float screenshot_scale = 1f; 
private static final float screenshot_drop_in_min_scale = screenshot_scale * 0.725f; 
private static final float screenshot_drop_out_min_scale = screenshot_scale * 0.45f; 
private static final float screenshot_fast_drop_out_min_scale = screenshot_scale * 0.6f; 
private static final float screenshot_drop_out_min_scale_offset = 0f; 
private context mcontext; 
private windowmanager mwindowmanager; 
private windowmanager.layoutparams mwindowlayoutparams; 
private notificationmanager mnotificationmanager; 
private display mdisplay; 
private displaymetrics mdisplaymetrics; 
private matrix mdisplaymatrix; 
private bitmap mscreenbitmap; 
private view mscreenshotlayout; 
private imageview mbackgroundview; 
private imageview mscreenshotview; 
private imageview mscreenshotflash; 
private animatorset mscreenshotanimation; 
private int mnotificationiconsize; 
private float mbgpadding; 
private float mbgpaddingscale; 
private mediaactionsound mcamerasound; 
/** 
* @param context everything needs a context :( 
*/ 
public globalscreenshot(context context) { 
resources r = context.getresources(); 
mcontext = context; 
layoutinflater layoutinflater = (layoutinflater) 
context.getsystemservice(context.layout_inflater_service); 
// inflate the screenshot layout 
mdisplaymatrix = new matrix(); 
mscreenshotlayout = layoutinflater.inflate(r.layout.global_screenshot, null); 
mbackgroundview = (imageview) mscreenshotlayout.findviewbyid(r.id.global_screenshot_background); 
mscreenshotview = (imageview) mscreenshotlayout.findviewbyid(r.id.global_screenshot); 
mscreenshotflash = (imageview) mscreenshotlayout.findviewbyid(r.id.global_screenshot_flash); 
mscreenshotlayout.setfocusable(true); 
mscreenshotlayout.setontouchlistener(new view.ontouchlistener() { 
@override 
public boolean ontouch(view v, motionevent event) { 
// intercept and ignore all touch events 
return true; 
} 
}); 
// setup the window that we are going to use 
mwindowlayoutparams = new windowmanager.layoutparams( 
viewgroup.layoutparams.match_parent, viewgroup.layoutparams.match_parent, 0, 0, 
windowmanager.layoutparams.type_secure_system_overlay, 
windowmanager.layoutparams.flag_fullscreen 
| windowmanager.layoutparams.flag_hardware_accelerated 
| windowmanager.layoutparams.flag_layout_in_screen 
| windowmanager.layoutparams.flag_show_when_locked, 
pixelformat.translucent); 
mwindowlayoutparams.settitle("screenshotanimation"); 
mwindowmanager = (windowmanager) context.getsystemservice(context.window_service); 
mnotificationmanager = 
(notificationmanager) context.getsystemservice(context.notification_service); 
mdisplay = mwindowmanager.getdefaultdisplay(); 
mdisplaymetrics = new displaymetrics(); 
mdisplay.getrealmetrics(mdisplaymetrics); 
// get the various target sizes 
mnotificationiconsize = 
r.getdimensionpixelsize(android.r.dimen.notification_large_icon_height); 
// scale has to account for both sides of the bg 
mbgpadding = (float) r.getdimensionpixelsize(r.dimen.global_screenshot_bg_padding); 
mbgpaddingscale = mbgpadding / mdisplaymetrics.widthpixels; 
// setup the camera shutter sound 
mcamerasound = new mediaactionsound(); 
mcamerasound.load(mediaactionsound.shutter_click); 
} 
/** 
* creates a new worker thread and saves the screenshot to the media store. 
*/ 
private void savescreenshotinworkerthread(runnable finisher) { 
saveimageinbackgrounddata data = new saveimageinbackgrounddata(); 
data.context = mcontext; 
data.image = mscreenbitmap; 
data.iconsize = mnotificationiconsize; 
data.finisher = finisher; 
new saveimageinbackgroundtask(mcontext, data, mnotificationmanager, 
screenshot_notification_id).execute(data); 
} 
/** 
* @return the current display rotation in degrees 
*/ 
private float getdegreesforrotation(int value) { 
switch (value) { 
case surface.rotation_90: 
return 360f - 90f; 
case surface.rotation_180: 
return 360f - 180f; 
case surface.rotation_270: 
return 360f - 270f; 
} 
return 0f; 
} 
/** 
* takes a screenshot of the current display and shows an animation. 
*/ 
void takescreenshot(runnable finisher, boolean statusbarvisible, boolean navbarvisible) { 
// we need to orient the screenshot correctly (and the surface api seems to take screenshots 
// only in the natural orientation of the device :!) 
mdisplay.getrealmetrics(mdisplaymetrics); 
float[] dims = {mdisplaymetrics.widthpixels, mdisplaymetrics.heightpixels}; 
float degrees = getdegreesforrotation(mdisplay.getrotation()); 
boolean requiresrotation = (degrees > 0); 
if (requiresrotation) { 
// get the dimensions of the device in its native orientation 
mdisplaymatrix.reset(); 
mdisplaymatrix.prerotate(-degrees); 
mdisplaymatrix.mappoints(dims); 
dims[0] = math.abs(dims[0]); 
dims[1] = math.abs(dims[1]); 
} 
// take the screenshot 
mscreenbitmap = surface.screenshot((int) dims[0], (int) dims[1]); 
if (mscreenbitmap == null) { 
notifyscreenshoterror(mcontext, mnotificationmanager); 
finisher.run(); 
return; 
} 
if (requiresrotation) { 
// rotate the screenshot to the current orientation 
bitmap ss = bitmap.createbitmap(mdisplaymetrics.widthpixels, 
mdisplaymetrics.heightpixels, bitmap.config.argb_8888); 
canvas c = new canvas(ss); 
c.translate(ss.getwidth() / 2, ss.getheight() / 2); 
c.rotate(degrees); 
c.translate(-dims[0] / 2, -dims[1] / 2); 
c.drawbitmap(mscreenbitmap, 0, 0, null); 
c.setbitmap(null); 
mscreenbitmap = ss; 
} 
// optimizations 
mscreenbitmap.sethasalpha(false); 
mscreenbitmap.preparetodraw(); 
// start the post-screenshot animation 
startanimation(finisher, mdisplaymetrics.widthpixels, mdisplaymetrics.heightpixels, 
statusbarvisible, navbarvisible); 
} 
/** 
* starts the animation after taking the screenshot 
*/ 
private void startanimation(final runnable finisher, int w, int h, boolean statusbarvisible, 
boolean navbarvisible) { 
// add the view for the animation 
mscreenshotview.setimagebitmap(mscreenbitmap); 
mscreenshotlayout.requestfocus(); 
// setup the animation with the screenshot just taken 
if (mscreenshotanimation != null) { 
mscreenshotanimation.end(); 
} 
mwindowmanager.addview(mscreenshotlayout, mwindowlayoutparams); 
valueanimator screenshotdropinanim = createscreenshotdropinanimation(); 
valueanimator screenshotfadeoutanim = createscreenshotdropoutanimation(w, h, 
statusbarvisible, navbarvisible); 
mscreenshotanimation = new animatorset(); 
mscreenshotanimation.playsequentially(screenshotdropinanim, screenshotfadeoutanim); 
mscreenshotanimation.addlistener(new animatorlisteneradapter() { 
@override 
public void onanimationend(animator animation) { 
// save the screenshot once we have a bit of time now 
savescreenshotinworkerthread(finisher); 
mwindowmanager.removeview(mscreenshotlayout); 
} 
}); 
mscreenshotlayout.post(new runnable() { 
@override 
public void run() { 
// play the shutter sound to notify that we've taken a screenshot 
mcamerasound.play(mediaactionsound.shutter_click); 
mscreenshotview.setlayertype(view.layer_type_hardware, null); 
mscreenshotview.buildlayer(); 
mscreenshotanimation.start(); 
} 
}); 
} 
private valueanimator createscreenshotdropinanimation() { 
final float flashpeakdurationpct = ((float) (screenshot_flash_to_peak_duration) 
/ screenshot_drop_in_duration); 
final float flashdurationpct = 2f * flashpeakdurationpct; 
final interpolator flashalphainterpolator = new interpolator() { 
@override 
public float getinterpolation(float x) { 
// flash the flash view in and out quickly 
if (x <= flashdurationpct) { 
return (float) math.sin(math.pi * (x / flashdurationpct)); 
} 
return 0; 
} 
}; 
final interpolator scaleinterpolator = new interpolator() { 
@override 
public float getinterpolation(float x) { 
// we start scaling when the flash is at it's peak 
if (x < flashpeakdurationpct) { 
return 0; 
} 
return (x - flashdurationpct) / (1f - flashdurationpct); 
} 
}; 
valueanimator anim = valueanimator.offloat(0f, 1f); 
anim.setduration(screenshot_drop_in_duration); 
anim.addlistener(new animatorlisteneradapter() { 
@override 
public void onanimationstart(animator animation) { 
mbackgroundview.setalpha(0f); 
mbackgroundview.setvisibility(view.visible); 
mscreenshotview.setalpha(0f); 
mscreenshotview.settranslationx(0f); 
mscreenshotview.settranslationy(0f); 
mscreenshotview.setscalex(screenshot_scale + mbgpaddingscale); 
mscreenshotview.setscaley(screenshot_scale + mbgpaddingscale); 
mscreenshotview.setvisibility(view.visible); 
mscreenshotflash.setalpha(0f); 
mscreenshotflash.setvisibility(view.visible); 
} 
@override 
public void onanimationend(android.animation.animator animation) { 
mscreenshotflash.setvisibility(view.gone); 
} 
}); 
anim.addupdatelistener(new animatorupdatelistener() { 
@override 
public void onanimationupdate(valueanimator animation) { 
float t = (float) animation.getanimatedvalue(); 
float scalet = (screenshot_scale + mbgpaddingscale) 
- scaleinterpolator.getinterpolation(t) 
* (screenshot_scale - screenshot_drop_in_min_scale); 
mbackgroundview.setalpha(scaleinterpolator.getinterpolation(t) * background_alpha); 
mscreenshotview.setalpha(t); 
mscreenshotview.setscalex(scalet); 
mscreenshotview.setscaley(scalet); 
mscreenshotflash.setalpha(flashalphainterpolator.getinterpolation(t)); 
} 
}); 
return anim; 
} 
private valueanimator createscreenshotdropoutanimation(int w, int h, boolean statusbarvisible, 
boolean navbarvisible) { 
valueanimator anim = valueanimator.offloat(0f, 1f); 
anim.setstartdelay(screenshot_drop_out_delay); 
anim.addlistener(new animatorlisteneradapter() { 
@override 
public void onanimationend(animator animation) { 
mbackgroundview.setvisibility(view.gone); 
mscreenshotview.setvisibility(view.gone); 
mscreenshotview.setlayertype(view.layer_type_none, null); 
} 
}); 
if (!statusbarvisible || !navbarvisible) { 
// there is no status bar/nav bar, so just fade the screenshot away in place 
anim.setduration(screenshot_fast_drop_out_duration); 
anim.addupdatelistener(new animatorupdatelistener() { 
@override 
public void onanimationupdate(valueanimator animation) { 
float t = (float) animation.getanimatedvalue(); 
float scalet = (screenshot_drop_in_min_scale + mbgpaddingscale) 
- t * (screenshot_drop_in_min_scale - screenshot_fast_drop_out_min_scale); 
mbackgroundview.setalpha((1f - t) * background_alpha); 
mscreenshotview.setalpha(1f - t); 
mscreenshotview.setscalex(scalet); 
mscreenshotview.setscaley(scalet); 
} 
}); 
} else { 
// in the case where there is a status bar, animate to the origin of the bar (top-left) 
final float scaledurationpct = (float) screenshot_drop_out_scale_duration 
/ screenshot_drop_out_duration; 
final interpolator scaleinterpolator = new interpolator() { 
@override 
public float getinterpolation(float x) { 
if (x < scaledurationpct) { 
// decelerate, and scale the input accordingly 
return (float) (1f - math.pow(1f - (x / scaledurationpct), 2f)); 
} 
return 1f; 
} 
}; 
// determine the bounds of how to scale 
float halfscreenwidth = (w - 2f * mbgpadding) / 2f; 
float halfscreenheight = (h - 2f * mbgpadding) / 2f; 
final float offsetpct = screenshot_drop_out_min_scale_offset; 
final pointf finalpos = new pointf( 
-halfscreenwidth + (screenshot_drop_out_min_scale + offsetpct) * halfscreenwidth, 
-halfscreenheight + (screenshot_drop_out_min_scale + offsetpct) * halfscreenheight); 
// animate the screenshot to the status bar 
anim.setduration(screenshot_drop_out_duration); 
anim.addupdatelistener(new animatorupdatelistener() { 
@override 
public void onanimationupdate(valueanimator animation) { 
float t = (float) animation.getanimatedvalue(); 
float scalet = (screenshot_drop_in_min_scale + mbgpaddingscale) 
- scaleinterpolator.getinterpolation(t) 
* (screenshot_drop_in_min_scale - screenshot_drop_out_min_scale); 
mbackgroundview.setalpha((1f - t) * background_alpha); 
mscreenshotview.setalpha(1f - scaleinterpolator.getinterpolation(t)); 
mscreenshotview.setscalex(scalet); 
mscreenshotview.setscaley(scalet); 
mscreenshotview.settranslationx(t * finalpos.x); 
mscreenshotview.settranslationy(t * finalpos.y); 
} 
}); 
} 
return anim; 
} 
static void notifyscreenshoterror(context context, notificationmanager nmanager) { 
resources r = context.getresources(); 
// clear all existing notification, compose the new notification and show it 
notification n = new notification.builder(context) 
.setticker(r.getstring(r.string.screenshot_failed_title)) 
.setcontenttitle(r.getstring(r.string.screenshot_failed_title)) 
.setcontenttext(r.getstring(r.string.screenshot_failed_text)) 
.setsmallicon(r.drawable.stat_notify_image_error) 
.setwhen(system.currenttimemillis()) 
.setautocancel(true) 
.getnotification(); 
nmanager.notify(screenshot_notification_id, n); 
}

可以从这部分源码中,去寻找解决办法。

其中有一个很重要的方法就是

mscreenbitmap = surface.screenshot((int) dims[0], (int) dims[1]);

这个是可以截图的,但是是隐藏的代码,不提供外面调用的。

注意需要在androidmanifest.xml中加入代码:android:shareduserid="android.uid.system" 进行后续开发。