Search code examples
bitmapandroid-canvasandroid-constraintlayoutandroid-cardview

Cardview loses its radius when taken a screenshot programmatically


I have constraintlayout within cardview. The cardview looks fine on the screen. It is only when taken a screenshot,it is rendered to a bitmap thats when it loses the corners. I have added the picture of the bitmap view. The constraintlayout overlaps the rounded corners. I need edge to edge roundness.

Below is the screenshot of the emulator showing round corners correctly:

Below is the image of the cardview within a Constraintlayout

Here is the view rendered to a bitmap:

Here is the view rendered to a bitmap.

Below is my sample code.

public class MainActivity extends AppCompatActivity {

    private CardView cardView;
    private Button button;
    private ConstraintLayout parentLayoutA;
    private ConstraintLayout layoutB;
    private ConstraintLayout childLayout;
    private ConstraintSet innerSet;
    private ConstraintSet outerSet;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CreateCardViewProgrammatically();
    }


    public void CreateCardViewProgrammatically(){

        cardView = new CardView(this);
        childLayout = new ConstraintLayout(this);
        parentLayoutA = new ConstraintLayout(this);
        layoutB = new ConstraintLayout(this);
        button = new Button(this);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            layoutB.setClipToOutline(true);
        }

        cardView.setRadius(150);

        button.setText("Click!!");
        button.setTextColor(Color.BLACK);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               takeScreenShot();
            }
        });

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            parentLayoutA.setId(View.generateViewId());
            layoutB.setId(View.generateViewId());
            childLayout.setId(View.generateViewId());
            cardView.setId(View.generateViewId());
            button.setId(View.generateViewId());
        }

        innerSet = new ConstraintSet();
        outerSet = new ConstraintSet();

        ConstraintLayout.LayoutParams parentParams = new ConstraintLayout.LayoutParams(
                ConstraintLayout.LayoutParams.MATCH_PARENT,
                ConstraintLayout.LayoutParams.MATCH_PARENT
        );

        layoutB.setLayoutParams(parentParams);
        childLayout.setLayoutParams(parentParams);
        cardView.setCardElevation(0);
        cardView.setPadding(50,50,50,50);

        parentLayoutA.addView(layoutB);
        layoutB.addView(cardView);
        cardView.addView(childLayout);
        childLayout.addView(button);

        innerSet.centerHorizontally(button.getId(), ConstraintSet.PARENT_ID);
        innerSet.centerVertically(button.getId(), ConstraintSet.PARENT_ID);
        innerSet.constrainHeight(button.getId(), 200);

        outerSet.connect(cardView.getId(), ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 200);
        outerSet.connect(cardView.getId(), ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 200);
        outerSet.centerHorizontally(cardView.getId(), ConstraintSet.PARENT_ID);
        outerSet.centerVertically(cardView.getId(), ConstraintSet.PARENT_ID);
        outerSet.constrainHeight(cardView.getId(), 1500);

        innerSet.applyTo(childLayout);
        outerSet.applyTo(layoutB);

        setContentView(parentLayoutA);

        parentLayoutA.setBackgroundColor(Color.YELLOW);
        layoutB.setBackgroundColor(Color.MAGENTA);
        cardView.setCardBackgroundColor(Color.GREEN);
        childLayout.setBackgroundColor(Color.BLUE);
    }

    public void takeScreenShot() {
        imageFromView(parentLayoutA);
    }

    public Bitmap imageFromView(View view) {

        DisplayMetrics metrics = this.getApplication().getResources().getDisplayMetrics();
        int width = view.getWidth() > 0 ? view.getWidth() : metrics.widthPixels;
        int height = view.getHeight() > 0 ? view.getHeight() : metrics.heightPixels;

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);

        return bitmap;
    }


Solution

  • From looking at your code, I am not clear on what you are trying to accomplish. However, if you change the following line

    cardView.setCardBackgroundColor(Color.GREEN);
    

    to

    cardView.setCardBackgroundColor(Color.BLUE);
    

    and delete the line

    childLayout.setBackgroundColor(Color.BLUE);
    

    you will see your screen image captured the way I think you want.


    The underlying problem is that the rounded corner clipping is dependent upon the hardware layer which is enabled for the canvas generated by the view display system but is turned off when you generate your own bitmap and canvas. You can check this with Canvas#isHardwareAccelerated(),

    Take a look at Taking Screenshot Programmatically Using PixelCopy Api for an approach that you might be able to take. Also, see PixelCopy.

    Here is a small project on GitHub demonstrating how to go about capturing a layout. This demo app simply uses the code in the question to display a layout, captures the layout and redisplays it in an ImageView. A TextView in the redisplay simply displays "Captured!"

    enter image description here