1

I am working on a project which makes drawing.

I don't use axml because I do my drawing in a class called filledpolygon and calling the function in MainActivity. I just want to take screenshot in my project. Is there any basic function, which I can call in onCreate method? So, when the program runs, it will automatically take the screenshot. I found answers except Xamarin platform.

Stefan Wanitzek
  • 2,059
  • 1
  • 15
  • 29
Aniki
  • 47
  • 1
  • 8
  • 1
    Possible duplicate of [How to programmatically take a screenshot in Android?](http://stackoverflow.com/questions/2661536/how-to-programmatically-take-a-screenshot-in-android) – Sven-Michael Stübe Apr 03 '16 at 12:07

2 Answers2

2

Since Android 28 DrawingCacheEnabled is deprecated and without it we are forcing our view to to redraw on our custom canvas wich can cause artifacts with custom controls and renderers and the screenshot version might be different from what we see on screen.

The legacy code that is still working on simple cases is:

  public byte[] CaptureScreenshot()
    {
        var view=
            Xamarin.Essentials.Platform.CurrentActivity.Window.DecorView.RootView;

       if (view.Height < 1 || view.Width < 1)
            return null;

        byte[] buffer = null;

        view.DrawingCacheEnabled = true;

        using (var screenshot = Bitmap.CreateBitmap(
            view.Width,
            view.Height,
            Bitmap.Config.Argb8888))
        {
            var canvas = new Canvas(screenshot);


            view.Draw(canvas);

            using (var stream = new MemoryStream())
            {
                screenshot.Compress(Bitmap.CompressFormat.Png, 90, stream);
                buffer = stream.ToArray();
            }
        }

        view.DrawingCacheEnabled = false;

        return buffer;

    }

Use legacy method above as follows

  if ((int)Android.OS.Build.VERSION.SdkInt < 28)
    {
        //legacy
    }

The DrawingCacheEnabled obsolete warning redirects us to use PixelCopy. This method is acting with a callback so to use it synchronously have made some helpers:

Usage:

    public byte[] CaptureScreenshot()
    {
        using var helper = new ScreenshotHelper(
            Xamarin.Essentials.Platform.CurrentActivity.Window.DecorView.RootView,
            Xamarin.Essentials.Platform.CurrentActivity);

        byte[] buffer = null;
        bool wait = true;

        Task.Run(async () =>
        {
            helper.Capture((Bitmap bitmap) =>
            {
                try
                {

                    if (!helper.Error)
                    {
                        using (var stream = new MemoryStream())
                        {
                            bitmap.Compress(Bitmap.CompressFormat.Png, 90, stream);
                            buffer = stream.ToArray();
                        }
                    }

                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                finally
                {
                    wait = false;
                }
            });

        }).ConfigureAwait(false);


        while (wait)
        {
            Task.Delay(10).Wait();
        }

        return buffer;
    }

The helper:

    public class ScreenshotHelper : Java.Lang.Object, PixelCopy.IOnPixelCopyFinishedListener
    {


        public void OnPixelCopyFinished(int copyResult)
        {
            var stop = true;
            if (copyResult == (int) PixelCopyResult.Success)
            {
                Error = false;
                //todo CallbackGotScreenshot();
                _callback(_bitmap);
            }
            else
            {
                Error = true;
            }

            _callback(_bitmap);

        }

        public bool Error { get; protected set; }

        public ScreenshotHelper(Android.Views.View view, Activity activity)
        {
            _view = view;
            _activity = activity;

            _bitmap = Bitmap.CreateBitmap(
                _view.Width,
                _view.Height,
                Bitmap.Config.Argb8888);
        }

        // Starts a background thread and its {@link Handler}.
        private void StartBackgroundThread()
        {
            _BackgroundThread = new HandlerThread("ScreeshotMakerBackground");
            _BackgroundThread.Start();
            _BackgroundHandler = new Handler(_BackgroundThread.Looper);
        }

        // Stops the background thread and its {@link Handler}.
        private void StopBackgroundThread()
        {
            try
            {
                _BackgroundThread.QuitSafely();
                _BackgroundThread.Join();
                _BackgroundThread = null;
                _BackgroundHandler = null;
            }
            catch (Exception e)
            {
                //e.PrintStackTrace();
            }
        }

        public void Capture(Action<Bitmap> callback)
        {
            //var locationOfViewInWindow = new int[2];
            //_view.GetLocationInWindow(locationOfViewInWindow);
            _callback = callback;

            try
            {
                StartBackgroundThread();
                //todo could create-use background handler
                PixelCopy.Request(_activity.Window, _bitmap, this,
                    _BackgroundHandler);


            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            finally
            {
                Task.Run(StopBackgroundThread);
            }
        }

        private Android.Views.View _view;
        private Activity _activity;
        private Bitmap _bitmap;
        private HandlerThread _BackgroundThread;
        private Handler _BackgroundHandler;
        private Action<Bitmap> _callback;


        public new void Dispose()
        {
            _bitmap?.Dispose();
            _bitmap= null;
            _activity = null;
            _view = null;
            _callback = null;

            base.Dispose();
        }

    }
Nick Kovalsky
  • 5,378
  • 2
  • 23
  • 50
0

In your View you could run the following code which will take a screenshot. I have not tried running it in OnCreate() before so you may need to test that out to make sure the view has been fully rendered.

*Edit: According to this post you may have trouble running this code in OnCreate() so you will need to find a better place. I was unable to figure out what post the user was referring to in the link he posted.

*Edit #2: Just found out that Compress() does not take the quality parameter (which is listed as 0 below) into account since PNG is lossless, but if you change the format to JPEG for example, then you may want to turn up the quality parameter since your image will look like garbage.

public byte[] SaveImage() {

    DrawingCacheEnabled = true;                  //Enable cache for the next method below
    Bitmap bitmap       = GetDrawingCache(true); //Gets the image from the cache

    byte[] bitmapData;
    using(MemoryStream stream = new MemoryStream()) {
        bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
        bitmapData = stream.ToArray();
    }

    return bitmapData;
}
hvaughan3
  • 10,955
  • 5
  • 56
  • 76
  • @Aniki: See edit and please mark as answer if it was helpful. – hvaughan3 Apr 04 '16 at 19:28
  • Im new to xamarin and android in general but how can I set DrawingCacheEnabled true? VS reports to me that the name 'DrawingCacheEnabled' does not exist in the current context. – Lexu Apr 07 '17 at 10:17
  • @Lexu You would need to do that from your Xamarin Android project code. See Amalan Dhananjayan's answer below where he sets `view.DrawingCacheEnabled = true;` – hvaughan3 Apr 07 '17 at 13:29
  • I realized that but I cant find a way to get my current view. All comes down to either use the current activity or the current view. But I cant find neither. – Lexu Apr 07 '17 at 13:39
  • @Lexu Would need to see some code. I would suggest asking a new question and posting a link to the new question in the comments area here. Be sure to put your current code in the question – hvaughan3 Apr 07 '17 at 13:47
  • Thank you for your help. I posted a question now which you can find here: http://stackoverflow.com/questions/43279971/get-current-activity-xamarin-android The ios version seems to work so far. – Lexu Apr 07 '17 at 13:56