Android screenshots

Android Library, please go to >ScreenShotTools

The principle of Android screenshot: Obtain a Bitmap of the specific area to be captured, draw it on the canvas, save it as a picture for sharing or other purposes

In the screenshot function, sometimes you need to capture the full screen content, sometimes you need to capture more than one screen content (such as: Listview, Scrollview, RecyclerView). The following describes how to get a Bitmap for various scenarios

Common screenshot implementation

Get the DrawingCache of the current Window, that is, the DrawingCache of the decorView

/**
   * shot the current screen ,with the status but the status is trans *
   *
   * @param ctx current activity
   */
  public static Bitmap shotActivity(Activity ctx) {

    View view = ctx.getWindow().getDecorView();
    view.setDrawingCacheEnabled(true);
    view.buildDrawingCache();

    Bitmap bp = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(),
        view.getMeasuredHeight());

    view.setDrawingCacheEnabled(false);
    view.destroyDrawingCache();

    return bp;
  }
Copy the code

Get the DrawingCache of the current View

public static Bitmap getViewBp(View v) {
        if (null == v) {
            return null;
        }
        v.setDrawingCacheEnabled(true);
        v.buildDrawingCache();
        if(Build.VERSION.SDK_INT >= 11) { v.measure(MeasureSpec.makeMeasureSpec(v.getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( v.getHeight(), MeasureSpec.EXACTLY)); v.layout((int) v.getX(), (int) v.getY(), (int) v.getX() + v.getMeasuredWidth(), (int) v.getY() + v.getMeasuredHeight());  }else {
            v.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
        }
        Bitmap b = Bitmap.createBitmap(v.getDrawingCache(), 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());

        v.setDrawingCacheEnabled(false);
        v.destroyDrawingCache();
        return b;
    }
Copy the code

Open source solutions

In a scroll View, if the current View is not fully drawn in the View, we can use the View’s ScrollTo() and ScrollBy() methods to move the canvas, get the DrawingCache of the current View’s visible portion, and finally concatenate it to get its Bitmap. Reference: PGSSoft/scrollscreenshot @ [making].

Scrollview screenshots

Of the three screenshots, ScrollView is the simplest, because ScrollView has only one childView. Although not all of them are displayed on the interface, all of them have been rendered, so you can directly call ScrollView.draw (canvas) to complete the screenshots


  public static Bitmap shotScrollView(ScrollView scrollView) {
    int h = 0;
    Bitmap bitmap = null;
    for (int i = 0; i < scrollView.getChildCount(); i++) {
      h += scrollView.getChildAt(i).getHeight();
      scrollView.getChildAt(i).setBackgroundColor(Color.parseColor("#ffffff"));
    }
    bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.RGB_565);
    final Canvas canvas = new Canvas(bitmap);
    scrollView.draw(canvas);
    return bitmap;
  }
Copy the code

Listview screenshots

ListView is an ItemView that can recycle and reuse items and only draw items on the screen. Stackoverflow recommends using a List view to store items, which is still not a good solution. When there are enough items, oom may occur.

  public static Bitmap shotListView(ListView listview) {

    ListAdapter adapter = listview.getAdapter();
    int itemscount = adapter.getCount();
    int allitemsheight = 0;
    List<Bitmap> bmps = new ArrayList<Bitmap>();

    for (int i = 0; i < itemscount; i++) {

      View childView = adapter.getView(i, null, listview);
      childView.measure(
          View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY),
          View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

      childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
      childView.setDrawingCacheEnabled(true);
      childView.buildDrawingCache();
      bmps.add(childView.getDrawingCache());
      allitemsheight += childView.getMeasuredHeight();
    }

    Bitmap bigbitmap =
        Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);
    Canvas bigcanvas = new Canvas(bigbitmap);

    Paint paint = new Paint();
    int iHeight = 0;

    for (int i = 0; i < bmps.size(); i++) {
      Bitmap bmp = bmps.get(i);
      bigcanvas.drawBitmap(bmp, 0, iHeight, paint);
      iHeight += bmp.getHeight();

      bmp.recycle();
      bmp = null;
    }

    return bigbitmap;
  }
