8

I am using a recyclerView having lists of dynamic sizes...and when i use this method to take screenshot of each recyclerView item.. it taking the screenshot but each item containing only one list item each.. even if the list item size > 1.

public static Bitmap getRecyclerViewScreenshot(RecyclerView view) {
    int size = view.getAdapter().getItemCount();
    RecyclerView.ViewHolder holder = view.getAdapter().createViewHolder(view, 0);
    view.getAdapter().onBindViewHolder(holder, 0);
    ArrayList<Bitmap> bitmaps=new ArrayList<>();
    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());
    Bitmap bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), holder.itemView.getMeasuredHeight() * size,
            Bitmap.Config.ARGB_8888);
    Canvas bigCanvas = new Canvas(bigBitmap);
    bigCanvas.drawColor(Color.WHITE);
    Paint paint = new Paint();
    int iHeight = 0;
    holder.itemView.setDrawingCacheEnabled(true);
    holder.itemView.buildDrawingCache();
    bigCanvas.drawBitmap(holder.itemView.getDrawingCache(), 0f, iHeight, paint);
    holder.itemView.setDrawingCacheEnabled(false);
    holder.itemView.destroyDrawingCache();
    iHeight += holder.itemView.getMeasuredHeight();
    for (int i = 1; i < size; i++) {
        view.getAdapter().onBindViewHolder(holder, i);
        holder.itemView.setDrawingCacheEnabled(true);
        holder.itemView.buildDrawingCache();
        bitmaps.add(holder.itemView.getDrawingCache());
        bigCanvas.drawBitmap(holder.itemView.getDrawingCache(), 0f, iHeight, paint);
        iHeight += holder.itemView.getMeasuredHeight();
        holder.itemView.setDrawingCacheEnabled(false);
        holder.itemView.destroyDrawingCache();
    }
    return bigBitmap;
}



  Here is my screenshot attached (`of each item of recyclerView`)!! It should show `two items` but uniformly its showing `1 item` each ` I am facing problem with the `inner listView` 

Original is :-enter image description here

Screenshot is :-enter image description here

Its the case for each and every recycler item screenshot only 1 item is shown.

Santanu Sur
  • 10,997
  • 7
  • 33
  • 52
  • I tested your code with a very basic recycler view and it works - in my case with 40 items, but not with 45. With limits like this one, I tend to think that the are resource dependent, however, a large heap didn't change anything. Also scrolling to the item indexed by the loop (and forcing the recycler view to layout below what is visible in the first place) didn't help. Just wanted you to know that 1 is not necessary the limit. By the way: you can let the loop start at 0 and get rid of the redundant code before the loop. – kalabalik Jan 11 '18 at 06:57
  • Ya the minimum limit of the list is taken for every recycler item...For exp. If the recycler item has a minimum list of size 6 every item will have 6 child items.. – Santanu Sur Jan 11 '18 at 07:04

3 Answers3

7

faced the same issue and resolved this by using this piece of code.

First of all, ask your view to calculate it's measurements and then ask view to give MeasuredHeight and all done.

recyclerView.measure(
        View.MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), View.MeasureSpec.EXACTLY),
        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

Bitmap bm = Bitmap.createBitmap(recyclerView.getWidth(), recyclerView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
recyclerView.draw(new Canvas(bm));

saveImage(bm);
ImageView im
          = new ImageView(getActivity());
im.setImageBitmap(bm);
 new AlertDialog.Builder(getActivity()).setView(im).show();
Nouman Ch
  • 4,023
  • 4
  • 29
  • 42
0

This might solve the issue,

I have created a Bitmap bmOverlay to draw both bitmaps over this bitmap. Also, I have taken another For loop for creating bitmap for the listview Items.

Here are the Changes I did to your Code:

public Bitmap getRecyclerViewScreenshot(RecyclerView view) {
        int size = view.getAdapter().getItemCount();
        RecyclerView.ViewHolder holder = view.getAdapter().createViewHolder(view, 0);
        view.getAdapter().onBindViewHolder(holder, 0);
        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());
        Bitmap bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), holder.itemView.getMeasuredHeight() * size,
                Bitmap.Config.ARGB_8888);
        Canvas bigCanvas = new Canvas(bigBitmap);
        bigCanvas.drawColor(Color.WHITE);
        Paint paint = new Paint();
        int iHeight = 0;
        holder.itemView.setDrawingCacheEnabled(true);
        holder.itemView.buildDrawingCache();
        bigCanvas.drawBitmap(holder.itemView.getDrawingCache(), 0f, iHeight, paint);
        holder.itemView.setDrawingCacheEnabled(false);
        holder.itemView.destroyDrawingCache();
        iHeight += holder.itemView.getMeasuredHeight();

        //Create bitmap to draw both recycler bitmap and listview
        Bitmap bmOverlay = Bitmap.createBitmap(bigBitmap.getWidth(), bigBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmOverlay);


        HashMap<Integer, List<Bitmap>> bmp = recommendTestAdapter.getBmp();
        HashMap<Integer, Integer> height = recommendTestAdapter.getHeight();
        HashMap<Integer, Integer> width = recommendTestAdapter.getWidth();

        for (int i = 1; i < size; i++) {
            view.getAdapter().onBindViewHolder(holder, i);
            holder.itemView.setDrawingCacheEnabled(true);
            holder.itemView.buildDrawingCache();
            List<Bitmap> bitmapList = bmp.get(i);
            int allHeight = height.get(i);
            int listViewWidth = width.get(i);

            Bitmap smallBitmap = Bitmap.createBitmap(listViewWidth, allHeight, Bitmap.Config.ARGB_8888);
            Canvas smallCanvas = new Canvas(smallBitmap);

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

            for (int j = 0; j < bitmapList.size(); j++) {
                Bitmap bmpList = bitmapList.get(j);
                smallCanvas.drawBitmap(bmpList, 0, iListHeight, paint);
                iListHeight += bmpList.getHeight();

                bmpList.recycle();
            }

            bigCanvas.drawBitmap(holder.itemView.getDrawingCache(), 0f, iHeight, paint);
            bigCanvas.drawBitmap(smallBitmap, 0f, iListHeight, paint);
            iHeight += holder.itemView.getMeasuredHeight();
            holder.itemView.setDrawingCacheEnabled(false);
            holder.itemView.destroyDrawingCache();

            canvas.drawBitmap(bigBitmap, new Matrix(), null);
            canvas.drawBitmap(smallBitmap, new Matrix(), null);
            return bmOverlay;
        }
        return bigBitmap;
    }

Here's my RecycleView Adapter,

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.viewHolder> {

    private Context context;
    private ArrayList<RecyclerData> recyclerData;
    private ListViewAdapter listAdapter;
    private HashMap<Integer, List<Bitmap>> bmp = new HashMap<>();
    private HashMap<Integer, Integer> height = new HashMap<>();
    private HashMap<Integer, Integer> width = new HashMap<>();

    public RecyclerAdapter(Context context, ArrayList<RecyclerData> recyclerData) {
        this.context = context;
        recyclerData = recyclerData;
    }

    @Override
    public viewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview, parent, false);
        return new viewHolder(view);
    }

    @Override
    public void onBindViewHolder(final viewHolder holder, final int position) {
        listAdapter = new ListViewAdapter(context);
        holder.listView.setAdapter(listAdapter);

        List<Bitmap> bitmapList = new ArrayList<>();
        int allItemsHeight = 0;
        for (int i = 0; i < listAdapter.getCount(); i++) {

            View childView = listAdapter.getView(i, null, holder.listView);
            childView.measure(View.MeasureSpec.makeMeasureSpec(holder.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();
            bitmapList.add(childView.getDrawingCache());
            allItemsHeight+=childView.getMeasuredHeight();
        }
        width.put(position, holder.listView.getMeasuredWidth());
        height.put(position, allItemsHeight);
        bmp.put(position, bitmapList);
    }

    public HashMap<Integer, List<Bitmap>> getBmp() {
        return bmp;
    }

    public void setBmp(HashMap<Integer, List<Bitmap>> bmp) {
        this.bmp = bmp;
    }

    public HashMap<Integer, Integer> getHeight() {
        return height;
    }

    public void setHeight(HashMap<Integer, Integer> height) {
        this.height = height;
    }

    public HashMap<Integer, Integer> getWidth() {
        return width;
    }

    public void setWidth(HashMap<Integer, Integer> width) {
        this.width = width;
    }

    @Override
    public int getItemCount() {
        return recyclerData.size();
    }

    class viewHolder extends RecyclerView.ViewHolder {

        private ListView listView;

        viewHolder(View itemView) {
            super(itemView);
            listView = itemView.findViewById(R.id.listview);
        }
    }
}
Patrick R
  • 6,621
  • 1
  • 24
  • 27
  • what is `rvNotifications` is it a handler? – Santanu Sur Jan 12 '18 at 07:28
  • it isnt working dude..same issue the child lists are not showing properly.. @Partrick R – Santanu Sur Jan 12 '18 at 09:01
  • I have modified the answer with one more solution, kindly look into and let me know if it works for you or not. – Patrick R Jan 12 '18 at 09:45
  • @PatrickR I agree with you, just simple draw the parent view, draw each item doesn't make sense. – Phong Nguyen Jan 12 '18 at 09:53
  • actually i added the bitmap to a `Arraylist bitmaps` and in the for loop `bitmaps.add(holder.itemView.getDrawingCache());` dont need the single `Bigbitmap` at one go . I am making a `pdf` of all the bitmaps of recyclerview items to be honest – Santanu Sur Jan 12 '18 at 10:20
  • I assume you need to generate PDF as per your comments, if so, you should generate PDF directly instead of taking screenshot from your data. Let me know if you need any further assistance you need. – Patrick R Jan 12 '18 at 10:35
  • How shall i generate it ? I mean the pdf of the recyclerView(`each page of pdf is each recycler item`) without taking screenshot of each item of recyclerview ? – Santanu Sur Jan 12 '18 at 10:41
  • Check the below links to generate PDF in android, https://stackoverflow.com/questions/45545782/creating-pdf-file-using-itext-from-a-recyclerview-entire-items-inside-in-andro or https://stackoverflow.com/questions/34296149/creating-a-pdf-file-in-android-programmatically-and-writing-in-it – Patrick R Jan 12 '18 at 11:07
  • I HAVE USED THE same technique , but the child list items are not showing up as it should be, thats the whole point of my question ... – Santanu Sur Jan 12 '18 at 13:56
  • I have added new code and modified the answer, I have tested this code and it works fine to generate PDF from recyclerview. Please try at your end and let me know. – Patrick R Jan 13 '18 at 09:24
  • NO its not working the problem is not with recyclerView its with the listview inside it...its showing only one item constantly from the begining...the process u have shown is the process i used since the first day i posted this.. @PatrickR check my screenshots for reference – Santanu Sur Jan 15 '18 at 07:31
  • Per your above comment, I have provided updated code, hope it helps. – Patrick R Jan 16 '18 at 10:19
  • How do i add each bitmap to a list within the loop? – Santanu Sur Jan 17 '18 at 10:43
  • Please check Recyclerview adapter in above code, in that I have included listview adapter also in "OnBindViewHolder()", bitmap list is filled with bitmap of individual list item. This bitmap list is used in main activity to draw bitmap with recyclerview's item view bitmap and its inner list item bitmaps. – Patrick R Jan 17 '18 at 11:01
  • Yes i got ur point but how do i add each bitmap to an arraylist in main activity...suppose i have a ArrayList bitmaps..so can you point out the exact place in MainActivity where i can add each bitmap to the bitmaps arraylist ?? – Santanu Sur Jan 17 '18 at 17:42
  • I have used List bitmapList = bmp.get(i); to get the list of bitmaps of inner listview items and then taken another loop inside the first for loop to add all the bitmaps of listview to each item of recyclerview. – Patrick R Jan 22 '18 at 11:22
0

Why you have to draw each item to create a screenshot ?

Case 1: Your RecyclerView is static (show all item, scroll with parent -NestedScrollView), recyclerView.setNestedScrollingEnabled(false);
If you are implementing this case, you don't need to draw each item, just draw root view:

public static Bitmap takeScreenShot(View view) {
                    Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
                    Canvas c = new Canvas(b);
                    view.draw(c);
                    return b;
                }

Case 2: Your RecyclerView still enable recycle-technique, if we use your solution when the list has many items (100 items for example), the screenshot will look very different with actual view of user, it's also very tall and ugly bitmap.

I checked out your code, it works but it doesn't make sense to me.

Phong Nguyen
  • 6,897
  • 2
  • 26
  • 36