Search code examples
androidcanvasbitmapscreenshottogglebutton

Text and line drawn on ToggleButton canvas appear on screen but not on screenshot


I have created a view MyToggleButton that extends ToogleButton. I overwrite the Togglebutton's onDraw method to draw a line on the button and to rotate it's text.

Here is MyToggleButton:

package com.example.gl.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.TextPaint;
import android.widget.ToggleButton;

public class MyToggleButton extends ToggleButton {

    public float textRotation=0;

    public MyToggleButton(Context context, float rotationValue) {
        super(context);

        textRotation=rotationValue;
    }

    @Override
    protected void onDraw(Canvas canvas){

        TextPaint textPaint = getPaint();
        textPaint.setColor(getCurrentTextColor());
        textPaint.drawableState = getDrawableState();

        canvas.save();

        //paint
        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(7);
        paint.setStrokeCap(Paint.Cap.ROUND);

        canvas.drawLine(canvas.getWidth() / 2, canvas.getHeight() / 2, (float) (canvas.getHeight() / 2 + 400), (canvas.getHeight() / 2), paint);

        canvas.rotate(textRotation, canvas.getWidth() / 2, canvas.getHeight() / 2);

        getLayout().draw(canvas);

        canvas.restore();

    }


}

After that I want to get a screenshot of and view it and for that I use the takeScreenshot() method that is called by pressing the 3rd button on screen. The screenshot code is based on How to programmatically take a screenshot in Android?. The same approach is followed in other posts on stackoverflow, and in various sites and tutorials I found around.

Here is the MainActivity that I use:

package com.example.gl.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //add 3 buttons
        addButtons();

    }


    private void addButtons(){

        LinearLayout linear1= (LinearLayout) findViewById(R.id.vert1);

        //MyToggleButton with rotated text
        MyToggleButton btn1 = new MyToggleButton(this,0);
        btn1.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));
        btn1.setTextOff("Button 1 not rotated");
        btn1.setTextOn("Button 1 not rotated");
        btn1.setText("Button 1 not rotated");
        linear1.addView(btn1);

        //MyToggleButton with normal text
        MyToggleButton btn2 = new MyToggleButton(this,50);
        btn2.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));
        btn2.setTextOff("Button 2 rotated");
        btn2.setTextOn("Button 2 rotated");
        btn2.setText("Button 2 rotated");
        linear1.addView(btn2);

        //button to create screenshot
        Button btn3 = new Button(this);
        btn3.setText("Create bitmap");
        btn3.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                takeScreenshot();

            }
        });
        linear1.addView(btn3);



    }

    //Screenshotcreator
        private void takeScreenshot() {
            try {
                View v1 = getWindow().getDecorView().getRootView();

                // create bitmap screen capture
                v1.setDrawingCacheEnabled(true);
                Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());

                v1.setDrawingCacheEnabled(false);

                ImageView imageView1 =(ImageView) findViewById(R.id.imageView1);
                imageView1.setImageBitmap(bitmap);


            } catch (Throwable e) {
                e.printStackTrace();
            }
        }


    }

Here is the layout of MainActivity:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.gl.myapplication.MainActivity"
    android:id="@+id/relative1">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/vert1">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="250dp"
            android:id="@+id/imageView1" />
    </LinearLayout>
</RelativeLayout>

The problem is that although everything looks ok on the screen, the screenshot's bitmap lacks the drawn line and the rotated text.

You can see the problem here. Screen is OK, screenshot is not.

What could be the reason for that strange result in the bitmap?

Notice that that the devices back-home-recents buttons are missing from the screenshot, as also the status bar information (time etc). I doubt that this has to do something with my issue. Those elements do not belong in my app so it is expected for them to not appear in the view I get with get with v1 = getWindow().getDecorView().getRootView(); But the canvas on which I draw the rotated text and the line do belong to that view.

My question is: Why does the drawn line and the rotated text do not appear on the screenshot, and how can I modify my code to get a screenshot with them on too?

I don't want to do something that needs a rooted device and also I cannot use MediaProjection API because this was added in API 21 but I want my app to run on android 4.4 (API 19) too. Finally, I am aware of the existence of external libraries such as Android Screenshot Library or this: https://android-arsenal.com/details/1/2793 , but I would rather not use any external library.

P.S. maybe this is related:Surfaceview screenshot with line/ellipse drawn over it


Solution

  • The problem is that both btn1.isDrawingChaceEnabled() and btn2.isDrawingChaceEnabled() return false.

    Even if you call setDrawingCacheEnabled(true) on the RootView, it is not set as true recursively on all his Children View (I tested it with Log.d).
    That means that the drawing cache of your ToggleButton will be empty as explained in the android.view.View.getDrawingCache() javadoc.

    /**
     * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
     *
     * @return A non-scaled bitmap representing this view or null if cache is disabled.
     *
     * @see #getDrawingCache(boolean)
     */
    

    So make sure you set setDrawingCacheEnabled(true) for each of the views you are interested in.
    For example I added the following lines to your code:
    in MainActivity

        btn1.setDrawingCacheEnabled(true);
        btn2.setDrawingCacheEnabled(true);
    

    And it looks like this