Android View轉(zhuǎn)換為Bitmap實(shí)現(xiàn)應(yīng)用內(nèi)截屏功能
前言
安卓設(shè)備一般都自帶截圖功能,但是用戶體驗(yàn)有不好之處。就是會(huì)連帶著狀態(tài)欄??、??、時(shí)間日期、其他不必要頁面中信息,等等與用戶想截屏的內(nèi)容不符的信息也會(huì)被保存下來。通常,截圖后用戶會(huì)再次裁剪一次才能想把真正需求分享出去。
因此,咱們技術(shù)研發(fā)會(huì)遇到針對(duì)性的會(huì)做一些應(yīng)用內(nèi)的截屏功能。
一、getDrawingCache
getDrawingCache()是其中一種截圖手段,使用方便,主要針對(duì)應(yīng)用內(nèi)截圖。
1、創(chuàng)建View
fun getShareView() : View { val shareView: View = LayoutInflater.from(context).inflate(R.layout.share_layout, null) //內(nèi)容... return shareView }
注意:一般大家實(shí)現(xiàn)思路都是點(diǎn)擊事件里進(jìn)行創(chuàng)建View繪制,很可能會(huì)遇到網(wǎng)絡(luò)圖片還未加載完的情況。因此,建議做延遲處理,或在點(diǎn)擊前前置創(chuàng)建好。
2、測(cè)試和繪制
public static void layoutView(View v, int width, int height) { v.layout(0, 0, width, height); int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); v.measure(measuredWidth, measuredHeight); v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); }
如果不走這個(gè)方法,bitmap轉(zhuǎn)換時(shí)會(huì)沒有視圖(黑屏情況)。
調(diào)用方法:
// 設(shè)置視圖的dp寬高 layoutView(share_view, dp2px(210), dp2px(180));
public static int dp2px(float dp) { float scale = Resources.getSystem().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); }
3、轉(zhuǎn)換Bitmap
public static Bitmap getCacheBitmapFromView(View view) { final boolean drawingCacheEnabled = true; view.setDrawingCacheEnabled(drawingCacheEnabled); //設(shè)置背景色 //view.setBackgroundColor(CommonUtils.getContext().getResources().getColor(R.color.half_white)); view.buildDrawingCache(drawingCacheEnabled); final Bitmap drawingCache = view.getDrawingCache(); Bitmap bitmap; if (drawingCache != null) { bitmap = Bitmap.createBitmap(drawingCache); view.setDrawingCacheEnabled(false); } else { bitmap = null; } return bitmap; }
二、黑屏問題
一般情況下,上面的代碼能夠正常實(shí)現(xiàn)效果。但有時(shí)候,生成Bitmap會(huì)出現(xiàn)問題(Bitmap全黑色)。主要原因是drawingCache的值大于系統(tǒng)給定的值。我們可以看一下buildDrawingCache()方法中的一段代碼:
//所要cache的view繪制的寬度和高度 if (width <= 0 || height <= 0 || //計(jì)算的是當(dāng)前所需要的drawingCache 大小 (width * height * (opaque && !translucentWindow ? 2 : 4) > //得到的是系統(tǒng)所提供的最大的DrawingCache的值 ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { destroyDrawingCache(); return; }
當(dāng)所需要的drawingCache > 系統(tǒng)所提供的最大DrawingCache值時(shí),生成Bitmap就會(huì)出現(xiàn)問題,此時(shí)獲取的Bitmap就為null。
所以在只需要修改所需的cache值就可以解決問題了。于是我們引入第二種方法:
解決方案:
public static Bitmap convertViewToBitmap(View view){ view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.buildDrawingCache(); Bitmap bitmap = view.getDrawingCache(); return bitmap; }
view 使用 "getMeasuredWidth()"、 "getMeasuredHeight()"方法計(jì)算長(zhǎng)寬。此時(shí),Bitmap就能正確獲取了。
三、源碼分析
public void buildDrawingCache() { buildDrawingCache(false); } public Bitmap getDrawingCache() { return getDrawingCache(false); } public Bitmap getDrawingCache(boolean autoScale) { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null; } if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { buildDrawingCache(autoScale); } return autoScale ? mDrawingCache : mUnscaledDrawingCache; } public void buildDrawingCache(boolean autoScale) { if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null)) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "buildDrawingCache/SW Layer for " + getClass().getSimpleName()); } try { buildDrawingCacheImpl(autoScale); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } } private void buildDrawingCacheImpl(boolean autoScale) { mCachingFailed = false; int width = mRight - mLeft; int height = mBottom - mTop; final AttachInfo attachInfo = mAttachInfo; final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired; if (autoScale && scalingRequired) { width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); } final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque(); final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache; final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4); final long drawingCacheSize = ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize(); if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) { if (width > 0 && height > 0) { Log.w(VIEW_LOG_TAG, getClass().getSimpleName() + " not displayed because it is" + " too large to fit into a software layer (or drawing cache), needs " + projectedBitmapSize + " bytes, only " + drawingCacheSize + " available"); } destroyDrawingCache(); mCachingFailed = true; return; } ..檢測(cè)drawingCache原有數(shù)據(jù)操作.. try { bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(), width, height, quality); bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); if (autoScale) { mDrawingCache = bitmap; } else { mUnscaledDrawingCache = bitmap; } if (opaque && use32BitCache) bitmap.setHasAlpha(false); } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the // view hierarchy if (autoScale) { mDrawingCache = null; } else { mUnscaledDrawingCache = null; } mCachingFailed = true; return; } ..執(zhí)行Bitmap寫入autoScale ? mDrawingCache : mUnscaledDrawingCache操作.. }
從以上源碼中,可以看到getDrawingcache = null的條件共有四個(gè):
1、(mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING為true
2、沒有設(shè)置setDrawingCacheEnabled(true)
3、width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize為true
4、OutOfMemory
除了第一個(gè)條件,其他的都是buildDrawingCache執(zhí)行時(shí)才會(huì)觸發(fā)。下面來分析下條件三。既然子布局可以正常顯示,那么一定是滿足width>0和height>0的, drawingCacheSize肯定是一個(gè)固定值,就是當(dāng)前設(shè)備系統(tǒng)所允許的最大繪制緩存值。projectedBitmapSize的計(jì)算方式為width * height * (opaque && !use32BitCache ? 2 : 4),顧名思義,就是當(dāng)前計(jì)劃緩存的圖片大小,(opaque && !use32BitCache ? 2 : 4)不可能為0,也不可能是導(dǎo)致計(jì)劃緩存值變大的主因,width就是屏幕的寬,這個(gè)沒有變動(dòng)的條件,那么可以肯定就是height出現(xiàn)了異常,對(duì)于視圖高度的計(jì)算,android源碼表示如下:
@ViewDebug.ExportedProperty(category = "layout") public final int getHeight() { return mBottom - mTop; }
一個(gè)View的高度getHeight()就是底-高,其中mBottom指的是視圖自身的底邊到父視圖頂邊的距離,mTop指的是視圖自身的頂邊到父視圖頂邊的距離。
四、View轉(zhuǎn)Canvas轉(zhuǎn)Bitmap
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); view.measure(View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(view.getHeight(), View.MeasureSpec.EXACTLY)); view.layout((int) view.getX(), (int) view.getY(), (int) view.getX() + view.getMeasuredWidth(), (int) view.getY() + view.getMeasuredHeight()); view.draw(canvas); return bitmap;
到此這篇關(guān)于Android View轉(zhuǎn)換為Bitmap實(shí)現(xiàn)應(yīng)用內(nèi)截屏功能的文章就介紹到這了,更多相關(guān)Android View轉(zhuǎn)換為Bitmap內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 下的 QuickJS Binding 庫特性使用詳解
這篇文章主要介紹了Android 下的 QuickJS Binding 庫特性使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Android開發(fā)中使用顏色矩陣改變圖片顏色,透明度及亮度的方法
這篇文章主要介紹了Android開發(fā)中使用顏色矩陣改變圖片顏色,透明度及亮度的方法,涉及Android針對(duì)圖片的讀取、運(yùn)算、設(shè)置等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10Java程序員轉(zhuǎn)Android開發(fā)必讀經(jīng)驗(yàn)一份
小編最近幾日偷偷的發(fā)現(xiàn)部分Java程序員想轉(zhuǎn)安卓開發(fā),故此加緊補(bǔ)充知識(shí),為大家搜集資料,積極整理前人的經(jīng)驗(yàn),希望可以給正處于困惑中的你,帶來些許的幫助。2017-11-11