This is a migrated thread and some comments may be shown as answers.

Image annotation using CartesianCustomAnnotation

8 Answers 374 Views
Chart - Xamarin.Android
This is a migrated thread and some comments may be shown as answers.
Rodrigo
Top achievements
Rank 1
Veteran
Rodrigo asked on 26 Oct 2020, 09:43 PM

Hi,

Would it be possible to get example of creating Image/Bitmap annotations using CartesianCustomAnnotation or similar technique in Native Android?

Regards,

Rodrigo

8 Answers, 1 is accepted

Sort by
0
Didi
Telerik team
answered on 27 Oct 2020, 12:28 PM

Hello Rodrigo,

We do not have an example of how to render RadChart control as an image/bitmap. There isn't yet an option to export the UI element as a bitmap. We consider this more of application logic.

My colleague Lance created a custom Xamarin.Forms demo application, RenderImage, that can render the UI as a png or jpeg. It demonstrates using native platform-specific APIs, you can find this in each platform's RenderService class. The demo explains the following Option - Get the Screen Pixels and Save Image. 

We have a how-to article in our documentation of how to create an image of a barcode: https://docs.telerik.com/devtools/xamarin/knowledge-base/barcode-image-export 

Also, you can check the following question and the answer given in our forum https://www.telerik.com/forums/export-map-as-an-image 

In addition, I have searched for a native solution on the internet and I have found the following links that could help you: https://docs.microsoft.com/en-us/dotnet/api/android.graphics.bitmap.createbitmap?view=xamarin-android-sdk-9 

https://docs.microsoft.com/en-us/xamarin/xamarin-forms/creating-mobile-apps-xamarin-forms/summaries/chapter13#platform-specific-bitmaps

You can also search from your side for possible solutions on how the scenario could be achieved. I hope the provided links will be of help.

Regards,
Didi
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Rodrigo
Top achievements
Rank 1
Veteran
answered on 27 Oct 2020, 07:39 PM

Sorry, I'm looking how to add image annotations to the chart, not how to render chart as a bitmap...

This functionality is available in Native iOS:

var imageView = new UIImageView
{
Bounds = new CGRect (0, 0, 40, 40),
Image = UIImage.FromBundle("my_img.png"),
Alpha = 1f,
ContentMode = UIViewContentMode.ScaleAspectFit
};
chart.AddAnnotation (new TKChartViewAnnotation(imageView, new NSNumber(positionX), new NSNumber(positionY), mySeries));

Looking for the same functionality in Native Android project

0
Lance | Senior Manager Technical Support
Telerik team
answered on 27 Oct 2020, 07:58 PM

Hi Rodrigo,

You can find this type of information in the Android wrapper documentation. If you go to the Chart annotations documentation, you'll find an section on creating custom annotations.

The example will show you how to create an ICustomAnnotationRenderer to draw Text. You will need to figure out your own custom logic for the Render method (i.e. instead drawing text, draw a bitmap).

To see any of this in code, you can open you locally installed demos C:\Program Files (x86)\Progress\Telerik UI for Xamarin R3 2020\Examples\Android 

Regards,
Lance | Manager Technical Support
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Rodrigo
Top achievements
Rank 1
Veteran
answered on 01 Nov 2020, 01:33 PM

Hi Lance,

 

and this is exactly what I'm trying to figure out. If I pass Bitmap or any other object - it gets rendered as object Type NAME, NOT image itself.

Regards,

Rodrigo

0
Lance | Senior Manager Technical Support
Telerik team
answered on 02 Nov 2020, 02:09 PM

Hi Rodrigo,

That sounds like you're experiencing a ToString() conversion. Please visit the Xamarin.Android Canvas-based bitmap API and then revisit your custom rendering code to make sure you're actually rendering a bitmap and not painting text. If I had an example, I would send it to you for comparison, but we do not have one available. 

If you're still stuck, I recommend opening a StackOverflow post and ask for Xamarin.Android bitmap rendering assistance. That's where you will find help for general Xamarin.Android development.

