Preface
Android devices generally come with screenshot function, but the user experience is not good. It will be accompanied by the status bar 📶, 🔋, time and date, other unnecessary page information, and other information that does not match the content that the user wants to take will also be saved. Usually, after taking a screenshot, the user will crop it again before he can share the real needs.
Therefore, our technical research and development will encounter targeted screenshot functions.
1. getDrawingCache
getDrawingCache() is one of the screenshot methods, which is easy to use and mainly aims at in-app screenshots.
1. Create a View
fun getShareView() : View { val shareView: View = (context).inflate(.share_layout, null) //content... return shareView }
Note: Generally, everyone’s idea is to create Views in click events, and they may encounter situations where the network image has not been loaded yet. Therefore, it is recommended to do delay processing, or create it before clicking.
2. Test and drawing
public static void layoutView(View v, int width, int height) { (0, 0, width, height); int measuredWidth = (width, ); int measuredHeight = (height, ); (measuredWidth, measuredHeight); (0, 0, (), ()); }
If you do not follow this method, there will be no view when converting bitmap (black screen).
Calling method:
// Set the dp width and height of the viewlayoutView(share_view, dp2px(210), dp2px(180));
public static int dp2px(float dp) { float scale = ().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); }
3. Convert Bitmap
public static Bitmap getCacheBitmapFromView(View view) { final boolean drawingCacheEnabled = true; (drawingCacheEnabled); //Set the background color //(().getResources().getColor(.half_white)); (drawingCacheEnabled); final Bitmap drawingCache = (); Bitmap bitmap; if (drawingCache != null) { bitmap = (drawingCache); (false); } else { bitmap = null; } return bitmap; }
2. Black screen problem
Generally speaking, the above code can achieve the effect normally. But sometimes, there will be problems with generating Bitmap (Bitmap is all black). The main reason is that the value of drawingCache is greater than the value given by the system. We can take a look at a piece of code in the buildDrawingCache() method:
//The width and height of the view to be cacheif (width <= 0 || height <= 0 || //The drawingCache size currently required (width * height * (opaque && !translucentWindow ? 2 : 4) > //What you get is the maximum DrawingCache value provided by the system (mContext).getScaledMaximumDrawingCacheSize())) { destroyDrawingCache(); return; }
When the required drawingCache > the maximum DrawingCache value provided by the system, a problem will occur when generating the Bitmap, and the obtained Bitmap is null.
So you can solve the problem by just modifying the required cache value. So we introduce the second method:
Solution:
public static Bitmap convertViewToBitmap(View view){ ((0, ), (0, )); (0, 0, (), ()); (); Bitmap bitmap = (); return bitmap; }
The view uses the "getMeasuredWidth()" and "getMeasuredHeight()" methods to calculate the length and width. At this point, Bitmap can be obtained correctly.
3. Source code analysis
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.TRACE_TAG_VIEW)) { (Trace.TRACE_TAG_VIEW, "buildDrawingCache/SW Layer for " + getClass().getSimpleName()); } try { buildDrawingCacheImpl(autoScale); } finally { (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 && ; if (autoScale && scalingRequired) { width = (int) ((width * ) + 0.5f); height = (int) ((height * ) + 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 = (mContext).getScaledMaximumDrawingCacheSize(); if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) { if (width > 0 && height > 0) { (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; } ..TestdrawingCacheOriginal data operation.. try { bitmap = ((), width, height, quality); (getResources().getDisplayMetrics().densityDpi); if (autoScale) { mDrawingCache = bitmap; } else { mUnscaledDrawingCache = bitmap; } if (opaque && use32BitCache) (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; } ..implementBitmapWriteautoScale ? mDrawingCache : mUnscaledDrawingCacheoperate.. }
From the above source code, you can see that there are four conditions for getDrawingcache = null:
1. (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING is true
2. SetDrawingCacheEnabled(true) is not set
3. width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize is true
4、OutOfMemory
Except for the first condition, the rest will only be triggered when the buildDrawingCache is executed. The following conditions three are analyzed. Since the sub-layout can be displayed normally, it must meet width>0 and height>0. DrawingCacheSize is definitely a fixed value, which is the maximum drawn cache value allowed by the current device system. The calculation method of projectedBitmapSize is width * height * (opaque && !use32BitCache ? 2 : 4), as the name implies, it is the size of the currently planned cached image. (opaque && !use32BitCache ? 2 : 4) It is impossible to be 0, and it is also impossible to cause the planned cache value to become larger. width is the width of the screen. This condition without change, then it is certain that there is an exception in height. For the calculation of view height, the Android source code is as follows:
@(category = "layout") public final int getHeight() { return mBottom - mTop; }
The height of a View getHeight() is the bottom-height, where mBottom refers to the distance from the bottom edge of the view itself to the top edge of the parent view, and mTop refers to the distance from the top edge of the view itself to the top edge of the parent view.
4. View to Canvas to Bitmap
Bitmap bitmap = ((), (), .ARGB_8888); Canvas canvas = new Canvas(bitmap); (((), ), ((), )); ((int) (), (int) (), (int) () + (), (int) () + ()); (canvas); return bitmap;
This is the article about converting Android View to Bitmap to implement in-app screenshot function. For more related content to convert Android View to Bitmap, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!