Android View轉換為Bitmap實現(xiàn)應用內(nèi)截屏功能
前言
安卓設備一般都自帶截圖功能,但是用戶體驗有不好之處。就是會連帶著狀態(tài)欄??、??、時間日期、其他不必要頁面中信息,等等與用戶想截屏的內(nèi)容不符的信息也會被保存下來。通常,截圖后用戶會再次裁剪一次才能想把真正需求分享出去。
因此,咱們技術研發(fā)會遇到針對性的會做一些應用內(nèi)的截屏功能。
一、getDrawingCache
getDrawingCache()是其中一種截圖手段,使用方便,主要針對應用內(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
}注意:一般大家實現(xiàn)思路都是點擊事件里進行創(chuàng)建View繪制,很可能會遇到網(wǎng)絡圖片還未加載完的情況。因此,建議做延遲處理,或在點擊前前置創(chuàng)建好。
2、測試和繪制
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());
}如果不走這個方法,bitmap轉換時會沒有視圖(黑屏情況)。
調用方法:
// 設置視圖的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、轉換Bitmap
public static Bitmap getCacheBitmapFromView(View view) {
final boolean drawingCacheEnabled = true;
view.setDrawingCacheEnabled(drawingCacheEnabled);
//設置背景色 //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;
}二、黑屏問題
一般情況下,上面的代碼能夠正常實現(xiàn)效果。但有時候,生成Bitmap會出現(xiàn)問題(Bitmap全黑色)。主要原因是drawingCache的值大于系統(tǒng)給定的值。我們可以看一下buildDrawingCache()方法中的一段代碼:
//所要cache的view繪制的寬度和高度
if (width <= 0 || height <= 0 ||
//計算的是當前所需要的drawingCache 大小
(width * height * (opaque && !translucentWindow ? 2 : 4) >
//得到的是系統(tǒng)所提供的最大的DrawingCache的值
ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
destroyDrawingCache();
return;
}當所需要的drawingCache > 系統(tǒng)所提供的最大DrawingCache值時,生成Bitmap就會出現(xiàn)問題,此時獲取的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()"方法計算長寬。此時,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;
}
..檢測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的條件共有四個:
1、(mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING為true
2、沒有設置setDrawingCacheEnabled(true)
3、width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize為true
4、OutOfMemory
除了第一個條件,其他的都是buildDrawingCache執(zhí)行時才會觸發(fā)。下面來分析下條件三。既然子布局可以正常顯示,那么一定是滿足width>0和height>0的, drawingCacheSize肯定是一個固定值,就是當前設備系統(tǒng)所允許的最大繪制緩存值。projectedBitmapSize的計算方式為width * height * (opaque && !use32BitCache ? 2 : 4),顧名思義,就是當前計劃緩存的圖片大小,(opaque && !use32BitCache ? 2 : 4)不可能為0,也不可能是導致計劃緩存值變大的主因,width就是屏幕的寬,這個沒有變動的條件,那么可以肯定就是height出現(xiàn)了異常,對于視圖高度的計算,android源碼表示如下:
@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}一個View的高度getHeight()就是底-高,其中mBottom指的是視圖自身的底邊到父視圖頂邊的距離,mTop指的是視圖自身的頂邊到父視圖頂邊的距離。
四、View轉Canvas轉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;到此這篇關于Android View轉換為Bitmap實現(xiàn)應用內(nèi)截屏功能的文章就介紹到這了,更多相關Android View轉換為Bitmap內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Kotlin + Flow 實現(xiàn)Android 應用初始化任務啟動庫
這篇文章主要介紹了Kotlin + Flow 實現(xiàn)Android 應用初始化任務啟動庫的方法,幫助大家更好的理解和學習使用Android,感興趣的朋友可以了解下2021-03-03
Android數(shù)據(jù)雙向綁定原理實現(xiàn)和應用場景
本文介紹了Android數(shù)據(jù)雙向綁定的原理和實現(xiàn)方式,包括基于觀察者模式和數(shù)據(jù)綁定框架的實現(xiàn)方法,以及應用場景和優(yōu)缺點的分析,幫助開發(fā)者了解和應用數(shù)據(jù)雙向綁定技術,提升應用的交互性和響應速度2023-04-04
超簡單Android集成華為HMS Scankit 掃碼SDK實現(xiàn)掃一掃二維碼
這篇文章主要介紹了超簡單Android集成華為HMS Scankit 掃碼SDK實現(xiàn)掃一掃二維碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-03-03
Android GridView實現(xiàn)滾動到指定位置的方法
這篇文章主要介紹了Android GridView實現(xiàn)滾動到指定位置的方法,本文介紹了4個相關的方法,分別對它們做了講解,需要的朋友可以參考下2015-06-06
Android Studio配置國內(nèi)鏡像源(利用hosts)
這篇文章主要介紹了Android Studio配置國內(nèi)鏡像源(利用hosts),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11
詳解Android的內(nèi)存優(yōu)化--LruCache
LruCache是基于Lru算法實現(xiàn)的一種緩存機制。本文對LruCache的概念和實現(xiàn)原理進行介紹,通過實例分析和使用介紹,讓大家更好的了解LruCache,下面跟著小編一起來看下吧2016-12-12