Tip: Be sure to decouple the question from the Chart from the basis of the question and focus on why you're painting text name of the class instead of the bitmap. Keeping focus on the Telerik chart will distract from the real problem.

Professional Support

If you open a Priority Support Ticket using your support license, one of the developers may or may not be able to assist because what you're requesting is a very custom implementation is not guaranteed (see Scope of Support for why we do not do custom implementations). I will see the ticket come in and discuss it with the dev team.

Regards,
Lance | Manager Technical Support
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Lance | Senior Manager Technical Support
Telerik team
answered on 02 Nov 2020, 02:57 PM

Hello Rodrigo,

I wanted to follow up and share an example that I wrote a few years ago on how to render custom labels. I believe they will help guide you in the right direction as they really show how you can take things in  any direction you want.

  1. First, go to my Custom Xamarin Demos repository. I keep many special edge cases and out of scope demos there.
  2. You will see a CustomSeriesLabels demo. Navigate to that project

In the Android project's Effects folder you will find several implementations. The two I want to show you are:

Let's drill into both.

Vertical Label Renderer

This demo is not exactly what you need, but I think it's show a very important concept... rendering content to the Canvas. 

You will see that I've broken out the label rendering logic so that it's easier to follow and understand how things are rendered inside the root canvas. Carefully review the code inside the RenderLabel method for guidance.

Horizontal Label Renderer

