Android实现长图文截图功能实例代码
前言
截图是我们日常开发经常会遇到的一个功能,最近工作遇到的需求又升级了,需要实现长图文的截图功能,经常查找相关资料终于实现了,支持截取微博、知乎、今日头条等第三方app......
先瞅瞅效果图:
效果图
再瞅瞅最终的长截图:
上一周脑子突然冒出长截图这个功能,想着如何截取如微博,知乎,头条等这些第三方app的界面呢?出于好奇心,花了一周业余时间,撸一个看看。
不就是截屏+拼图,还能有什么难度么?这个。。。好像确实是。
question:
1.如何截屏?
android 5.0 api 21之前,想要系统截屏,是需要root,不过android 5.0开始开放了响应的截屏接口:
mediaprojection (added in api level 21.)
a token granting applications the ability to capture screen contents and/or record system audio. the exact capabilities
granted depend on the type of mediaprojection.
2.如何优雅的截图?
悬浮窗那么小,难道每次我都得滑一定的距离,然后点一次悬浮窗么,理论上可以,但体验不好。估计更多人倾向只要触摸屏幕就可以截图,所以选择监听悬浮窗外的触屏事件。
3.如何监听悬浮窗口外部的touchevent?
悬浮窗外的触屏事件都已经脱离整个应用了,如何监听呢?这里确实卡了些时间,因为确实找不到如何捕获这个事件的好,我感觉这个问题也是最烦的一个,后来来了点灵感,我设置一个全屏的透明背景,然后给这个背景设置ontouch事件,哦!!!恍然大悟,以为这样就可以了?错!!这样会出现整个手机的事件都将被这个透明背景拦截,无法传递到手机桌面,如果非开发人员安装了这个软件。。,告诉他,重新开机吧。。。所以翻了下windowmanager的源码,看到flag参数,把各种flag参数的注释看了遍,最后定位在如下几个flag参数值上。
/** window flag: this window won't ever get key input focus, so the * user can not send key or other button events to it. those will * instead go to whatever focusable window is behind it. this flag * will also enable {@link #flag_not_touch_modal} whether or not that * is explicitly set. * * <p>setting this flag also implies that the window will not need to * interact with * a soft input method, so it will be z-ordered and positioned * independently of any active input method (typically this means it * gets z-ordered on top of the input method, so it can use the full * screen for its content and cover the input method if needed. you * can use {@link #flag_alt_focusable_im} to modify this behavior. */ public static final int flag_not_focusable = 0x00000008; /** window flag: this window can never receive touch events. */ public static final int flag_not_touchable = 0x00000010; /** window flag: even when this window is focusable (its * {@link #flag_not_focusable} is not set), allow any pointer events * outside of the window to be sent to the windows behind it. otherwise * it will consume all pointer events itself, regardless of whether they * are inside of the window. */ public static final int flag_not_touch_modal = 0x00000020; /** window flag: if you have set {@link #flag_not_touch_modal}, you * can set this flag to receive a single special motionevent with * the action * {@link motionevent#action_outside motionevent.action_outside} for * touches that occur outside of your window. note that you will not * receive the full down/move/up gesture, only the location of the * first down as an action_outside. */ public static final int flag_watch_outside_touch = 0x00040000;
在全屏透明背景的环境下,本以为可以监听桌面的down、move、up事件,但是出现事件全部被拦截死在这个透明背景上,无法传到手机桌面,再发现组合这些参数,总结这种思路不可取。
查看注释可以知道设置flag_watch_outside_touch可以在窗口外部(即app外部)接收一个指定事件motionevent#action_outside,但同时,你将无法接收到任何的down、move、up事件。所以,也只能这样了。。有其它高招的兄弟指点下哈。
4.如何控制截屏频次?
在步骤3的基础上,基本可以做一个截图策略了,比如,每接收一次action_outside就截一次图,又或者,每次监听一次action_outside,就进行一次计数器的累加,为了保证截图能承上启下连贯,可以设置阈值为2这样。
5.如何拼图?
这里因人而异了,但目的都一样,将上述步骤所截的图对比出不同的地方,然后把不同的地方拼接起来。出于运算效率考虑,这里我是用jni来实现的,主函数:
jniexport void jnicall java_com_zfw_screenshot_utils_sewutils_merge( jnienv *env, jobject thiz, jobject bmp0, jobject bmp1, jobject bmp2, int h0, int h1, int h2, int samepart, int len) { int *pixels_0 = lockpixel(env, bmp0); int *pixels_1 = lockpixel(env, bmp1); int *pixels_2 = lockpixel(env, bmp2); /* -------------------- merge the difference ----------------------- */ int index = 0; while(index < h0) { if(index < h1) { getrowpixels(pixels_0, index, pixels_1, index, len); } else { getrowpixels(pixels_0, index, pixels_2, index - h1 + samepart, len); } index++; } /* -------------------- merge the difference ----------------------- */ unlockpixel(env, bmp0); unlockpixel(env, bmp1); unlockpixel(env, bmp2); }
功能实现上没什么难度,感觉更多的是得选好实现的策略,比如如何优雅的监听悬浮窗外的touch事件,如何优雅的实现一个“定点”截图的策略,如何优雅的对比两个bitmap的不同地方,进行拼接。
源码传送门:https://github.com/zengfw/longscreenshot (本地下载)
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。