Android 屏幕截屏方法汇总
1、直接使用getwindow().getdecorview().getrootview()
直接使用getwindow().getdecorview().getrootview()是获取当前屏幕的activity。然而对于系统状态栏的信息是截不了,出现一条空白的。如下图:
主要到没,有一条白色边就是系统状态栏。看一下代码,很简单都加了注释了。
//这种方法状态栏是空白,显示不了状态栏的信息 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的吗,那么就有可以获取宽度,高度。生成图片,把它绘制出来的。我拿了直接写的自定义随机验证码的例子来截图。
//保存自定义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" 进行后续开发。
下一篇: .NET中的属性用法分析
推荐阅读
-
Android 屏幕截屏方法汇总
-
Android 四种获取屏幕宽度的方法总结
-
Android中闪屏实现方法小结(普通闪屏、倒计时闪屏、倒计时+动画闪屏)
-
Android切换至SurfaceView时闪屏(黑屏闪一下)以及黑屏移动问题的解决方法
-
Android 使用Shell脚本截屏并自动传到电脑上
-
Android避免内存溢出(Out of Memory)方法汇总
-
Android程序设置成横屏方法 博客分类: android AndroidXML
-
Android程序设置成横屏方法 博客分类: android AndroidXML
-
Android应用开发中触摸屏手势识别的实现方法解析
-
Android6.0 固定屏幕功能实现方法及实例