Copy the code

RecyclerView screenshots

As we all know, in the new Android version, RecyclerView can be used to replace the scene using ListView. Compared with ListView, RecyclerView has better cache support for Item View. You can use the same scenario as ListView, which is also seen on StackOverflow.


  public static Bitmap shotRecyclerView(RecyclerView view) {
    RecyclerView.Adapter adapter = view.getAdapter();
    Bitmap bigBitmap = null;
    if(adapter ! = null) { int size = adapter.getItemCount(); int height = 0; Paint paint = new Paint(); int iHeight = 0; final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memoryfor this memory cache.
      final int cacheSize = maxMemory / 8;
      LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize);
      for (int i = 0; i < size; i++) {
        RecyclerView.ViewHolder holder = adapter.createViewHolder(view, adapter.getItemViewType(i));
        adapter.onBindViewHolder(holder, i);
        holder.itemView.measure(
            View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(),
            holder.itemView.getMeasuredHeight());
        holder.itemView.setDrawingCacheEnabled(true);
        holder.itemView.buildDrawingCache();
        Bitmap drawingCache = holder.itemView.getDrawingCache();
        if(drawingCache ! = null) { bitmaCache.put(String.valueOf(i), drawingCache); } height += holder.itemView.getMeasuredHeight(); } bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888); Canvas bigCanvas = new Canvas(bigBitmap); Drawable lBackground = view.getBackground();if (lBackground instanceof ColorDrawable) {
        ColorDrawable lColorDrawable = (ColorDrawable) lBackground;
        int lColor = lColorDrawable.getColor();
        bigCanvas.drawColor(lColor);
      }

      for(int i = 0; i < size; i++) { Bitmap bitmap = bitmaCache.get(String.valueOf(i)); bigCanvas.drawBitmap(bitmap, 0f, iHeight, paint); iHeight += bitmap.getHeight(); bitmap.recycle(); }}return bigBitmap;
  }
Copy the code