If the vertical one was too complex (because I needed to rotate and remeasure), take a look at the horizontal label renderer. In there you will find a simple RenderLabel method (again, you'll be drawing a bitmap instead of a circle)

I hope this helps.

Regards,
Lance | Manager Technical Support
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Accepted
Lance | Senior Manager Technical Support
Telerik team
answered on 02 Nov 2020, 09:20 PM

Hi Rodrigo,

I had a little extra free time today, so I went ahead and built a custom example for you. I updated my existing demo with a separate annotation renderer so you can just focus on what is important

See:

Here's the result at runtime:

I take you through the code below, but you can find the complete source in the same demo (I pushed the changes in the repo)

First, here's the annotation content renderer:

using Android.Graphics;
using Com.Telerik.Android.Common.Math;
using Com.Telerik.Widget.Chart.Engine.Decorations.Annotations.Custom;

namespace CustomSeriesLabels.Android.ChartFeatureRenderers
{
    public class ImageAnnotationRenderer : Java.Lang.Object, ICustomAnnotationRenderer
    {
        public RadSize MeasureContent(Java.Lang.Object content)
        {
            if (content == null)
            {
                return RadSize.Empty;
            }

            // Cast the content as Bitmap
            var imgBitmap = (Bitmap)content;

            // Get the bitmap dimensions to measure the size of the contents.
            return new RadSize(imgBitmap.Width, imgBitmap.Height);
        }

        public void Render(
            Java.Lang.Object content,
            RadRect layoutSlot,
            Canvas canvas,
            Paint paint)
        {
            if (content == null)
            {
                return;
            }

            // Cast the content as Bitmap
            var imgBitmap = (Bitmap)content;

            // Draw the bitmap to the Canvas
            canvas.DrawBitmap(
                imgBitmap, 
                (float)layoutSlot.GetX() - (float)(layoutSlot.Width / 2.0),
                (float)layoutSlot.Bottom - (float)layoutSlot.Height / 2,
                paint);

        }
    }
}

> As you can see, it is very simply drawing the bitmap onto the canvas.

That's it, you would just use that annotation renderer whenever you add a custom annotation that has Bitmap content.  In the following custom chart renderer, I show how to use it when adding an annotation:

using System;
using System.IO;
using Com.Telerik.Widget.Chart.Visualization.Annotations.Cartesian;
using Com.Telerik.Widget.Chart.Visualization.CartesianChart;
using Com.Telerik.Widget.Chart.Visualization.CartesianChart.Series.Categorical;
using CustomSeriesLabels.Android.Renderers;
using Android.Graphics;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using System.Threading.Tasks;

// [assembly: ResolutionGroupName("MyCompany")] // Already defined in another Effect, if you are only copying this class, be sure to uncomment this in your project
[assembly: ExportEffect(typeof(CustomSeriesLabels.Android.Effects.CustomAnnotationEffect), "CustomAnnotationEffect")]
namespace CustomSeriesLabels.Android.Effects
{
    public class CustomAnnotationEffect : PlatformEffect
    {
        protected override async void OnAttached()
        {
            if (Control is RadCartesianChartView nativeChart && nativeChart.Series.Get(0) is BarSeries barSeries)
            {
                var myImage = await GetMyImageAsync();

                var annotation = new CartesianCustomAnnotation(
                    nativeChart.VerticalAxis,
                    nativeChart.HorizontalAxis,
                    6,
                    "Feb",
                    myImage);

                annotation.ContentRenderer = new ImageAnnotationRenderer();

                nativeChart.Annotations.Add(annotation);
            }
        }

        protected override void OnDetached()
        {
        }

        private static async Task<Bitmap> GetMyImageAsync()
        {
            var request = new System.Net.HttpWebRequest(new Uri("https://dvlup.blob.core.windows.net/general-app-files/OfficialIcons/LancelotFavicon.jpg"));

            var localFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            var imgPath = System.IO.Path.Combine(localFolder, "favicon.jpg");

            using (var response = request.GetResponse())
            using (var stream = response.GetResponseStream())
            {
                using (var fs = File.OpenWrite(imgPath))
                {
                    await stream.CopyToAsync(fs);
                    await stream.FlushAsync();
                }
            }

            var imgBitmap = await BitmapFactory.DecodeFileAsync(imgPath, new BitmapFactory.Options { InJustDecodeBounds = false });
            return imgBitmap;
        }
    }
}

 

In the XAML, it is just like any normal Xamarin.Forms Effect.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="CustomSeriesLabels.Portable.Views.AnnotationsPage" ...>
    <Grid Padding="20">
        <telerikChart:RadCartesianChart>

            <telerikChart:RadCartesianChart.Effects>
                <effects:CustomAnnotationEffect />
            </telerikChart:RadCartesianChart.Effects>

            <telerikChart:RadCartesianChart.HorizontalAxis>
                <telerikChart:CategoricalAxis ShowLabels="True" />
            </telerikChart:RadCartesianChart.HorizontalAxis>
            <telerikChart:RadCartesianChart.VerticalAxis>
                <telerikChart:NumericalAxis ShowLabels="True"  Minimum="0"  Maximum="15"/>
            </telerikChart:RadCartesianChart.VerticalAxis>
            <telerikChart:RadCartesianChart.Series>
                <telerikChart:BarSeries ItemsSource="{Binding BarSeriesData}">
                    <telerikChart:BarSeries.ValueBinding>
                        <telerikChart:PropertyNameDataPointBinding PropertyName="Value" />
                    </telerikChart:BarSeries.ValueBinding>
                    <telerikChart:BarSeries.CategoryBinding>
                        <telerikChart:PropertyNameDataPointBinding PropertyName="Category" />
                    </telerikChart:BarSeries.CategoryBinding>
                </telerikChart:BarSeries>
            </telerikChart:RadCartesianChart.Series>
        </telerikChart:RadCartesianChart>
    </Grid>
</ContentPage>

 

Side Note:

Your next question may be how you can get an image file from the Xamarin.Forms project into the Xamarin.Android project... this is outside the scope of what I can help you with. There are literally dozens and dozens of article and examples and StackOverflow posts on file handling in Xamarin. My example just downloads one and saves it locally.

Regards,
Lance | Manager Technical Support
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

// Cast the content as Bitmap
0
Rodrigo
Top achievements
Rank 1
Veteran
answered on 03 Nov 2020, 10:44 PM

Hi Lance,

Thank you very much for the thorough examples. ImageAnnotationRenderer was exactly what I needed.

Appreciate your help.

Regards,

Rodrigo

Tags
Chart - Xamarin.Android
Asked by
Rodrigo
Top achievements
Rank 1
Veteran
Answers by
Didi
Telerik team
Rodrigo
Top achievements
Rank 1
Veteran
Lance | Senior Manager Technical Support
Telerik team
Share this question
or