Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CardView shadow not rendered when converted to bitmap

THE PROBLEM

I'm try to save a view group (which has a CardView as one of its childern) as a PNG file. To achieve this,

  1. I inflate the view group and populate the views with required information
  2. Load an image to an image view via Glide
  3. Add a ViewTreeObserver.OnGlobalLayoutListener to the ViewTreeObserver of the image view and pass the entire (parent) view that is going to be shared to a method that converts the view to a bitmap when image view's bottom is greater than zero (image view's height attribute is set to wrap_content, thus its bottom will be zero until image is loaded).

By doing this, I'm able to convert the view to a bitmap, however, with one caveat: the CardView's show is not rendered on the bitmap.

FAILED ATTEMPTS

So far I've tried:

  1. Switching between layerType attribute from "software" to "hardware".
  2. Setting on and off cardUseCompatPadding attribute of the CardView.
  3. Tried setting the image drawable without using Glide.
  4. Tried without loading an image drawable at all.

THE CODE

Here are code snippets that might help you guys identify the problem:

The method used to convert a view to a bitmap

public static Bitmap getBitmapFromView(View view) {
    //Define a bitmap with the same size as the view
    Bitmap b = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    //Bind a canvas to it
    Canvas canvas = new Canvas(b);
    //Get the view's background
    Drawable bgDrawable = view.getBackground();
    if (bgDrawable != null)
        //has background drawable, then draw it on the canvas
        bgDrawable.draw(canvas);
    else
        //does not have background drawable, then draw white background on the canvas
        canvas.drawColor(Color.WHITE);
    // draw the view on the canvas
    view.draw(canvas);
    //return the bitmap
    return b;
}

XML layout file of the view that's being inflated and passed to the getBitmapFromView() above.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="16dp">

    <com.devspark.robototextview.widget.RobotoTextView
        android:id="@+id/title"
        style="@style/text_subhead"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:layout_marginBottom="@dimen/lessons_horizontal_margin_narrow"
        android:layout_marginLeft="@dimen/lessons_horizontal_margin_narrow"
        android:layout_marginRight="@dimen/lessons_horizontal_margin_narrow"
        android:layout_marginTop="@dimen/lessons_horizontal_margin_narrow"
        android:gravity="left"
        app:typeface="roboto_medium" />

    <com.devspark.robototextview.widget.RobotoTextView
        android:id="@+id/text"
        style="@style/text_subhead"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/lessons_horizontal_margin_narrow"
        android:layout_marginRight="@dimen/lessons_horizontal_margin_narrow"
        android:gravity="left"
        android:textColor="@color/text"
        app:typeface="roboto_regular" />

    <android.support.v7.widget.CardView
        android:id="@+id/image_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/lessons_horizontal_margin_narrow"
        app:cardCornerRadius="@dimen/lessons_image_card_corner_radius"
        app:cardElevation="3dp"
        app:cardPreventCornerOverlap="false"
        app:cardUseCompatPadding="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <com.makeramen.roundedimageview.RoundedImageView
                android:id="@+id/image"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:contentDescription="@null"
                app:riv_corner_radius_top_left="@dimen/lessons_image_card_corner_radius"
                app:riv_corner_radius_top_right="@dimen/lessons_image_card_corner_radius" />

            <com.devspark.robototextview.widget.RobotoTextView
                android:id="@+id/caption"
                style="@style/text_caption"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/lessons_image_card_caption_margin"
                android:gravity="left"
                app:typeface="roboto_condensed_regular" />

        </LinearLayout>

    </android.support.v7.widget.CardView>

    <!-- Some other views that aren't particularly interesting -->

</LinearLayout>
like image 227
fahmy Avatar asked Oct 04 '15 08:10

fahmy


2 Answers

just change cardview to view, and set

android:background="@android:drawable/dialog_holo_light_frame"

ofcause you need to deal the padding yourself

like image 145
lynn Avatar answered Nov 07 '22 10:11

lynn


As @galex said - shadows are not drawn on views only by calling measure and layout.

So we can't use the elevation. Also we can't use the drawable shadow, because then we get sharp angles and straight sides.

Solution: use the png 9-path resizable drawable. For this we can use this beautiful tool: Android shadow generator

  1. Create 9-path files for all of mdpi, hdpi, xhdpi, xxhdpi and xxxhdpi.
  2. Put all of your png`s to res/drawables folder.
  3. Now we can use this drawable like background for view where we want to see shadow.

Note that for different densities, you must change following parameter: height and width of the view, shadow offsets (x and y), blur, round corners radius and paddings.

Multipliers for diffrent densities:

LDPI - x0.75//practically not used, so you can do without it
MDPI - x1.0// means original size 
HDPI - x1.5
XHDPI - x2.0
XXHDPI - x3.0
XXXHDPI - x4.0

For example if you need to create rectangle with 30dp x 100dp and radius = 8dp

list of 9.path images you should generate:

  1. 30х100px, rad = 8
  2. 45х150px, rad = 12
  3. 60х200px, rad = 16
  4. 90х300px, rad = 24
  5. 120х400px, rad = 32
like image 33
tasjapr Avatar answered Nov 07 '22 09:11

tasjapr