Android View轉(zhuǎn)換為Bitmap實(shí)現(xiàn)應(yīng)用內(nèi)截屏功能
前言
安卓設(shè)備一般都自帶截圖功能,但是用戶體驗(yàn)有不好之處。就是會(huì)連帶著狀態(tài)欄??、??、時(shí)間日期、其他不必要頁(yè)面中信息,等等與用戶想截屏的內(nèi)容不符的信息也會(huì)被保存下來(lái)。通常,截圖后用戶會(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ì)沒(méi)有視圖(黑屏情況)。
調(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;
}二、黑屏問(wèn)題
一般情況下,上面的代碼能夠正常實(shí)現(xiàn)效果。但有時(shí)候,生成Bitmap會(huì)出現(xiàn)問(wè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)問(wèn)題,此時(shí)獲取的Bitmap就為null。
所以在只需要修改所需的cache值就可以解決問(wèn)題了。于是我們引入第二種方法:
解決方案:
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寫(xiě)入autoScale ? mDrawingCache : mUnscaledDrawingCache操作..
}從以上源碼中,可以看到getDrawingcache = null的條件共有四個(gè):
1、(mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING為true
2、沒(méi)有設(shè)置setDrawingCacheEnabled(true)
3、width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize為true
4、OutOfMemory
除了第一個(gè)條件,其他的都是buildDrawingCache執(zhí)行時(shí)才會(huì)觸發(fā)。下面來(lái)分析下條件三。既然子布局可以正常顯示,那么一定是滿足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è)沒(méi)有變動(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)文章
Kotlin + Flow 實(shí)現(xiàn)Android 應(yīng)用初始化任務(wù)啟動(dòng)庫(kù)
這篇文章主要介紹了Kotlin + Flow 實(shí)現(xiàn)Android 應(yīng)用初始化任務(wù)啟動(dòng)庫(kù)的方法,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03
android實(shí)現(xiàn)動(dòng)態(tài)顯示隱藏進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)動(dòng)態(tài)顯示隱藏進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Android數(shù)據(jù)雙向綁定原理實(shí)現(xiàn)和應(yīng)用場(chǎng)景
本文介紹了Android數(shù)據(jù)雙向綁定的原理和實(shí)現(xiàn)方式,包括基于觀察者模式和數(shù)據(jù)綁定框架的實(shí)現(xiàn)方法,以及應(yīng)用場(chǎng)景和優(yōu)缺點(diǎn)的分析,幫助開(kāi)發(fā)者了解和應(yīng)用數(shù)據(jù)雙向綁定技術(shù),提升應(yīng)用的交互性和響應(yīng)速度2023-04-04
超簡(jiǎn)單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼
這篇文章主要介紹了超簡(jiǎn)單Android集成華為HMS Scankit 掃碼SDK實(shí)現(xiàn)掃一掃二維碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
Android GridView實(shí)現(xiàn)滾動(dòng)到指定位置的方法
這篇文章主要介紹了Android GridView實(shí)現(xiàn)滾動(dòng)到指定位置的方法,本文介紹了4個(gè)相關(guān)的方法,分別對(duì)它們做了講解,需要的朋友可以參考下2015-06-06
Android Studio配置國(guó)內(nèi)鏡像源(利用hosts)
這篇文章主要介紹了Android Studio配置國(guó)內(nèi)鏡像源(利用hosts),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Android自定義View實(shí)現(xiàn)五星好評(píng)效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)五星好評(píng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
詳解Android的內(nèi)存優(yōu)化--LruCache
LruCache是基于Lru算法實(shí)現(xiàn)的一種緩存機(jī)制。本文對(duì)LruCache的概念和實(shí)現(xiàn)原理進(jìn)行介紹,通過(guò)實(shí)例分析和使用介紹,讓大家更好的了解LruCache,下面跟著小編一起來(lái)看下吧2016-12-12