The above method can not load the picture when intercepting the RecyclerView of asynchronous loading pictures. Here a rolling screen capture method is added

  public static void screenShotRecycleView(final RecyclerView mRecyclerView, final 
 RecycleViewRecCallback callBack) {
        if (mRecyclerView == null) {
            return;
        }
        BaseListFragment.MyAdapter adapter = (BaseListFragment.MyAdapter) mRecyclerView.getAdapter();
        final Paint paint = new Paint();
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // Use 1/8th of the available memory for this memory cache.
        final int cacheSize = maxMemory / 8;
        LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize);
        final int oneScreenHeight = mRecyclerView.getMeasuredHeight();
        int shotHeight = 0;
        if(adapter ! = null && adapter.getData().size() > 0) { int headerSize = adapter.getHeaderLayoutCount(); int dataSize = adapter.getData().size();for (int i = 0; i < headerSize + dataSize; i++) {
                BaseViewHolder holder = (BaseViewHolder) adapter.createViewHolder(mRecyclerView, adapter.getItemViewType(i));
                if (i >= headerSize)
                    adapter.startConvert(holder, adapter.getData().get(i - headerSize));
                holder.itemView.measure(
                        View.MeasureSpec.makeMeasureSpec(mRecyclerView.getWidth(), View.MeasureSpec.EXACTLY),
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight());
                holder.itemView.setDrawingCacheEnabled(true); holder.itemView.buildDrawingCache(); Bitmap drawingCache = holder.itemView.getDrawingCache(); //holder.itemView.destroyDrawingCache(); // Release cached resourcesif(drawingCache ! = null) { bitmaCache.put(String.valueOf(i), drawingCache); } shotHeight += holder.itemView.getHeight();if(shotHeight > 12000) {// Set the maximum number of snapshotsif(callBack ! = null) callBack.onRecFinished(null);return; } final int footHight = util.dip2px (mrecyclerView.getContext (), 42);} final int footHight = util.dip2px (mrecyclerview.getContext (), 42); shotHeight += footHight; // Return to the topwhile(mRecyclerView.canScrollVertically(-1)) { mRecyclerView.scrollBy(0, -oneScreenHeight); } / / final map screenshots of background Bitmap bigBitmap = Bitmap. CreateBitmap (mRecyclerView. GetMeasuredWidth () and shotHeight, Bitmap.Config.ARGB_8888); final Canvas bigCanvas = new Canvas(bigBitmap); Drawable lBackground = mRecyclerView.getBackground();if (lBackground instanceof ColorDrawable) {
                ColorDrawable lColorDrawable = (ColorDrawable) lBackground;
                int lColor = lColorDrawable.getColor();
                bigCanvas.drawColor(lColor);
            }


            final int[] drawOffset = {0};
            final Canvas canvas = new Canvas();
            if(shotHeight <= oneScreenHeight) {// Only one page Bitmap Bitmap = bitmap.createBitmap (mrecyclerview.getwidth (), mRecyclerView.getHeight(), Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); mRecyclerView.draw(canvas);if(callBack ! = null) callBack.onRecFinished(bitmap); }elseFinal int finalShotHeight = shotHeight; mRecyclerView.postDelayed(newRunnable() {
                    @Override
                    public void run() {
                        if(drawOffset[0] + oneScreenHeight < finalShotHeight) {// More than one screen Bitmap Bitmap = Bitmap.createBitmap(mRecyclerView.getWidth(), mRecyclerView.getHeight(), Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); mRecyclerView.draw(canvas); bigCanvas.drawBitmap(bitmap, 0, drawOffset[0], paint); drawOffset[0] += oneScreenHeight; mRecyclerView.scrollBy(0, oneScreenHeight); try { bitmap.recycle(); } catch (Exception ex) { ex.printStackTrace(); } mRecyclerView.postDelayed(this, 10); }elseInt leftHeight = finalShotHeight - drawOffset[0] - footHight; mRecyclerView.scrollBy(0, leftHeight); int top = oneScreenHeight - (finalShotHeight - drawOffset[0]);if(top > 0 && leftHeight > 0) { Bitmap bitmap = Bitmap.createBitmap(mRecyclerView.getWidth(), mRecyclerView.getHeight(), Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); mRecyclerView.draw(canvas); Bitmap = bitmap.createBitmap (bitmap, 0, top, bitmap.getwidth (), leftHeight, null,false); bigCanvas.drawBitmap(bitmap, 0, drawOffset[0], paint); try { bitmap.recycle(); } catch (Exception ex) { ex.printStackTrace(); }}if(callBack ! = null) callBack.onRecFinished(bigBitmap); }}}, 10); } } } public interface RecycleViewRecCallback { void onRecFinished(Bitmap bitmap); }Copy the code

I believe there are many partners to use BRVH third party library to do recycleView adapter. Using this library then using the above method will report the Angle mark out of bounds error, looked at the BRVH source code

 public void onBindViewHolder(ViewHolder holder, int positions) {
        int viewType = holder.getItemViewType();
        switch(viewType) {
        case 0:
            this.convert((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount()));
        case 273:
        case 819:
        case 1365:
            break;
        case 546:
            this.addLoadMore(holder);
            break; default: this.convert((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount())); this.onBindDefViewHolder((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount())); }}Copy the code

When we call Adapter. onBindViewHolder, because the position argument is not used, With the calculation of the holder. GetLayoutPosition () – this. GetHeaderLayoutCount () value is always 1 lead to Angle the cross-border error.

I understand that the screenshot principle of RecyclerView is that ViewHolder of each item is constructed first, and then the method of setting data to each item is called. At this time, the content of item is stored in cache, and the complete content can be obtained by drawing at this time. The onBindViewHolder (onBindViewHolder) or BRVH convert (onBindViewHolder) is not exposed in BRVH. The onBindViewHolder (onBindViewHolder) is not exposed in BRVH. The onBindViewHolder (onBindViewHolder) is not exposed in BRVH. At this point we need to expose convert based on BRVH, as shown below

 public class MyAdapter extends BaseQuickAdapter<T> {

        public MyAdapter() { super(getItemLayoutResId(), datas); * @param viewHolder * @param t */ public void startConvert(BaseViewHolder) viewHolder, T t){ convert(viewHolder,t); } @Override protected void convert(BaseViewHolder viewHolder, T t) {bindView(viewHolder, t); }}Copy the code

Then modify the method of getting the Bitmap described above

/ * * * capture recycler view * / public static Bitmap getRecyclerViewScreenshot (RecyclerView view) {BaseListFragment MyAdapter adapter = (BaseListFragment.MyAdapter) view.getAdapter(); Bitmap bigBitmap = null;if(adapter ! = null) { int size = adapter.getData().size(); int height = 0; Paint paint = new Paint(); int iHeight = 0; final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memoryfor this memory cache.
            final int cacheSize = maxMemory / 8;
            LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize);
            for(int i = 0; i < size; i++) { BaseViewHolder holder = (BaseViewHolder) adapter.createViewHolder(view, adapter.getItemViewType(i)); StartConvert (holder, adapter.getData().get(I))); holder.itemView.measure( View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(), holder.itemView.getMeasuredHeight()); holder.itemView.setDrawingCacheEnabled(true);
                holder.itemView.buildDrawingCache();
                Bitmap drawingCache = holder.itemView.getDrawingCache();
                if(drawingCache ! = null) { bitmaCache.put(String.valueOf(i), drawingCache); } height += holder.itemView.getMeasuredHeight(); } bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888); Canvas bigCanvas = new Canvas(bigBitmap); Drawable lBackground = view.getBackground();if (lBackground instanceof ColorDrawable) {
                ColorDrawable lColorDrawable = (ColorDrawable) lBackground;
                int lColor = lColorDrawable.getColor();
                bigCanvas.drawColor(lColor);
            }

            for(int i = 0; i < size; i++) { Bitmap bitmap = bitmaCache.get(String.valueOf(i)); bigCanvas.drawBitmap(bitmap, 0f, iHeight, paint); iHeight += bitmap.getHeight(); bitmap.recycle(); }}return bigBitmap;
    }
Copy the code

Synthesis of Bitmap

Let’s say four are made into one

/** * create a single ** @param pic1 * @param pic2 * @param pic3 * @param pic4 * @returnOnly_bitmap * See the instructions for details: {@link com.bertadata.qxb.util.ScreenShotUtils} */ public static Bitmap combineBitmapsIntoOnlyOne(Bitmap pic1, Bitmap pic2, Bitmap pic3, Bitmap pic4, Activity context) { int w_total = pic2.getWidth(); int h_total = pic1.getHeight() + pic2.getHeight() + pic3.getHeight() + pic4.getHeight(); int h_pic1 = pic1.getHeight(); int h_pic4 = pic4.getHeight(); int h_pic12 = pic1.getHeight() + pic2.getHeight(); // There is a height limit to prevent OOMif (h_total > HEIGHTLIMIT) {
            return null;
        }

        Bitmap only_bitmap = Bitmap.createBitmap(w_total, h_total, Bitmap.Config.ARGB_4444);
        Canvas canvas = new Canvas(only_bitmap);
        canvas.drawColor(ContextCompat.getColor(context, R.color.color_content_bg));
        canvas.drawBitmap(pic1, 0, 0, null);
        canvas.drawBitmap(pic2, 0, h_pic1, null);
        canvas.drawBitmap(pic3, 0, h_pic12, null);
        canvas.drawBitmap(pic4, 0, h_total - h_pic4, null);
        return only_bitmap;
    }
