您好,登錄后才能下訂單哦!
前言
截圖是我們日常開發經常會遇到的一個功能,最近工作遇到的需求又升級了,需要實現長圖文的截圖功能,經常查找相關資料終于實現了,支持截取微博、知乎、今日頭條等第三方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 h2, int h3, 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 < h2) { getRowPixels(pixels_0, index, pixels_1, index, len); } else { getRowPixels(pixels_0, index, pixels_2, index - h2 + samePart, len); } index++; } /* -------------------- merge the difference ----------------------- */ unlockPixel(env, bmp0); unlockPixel(env, bmp1); unlockPixel(env, bmp2); }
功能實現上沒什么難度,感覺更多的是得選好實現的策略,比如如何優雅的監聽懸浮窗外的Touch事件,如何優雅的實現一個“定點”截圖的策略,如何優雅的對比兩個Bitmap的不同地方,進行拼接。
源碼傳送門:https://github.com/zengfw/LongScreenShot (本地下載)
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。