Copy the code

Image post-processing

/** * Compress the incoming Bitmap and output it to the system screenshot directory * name format: Screenshot from Screenshot Screenshot from ScrollView Screenshot from @param Context * * Also notify system to rescan system files * * @param pic1 figure 1 title bar Screenshot * @param pic2 Figure 2 ScrollView Screenshot * @param context Used to notify rescanning of file systems, can be removed to improve performance * see instructions for details: {@link com.bertadata.qxb.util.ScreenShotUtils} */ public static void savingBitmapIntoFile(final Bitmap pic1, final Bitmap pic2, final Activity context, final BitmapAndFileCallBack callBack) {if (context == null || context.isFinishing()) {
            return;
        }
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String fileReturnPath = "";
                int w = pic1.getWidth();
                Bitmap bottom = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_picture_combine_bottom);
                Bitmap top_banner = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_picture_combine_top);

                Bitmap bitmap_bottom = anyRatioCompressing(bottom, (float) w / bottom.getWidth(), (float) w / bottom.getWidth());
                Bitmap bitmap_top = anyRatioCompressing(top_banner, (float) w / bottom.getWidth(), (float) w / bottom.getWidth()); final Bitmap only_bitmap = combineBitmapsIntoOnlyOne(bitmap_top, pic1, pic2, bitmap_bottom, context); SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-ms", Locale.getDefault()); String data = sdf.format(new Date()); Get the memory path / / / / set the image path + output file naming conventions / / statement String storagePath = Environment. External.getexternalstoragedirectory () getAbsolutePath (); String fileTitle ="Screenshot_" + data + "_com.bertadata.qxb.biz_info.jpg";
                String filePath = storagePath + "/DCIM/"; final String fileAbsolutePath = filePath + fileTitle; File file = new File(fileAbsolutePath); /** ** quality and specific pressure combination * hierarchical compression * output file */if(only_bitmap ! FileOutputStream fos = new FileOutputStream(file); only_bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos); // create another file other_file and prepare String other_fileTitle ="Screenshot_" + data + "_com.bertadata.qxb.jpg"; String other_fileAbsolutePath = filePath + other_fileTitle; File other_file = new File(other_fileAbsolutePath); FileOutputStream other_fos = new FileOutputStream(other_file); Long file_size = file.length() / 1024; long file_size = file.length() / 1024; // size of file(KB)if(file_size < 0 || ! (file.exists())) {throw new NullPointerException(); }else if(file_size > 0 && file_size <= 256) {// direct output deleteFile(other_file); FileReturnPath = fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + fileAbsolutePath)));
                        } else if(file_size > 25&&file_size <= 768) {// Pressing (pressing) : pressing to 3/4 of pressing ratio, pressing to 50%float) 3/4, (float) 3 / 4).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // Refresh the file system to display the latest captured graph file fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + other_fileAbsolutePath)));
                        } else if(file_size > 768&&file_size <= 1280) {// Level 3: moderate pressing: pressing to 1/2 of pressing ratio, pressing to 40%float) 1/2, (float) 1 / 2).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // Refresh the file system to display the latest captured graph file fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + other_fileAbsolutePath)));
                        } else if(file_size > 1280&&file_size <= 2048) {// Table 4: pressing pressing: 1/3 pressing pressing, 40% pressing pressing(float) 1/3, (float) 1 / 3).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // Refresh the file system to display the latest captured graph file fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + other_fileAbsolutePath)));
                        } else if(file_size > 2048) {// Pressing level 5: moderate pressing: pressing to 1/2 of pressing ratio, pressing to 40%float) 1/2, (float) 1 / 2).compress(Bitmap.CompressFormat.JPEG, 40, other_fos); deleteFile(file); // Refresh the file system to display the latest captured graph file fileReturnPath = other_fileAbsolutePath; context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"+ other_fileAbsolutePath))); } // cancel fos; fos.flush(); other_fos.flush(); other_fos.close(); fos.close(); //callback returns the saved path and Bitmap callback. onSuccess(only_bitmap, fileReturnPath); } catch (Exception e) { e.printStackTrace(); }}else callBack.onSuccess(null, ""); }}); thread.start(); }Copy the code
@param width_ratio (width_ratio, width_ratio, width_ratio);float) (0<&&<1) * @param height_ratiofloat) (0<&&<1) * @return* <p> */ public static Bitmap anyRatioCompressing(Bitmap Bitmap, pressing)float width_ratio, float height_ratio) {
        Matrix matrix = new Matrix();
        matrix.postScale(width_ratio, height_ratio);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
    }
Copy the code

This article refer to http://www.cnblogs.com/BoBoMEe/p/4556917.html, and combined with their actual project is finished

Here is my wechat official account, welcome everyone to follow 👏👏👏👏 college ~ ~