Xamarin.Tip – Embed Your Xamarin.Forms Pages in Your iOS ViewControllers

The number one complaint I hear about Xamarin.Forms is the slow startup time of the applications that use it. The team at Xamarin has done a lot to help with this and give some more options such as XAML Compilation, Lazy Loading, and Ahead of Time Compilation. Check out some of David Ortinau’s suggestions here: 5 Ways to Boost Xamarin.Forms App Startup Time.

However, one of the best ways I’ve found to help with this issue is to use Xamarin.Forms Embedding to its full potential. Embedding became available in Xamarin.Forms 2.5 and at a high level allows you to embed your Xamarin.Forms pages into your Native Xamarin views by using the extension methods .CreateFragment(Context context); for Android and .CreateViewController(); for iOS. This is most commonly used for when you want to share some UI in your Xamarin Native apps using Xamarin.Forms, however you still need to call Xamarin.Forms.Init() which is one of the main culprits in the slow startup time.

For Android embedding, see: Xamarin.Tip – Embed Your Xamarin.Forms Pages in Your Android Activities

iOS

The solution proposed here still allows you to create almost all of your views in Xamarin.Forms by using embedding, but requires some architecture and design changes. The premise is this:

  • First ViewController is non-Xamarin.Forms and loads your app right away
  • Init Xamarin.Forms after this ViewController is loaded
  • Embed Xamarin.Forms pages in other ViewControllers
  • Lift navigation out of Xamarin.Forms and into the native navigation.

This also has advantages outside the startup time such as better performance on transitions, more natural look and feel to end-users, performance gains in other areas, and a smaller app-size.

This means:

  • No NavigationPage
  • No Xamarin.Forms toolbar (using the native UINavigationBar control instead)
  • Still have MVVM and all our bindings we would expect

So if you’re already using a framework that is not tied down to Xamarin.Forms such as MvvmLight, you don’t have to change much behind the scenes since the INavigationService is abstracted.

Let’s kick this off by creating an inheritable ViewController that handles the embedding and layout how we want. Be sure to use your Storyboard and have the RootViewController be a UINavigationController, then use this embeddable ViewController within that.

XamarinFormsViewController

/// <summary>
/// Base xamarin forms view controller. Used for embedding a Xamarin.Forms page within a native view controller.
/// When inheriting from this, be sure to create a ViewController within the storyboard as well so that navigation
/// can properly work.
/// </summary>
public abstract class XamarinFormsViewController<TPage> : UIViewController
    where TPage : ContentPage, new()
{
    protected TPage _page;


    public XamarinFormsViewController(IntPtr handle) : base(handle)
    {
    }

    /// <summary>
    /// Load the Xamarin.Forms Page's ViewController into the parent
    /// </summary>
    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        _page = new TPage();
        var xamarinFormsController = _page.CreateViewController();
        AddChildViewController(xamarinFormsController);
        View.AddSubview(xamarinFormsController.View);
        xamarinFormsController.DidMoveToParentViewController(this);

        // add whatever other settings you want - ex:
        EdgesForExtendedLayout = UIKit.UIRectEdge.None;
        ExtendedLayoutIncludesOpaqueBars = false;
        AutomaticallyAdjustsScrollViewInsets = false;

    }
}

When creating a child of this XamarinFormsViewController, be sure to also create an empty ViewController in your .storyboard file for each unique type. This is required for handling navigation using the storyboard and root UINavigationViewController. If you’re using .xib files for some reason, then don’t worry about it, just instantiate the XamarinFormsViewController itself (you’ll have to add the other constructor overloads though).

So now we can create a simple Xamarin.Forms page:

SomePage.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"     x:Class="MyApp.Pages.SomePage">
    <ContentPage.Content>
        <Grid>
            <Label Text="I'm Embedded!" HorizontalOptions="Center" VerticalOptions="Center"/>
        </Grid>
    </ContentPage.Content>
</ContentPage>

Then create the associated ViewController:

SomeViewController.cs

public class SomeViewController: XamarinFormsViewController<SomePage>
{
    protected void ViewDidLoad()
    {
        base.ViewDidLoad();

        NavigationItem.Title = "Some title";
    }
}

Now all we have to do is kick off this SomeViewController after calling Xamarin.Forms.Init() and we are good to go! If we have a MainController we can call it before navigating if it isn’t initialized, or execute it in ViewDidLoad or a similar lifecycle event.

MainController.cs

public class MainController: UIViewController
{    

    protected override void ViewDidLoad()
    {
        base.ViewDidLoad();

        // assume SomeButton is created and named in the Storyboard file
        SomeButton.TouchUpInside += delegate 
        {
             if(!Xamarin.Forms.Forms.IsInitialized)
                 Xamarin.Forms.Forms.Init(this, savedInstance);

             var someController = this.Storyboard.InstantiateViewController("SomeController") as SomeViewController;
             NavigationController.PushViewController(someController, true);
        }
    } 
}

And there you have it! Some new Xamarin.Forms embedding for performance and other extra benefits 🙂

In future posts of this subject, we’ll look at extending interactions between the Xamarin.Forms Page and the native Activity and ViewControllers, using advanced native components with the embedded Xamarin.Forms Page, and more!

Let me know what you think of this pattern – have you used it? What else would you want to hear about it??

Be sure to checkout some of the Xamarin examples on embedding too!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

Xamarin.Tip – Adding a Password Toggle Button in Android

Here’s another quick tip to use when building your Android apps whether in Xamarin, or native in Java/Kotlin. Depending on the target audience of your application, you may find it necessary to add a password toggle button to your password EditText to allow users to view and hide their password while typing it.

In this post, we will explore how to do this in Android natively while future posts will cover iOS (which is more involved) as well as building a Xamarin.Forms custom Entry view (no renderer’s required!) to handle the toggling.

There are two places to achieve this in Android – in the xml of the layout resource, or in the code behind where manipulating the view. Luckily, Android (unlike iOS) has this feature built in to the properties of the TextInputLayout control.

We get the ability to show the button as well as apply a custom Drawable if we want!

Using the Layout Resource

Here is an example using the TextInputLayout from the xml side to wrap an EditText:

activity_login.axml

<android.support.design.widget.TextInputLayout
    android:id="@+id/password_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:passwordToggleEnabled="true"
    app:passwordToggleTint="@android:color/white">
    <android.support.design.widget.TextInputEditText
        android:id="@+id/password_editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:password="true"
        android:hint="Password"
        android:textColor="@android:color/white"
        android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>

Notice the two properties:

app:passwordToggleEnabled="true"

and

app:passwordToggleTint="@android:color/white"

These are the two that control showing the password toggle. Alternatively, you can set the

app:passwordToggleDrawable="@drawable/someDrawable"

Using Code

If you’re manipulating your TextInputLayout in your code, you can also update these fields very easily:

LoginActivity.cs

public class LoginActivity : AppCompatActivity
{
    protected override void OnCreate(Bundle savedInstance)
    {
        base.OnCreate(savedInstance);
        SetContentView(Resource.Layout.activity_login);

        var editTextLayout = FindViewById<TextInputLayout>(Resource.Id.password_layout);

        editTextLayout.PasswordVisibilityToggleEnabled = true;
    }
}

That’s it!

Results

Take a look for yourself!

Screen Shot 2018-03-15 at 11.04.31 AM
Screen Shot 2018-03-15 at 11.04.40 AM


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tip – Embed Your Xamarin.Forms Pages in Your Android Activities

The number one complaint I hear about Xamarin.Forms is the slow startup time of the applications that use it. The team at Xamarin has done a lot to help with this and give some more options such as XAML Compilation, Lazy Loading, and Ahead of Time Compilation. Check out some of David Ortinau’s suggestions here: 5 Ways to Boost Xamarin.Forms App Startup Time.

However, one of the best ways I’ve found to help with this issue is to use Xamarin.Forms Embedding to its full potential. Embedding became available in Xamarin.Forms 2.5 and at a high level allows you to embed your Xamarin.Forms pages into your Native Xamarin views by using the extension methods .CreateFragment(Context context); for Android and .CreateViewController(); for iOS. This is most commonly used for when you want to share some UI in your Xamarin Native apps using Xamarin.Forms, however you still need to call Xamarin.Forms.Init() which is one of the main culprits in the slow startup time.

The solution proposed here still allows you to create almost all of your views in Xamarin.Forms by using embedding, but requires some architecture and design changes. The premise is this:

  • First Activity is non-Xamarin.Forms and loads your app right away
  • Init Xamarin.Forms after this activity is loaded
  • Embed Xamarin.Forms pages in other Activities
  • Lift navigation out of Xamarin.Forms and into the native navigation.

This also has advantages outside the startup time such as better performance on transitions, more natural look and feel to end-users, performance gains in other areas, and a smaller app-size.

This means:

  • No NavigationPage
  • No Xamarin.Forms toolbar (using the native Toolbar control instead)
  • Still have MVVM and all our bindings we would expect

So if you’re already using a framework that is not tied down to Xamarin.Forms such as MvvmLight, you don’t have to change much behind the scenes since the INavigationService is abstracted.

Let’s kick this off by creating an inheritable Activity that handles the embedding and layout how we want.

xamarin_forms_activity.axml

<?xml version="1.0" encoding="utf-8"?>
<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.support.design.widget.AppBarLayout     
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/actionBarSize"
        android:layout_gravity="top"
        app:elevation="0dp">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/actionBarSize"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    </android.support.design.widget.AppBarLayout>
    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

This layout gives us the native Android toolbar (with the shadow! Another plus!) and a space for us to embed in this FrameLayout.

Now let’s create the Activity:

XamarinFormsActivity.cs

/// <summary>
/// Base xamarin forms activity.
/// This activity contains a single fragment in the layout and renders the fragment pulled from the Xamarin.Forms page
/// </summary>
public abstract class XamarinFormsActivity<TPage> : AppCompatActivity
    where TPage : ContentPage, new()
{
    protected readonly TPage _page;
    protected int _containerLayoutId = Resource.Layout.activity_fragment_container;
    public Android.Support.V7.Widget.Toolbar Toolbar { get; set; }
    public AppBarLayout AppBar { get; set; }

    public XamarinFormsActivity()
    {
        _page = new TPage();
    }

    /// <summary>
    /// Creates the activity and maps the Xamarin.Forms page to the fragment
    /// </summary>
    /// <param name="savedInstanceState">Saved instance state.</param>
    protected override void OnCreate(Android.OS.Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.xamarin_forms_activity);

        Toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
        if (Toolbar?.Parent != null)
        {
            AppBar = Toolbar?.Parent as AppBarLayout;
            SetSupportActionBar(Toolbar);
        }

        // register the fragment
        var transaction = SupportFragmentManager.BeginTransaction();
        transaction.Add(Resource.Id.fragment_container, _page.CreateSupportFragment(this));
        transaction.Commit();
        SupportActionBar?.SetDisplayHomeAsUpEnabled(true);
        SupportActionBar?.SetDisplayShowHomeEnabled(true);
        Toolbar?.SetBackgroundColor(Android.Graphics.Color.White);
        // everything else from this point should be managed by the Xamarin.Forms page behind the fragment
    }
}

So now we can create a simple Xamarin.Forms page:

SomePage.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"     x:Class="MyApp.Pages.SomePage">
    <ContentPage.Content>
        <Grid>
            <Label Text="I'm Embedded!" HorizontalOptions="Center" VerticalOptions="Center"/>
        </Grid>
    </ContentPage.Content>
</ContentPage>

Then create the associated Activity:

SomeActivity.cs

public class SomeActivity : XamarinFormsActivity<SomePage>
{
    protected override void OnCreate(Bundle savedInstance)
    {
        SupportActionBar.Title = "Some Page";
    }
}

Now all we have to do is kick off this SomeActivity after calling Xamarin.Forms.Init() and we are good to go! If we have a MainActivity we can call it before navigating if it isn’t initialized, or execute it in OnResume or a similar lifecycle event.

MainActivity.cs

public class MainActivity : AppCompatActivity 
{    

    protected override void OnCreate(Bundle savedInstance)
    {
        base.OnCreate(savedInstance);
        SetContentView(Resource.Layout.main_activity);
        var someButton = FindViewBy<Button>(Resource.Id.some_button);
        someButton.Click += delegate 
        {
             if(!Xamarin.Forms.Forms.IsInitialized)
                 Xamarin.Forms.Forms.Init(this, savedInstance);
             StartActivity(typeof(SomeActivity));
        }
    } 
}

And there you have it! Some new Xamarin.Forms embedding for performance and other extra benefits 🙂

In future posts of this subject, we’ll look at the same setup for iOS, extending interactions between the Xamarin.Forms Page and the native Activity and ViewControllers, using advanced native components with the embedded Xamarin.Forms Page, and more!

Let me know what you think of this pattern – have you used it? What else would you want to hear about it??

Be sure to checkout some of the Xamarin examples on embedding too!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Adding a File Upload Field to Your Swagger UI With Swashbuckle

If you’re building ASP.NET Core Web APIs, then I hope you’ve heard of Swashbuckle – the tool to generate the Swagger UI automatically for all of your controllers to make manual testing your endpoints visual and simple.

Out of the box, the documentation helps you set up your UI, handle different ways to authenticate (which we will touch on in a later post), and have it all hooked up to your controllers. However, this only handles the most common cases of required requests with query string parameters and HTTP Body content.

In this post, we’ll look at a quick and easy way to also add File upload fields for your API endpoints that consume IFormFile properties to make testing file uploading even easier.

Basic Swagger Setup

First thing’s first, install that puppy:

Package Manager : Install-Package Swashbuckle.AspNetCore
CLI : dotnet add package Swashbuckle.AspNetCore

Let’s first look at a simple swagger setup as our baseline before we add everything for our HTTP Header Field.

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });
    });
    // ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...
    app.UseMvc();

    app.UseSwagger();
    app.UseSwaggerUI(config =>
    {
        config.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1");
    });
    // ...
}
//...

This setup gives us all we need for our basic UI and wireup to our controllers!
Running this gives us our basic swagger at /swagger:
Swagger no header

Adding a File Upload Field

What we have to do now is add an OperationFilter to our swagger generation. These OperationFilters can do a whole lot and enable us to customize the swagger document created which is what drives the fields and info on the UI.

Let’s start by creating our FormFileSwaggerFilter class.

FormFileSwaggerFilter.cs

/// <summary>
/// Filter to enable handling file upload in swagger
/// </summary>
public class FormFileSwaggerFilter : IOperationFilter
{
    private const string formDataMimeType = "multipart/form-data";
    private static readonly string[] formFilePropertyNames =
        typeof(IFormFile).GetTypeInfo().DeclaredProperties.Select(p => p.Name).ToArray();

    public void Apply(Operation operation, OperationFilterContext context)
    {
        var parameters = operation.Parameters;
        if (parameters == null || parameters.Count == 0) return;

        var formFileParameterNames = new List<string>();
        var formFileSubParameterNames = new List<string>();

        foreach (var actionParameter in context.ApiDescription.ActionDescriptor.Parameters)
        {
            var properties =
                actionParameter.ParameterType.GetProperties()
                    .Where(p => p.PropertyType == typeof(IFormFile))
                    .Select(p => p.Name)
                    .ToArray();

            if (properties.Length != 0)
            {
                formFileParameterNames.AddRange(properties);
                formFileSubParameterNames.AddRange(properties);
                continue;
            }

            if (actionParameter.ParameterType != typeof(IFormFile)) continue;
            formFileParameterNames.Add(actionParameter.Name);
        }

        if (!formFileParameterNames.Any()) return;

        var consumes = operation.Consumes;
        consumes.Clear();
        consumes.Add(formDataMimeType);

        foreach (var parameter in parameters.ToArray())
        {
            if (!(parameter is NonBodyParameter) || parameter.In != "formData") continue;

            if (formFileSubParameterNames.Any(p => parameter.Name.StartsWith(p + "."))
                || formFilePropertyNames.Contains(parameter.Name))
                parameters.Remove(parameter);
        }

        foreach (var formFileParameter in formFileParameterNames)
        {
            parameters.Add(new NonBodyParameter()
            {
                Name = formFileParameter,
                Type = "file",
                In = "formData"
            });
        }
    }
}

This operation filter takes the operation parameters, then uses reflection to find the type of the field. If the field is an IFormFile, then we add a new file field from the formData section to our parameters. This in turn will update our swagger definition json file, and when rendered adds the field to our UI.

This even works great with endpoints that take a separate HTTP Body, query parameters, and files!

Now we need to reference it in our Startup when we initialize swagger:

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });

        config.OperationFilter<FormFileSwaggerFilter>();
    });
    // ...
}

Here’s an example controller with an endpoint that uses the file upload:

FileUploadController

[Route("api/[controller]")]
public class FileUploadController : Controller
{
    [HttpPost]
    public async Task<IActionResult> CreateMediaItem(string name, [FromForm]IFormFile file)
    {
        // Do something with the file
        return Ok();
    }
}

This controller ends up rendering in our Swagger UI as:
Fileupload_swagger

And using this, we can now submit our request with an uploaded file and all our other parameters!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Adding a Required HTTP Header to Your Swagger UI With Swashbuckle

If you’re building ASP.NET Core Web APIs, then I hope you’ve heard of Swashbuckle – the tool to generate the Swagger UI automatically for all of your controllers to make manual testing your endpoints visual and simple.

Out of the box, the documentation helps you set up your UI, handle different ways to authenticate (which we will touch on in a later post), and have it all hooked up to your controllers. However, this only handles the most common cases of required requests with query string parameters and HTTP Body content.

In this post, we’ll look at a quick and easy way to also add fields for your custom HTTP Request Headers so you can fill them out while testing without having to do some funky stuff in the console.

Basic Swagger Setup

First thing’s first, install that puppy:

Package Manager : Install-Package Swashbuckle.AspNetCore
CLI : dotnet add package Swashbuckle.AspNetCore

Let’s first look at a simple swagger setup as our baseline before we add everything for our HTTP Header Field.

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });
    });
    // ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...
    app.UseMvc();

    app.UseSwagger();
    app.UseSwaggerUI(config =>
    {
        config.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1");
    });
    // ...
}
//...

This setup gives us all we need for our basic UI and wireup to our controllers!
Running this gives us our basic swagger at /swagger:
Swagger no header

Adding Custom Headers

What we have to do now is add an OperationFilter to our swagger generation. These OperationFilters can do a whole lot and enable us to customize the swagger document created which is what drives the fields and info on the UI.

Let’s create a MyHeaderFilter and then add it to the AddSwaggerGen call.

MyHeaderFilter.cs

/// <summary>
/// Operation filter to add the requirement of the custom header
/// </summary>
public class MyHeaderFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        if (operation.Parameters == null)
            operation.Parameters = new List<IParameter>();

        operation.Parameters.Add(new NonBodyParameter
        {
            Name = "MY-HEADER",
            In = "header",
            Type = "string",
            Required = true // set to false if this is optional
        });
    }
}

And now we need to update our Startup class:

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });

        config.OperationFilter<MyHeaderFilter>();
    });
    // ...
}

Now when we run and navigate to our /swagger url, we can see:
Swagger with header

When we fill out that field, we can now pull the value off the request header in our controller!

ContentController.cs

[Route("api/[controller]")]
public class ContentController : Controller
{
    public void Search([FromBody]InputModel model)
    {
        Console.WriteLine(Request.Headers["MY-HEADER"]);        
    }
}

In further posts, we’ll talk about adding Bearer Authentication, XML Comments, and More 🙂


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Android Kotlin Basics – Lazy Loading Images with Picasso

About This Series

This “Android Kotlin Basics” blog series is all about fundamentals. We’ll take a look at the basics of building Android apps with Kotlin from the SUPER basics, to the standard basics, to the not-so-basics. We’ll also be drawing comparisons to how things are done in Kotlin vs. Java and some other programming languages to build Android apps (like C# and Xamarin or JavaScript/TypeScript for Hybrid implementations).

Find other posts in this series: Android Kotlin Basics

Check Out the Pluralsight Course!

If you like this series, be sure to check out my course on Pluralsight – Building Android Apps with Kotlin: Getting Started where you can learn more while building your own real-world application in Kotlin along the way. You can also join the conversation and test your knowledge throughout the course with learning checks through each module!

Watch it here: https://app.pluralsight.com/library/courses/building-android-apps-kotlin-getting-started/table-of-contents

Lazy Loading Images with Picasso

Loading images from remote sources without help can be a pain in Android development. You need to make the http request against the url, download the bytes of the image, create a Bitmap from those bytes, then render that BitMap in an ImageView. Not only is that tedious, but it also leaves room for tons of errors and especially the dreaded OutOfMemoryException that happens oh so often when trying to render large images in Android.

There are some really great libraries out there for helping face this issue that can also provide some other useful tools such as filtering, extensions, customizing, and more.

In the course we use Picasso which is a library by Square to help lazy load images. Find it here: http://square.github.io/picasso/

Picasso does a great job loading images asynchronously and handles rendering them with proper scale. It does a great job handling this performance in ListViews and RecyclerViews as well. This is exactly how we use it in our real-world app that we build in the course!

Picasso also has some great features with creating custom transformations or extending the functionality. In this post we will stick to the basics though. If you want to get into details check out the Java documentation at their website. Copy and pasting that into a Kotlin file in Android studio will also automatically translate it for you!

Installing

Picasso can be installed 3 ways:
– Gradle (what we use in the course)
– Maven
– Manual

To install with gradle, add:

implementation 'com.squareup.picasso:picasso:2.71828'

to your build.gradle config.

For maven use:

<dependency>
  <groupId>com.squareup.picasso</groupId>
  <artifactId>picasso</artifactId>
  <version>2.71828</version>
</dependency>

And if you want to do it manually, download the jar from http://square.github.io/picasso/ and reference it in your project.

Basic Image Loading

Now that you have Picasso in your project, lets look at how we can do simple image loading.

Let’s start with a simple layout:

activity_main.xml

<RelativeLayout 
    android:layout_height="match_parent"
    android:layout_width="match_parent">
    <ImageView android:id="@+id/myImageView"
        android:layout_height="match_parent"
        android:layout_width="match_parent" />
</RelativeLayout>

Now in our Activity code, we can load the image from the url in the onCreate override.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // load the image with Picasso
        Picasso
            .with(this) // give it the context
            .load("https://i.imgur.com/H981AN7.jpg") // load the image
            .into(myImageView) // select the ImageView to load it into
    }
}

If you’re unsure of why we didn’t need to call

val myImageView = findViewByid<ImageView>(R.id.myImageView)

Check out my post on how Kotlin automatically maps views to properties by id: Android Kotlin Basics – Auto-mapping Views

And that’s it! Now you have automatically handled all performance concerns, caching needs, sizing and downloading, and so much more!

Using in a RecyclerView

So loading a single image into a single ImageView isn’t so bad on its own, but when you have to load high-res images into large lists that can change all the time, there are a lot of places this can go wrong doing it yourself. Developers often run into:
– Lag on new items appearing
– Mismatched images
– No images loading
– Images loading incorrectly
– Dreaded Out of Memory Exceptions

Because Picasso runs the image loading asynchronously and only touches the UI thread when it needs to render the image itself, it avoid the issues of locking the UI up when loading. Because it also caches the images locally, when the image needs to be loaded again (such as scrolling back up), it does so quickly and can void mismatching renderings. Also, because it renders the image in chunks before showing it, they avoid the easy to find Out of Memory Exception that we have all come to hate when trying to render a large image into an ImageView. Normally to avoid this, you have to manually load a scaled down image into memory and handle how you scale it per image. Picasso does all this for us too.

On top of all that, Picasso ships with some of the nice-to-haves such as:
– Transitions when images load or change
– Placeholder images to show while loading from a url
– Transformations on images once loaded
– Debugging indicators to show where the image loaded from

All these great features even scale to these large lists and RecyclerViews.

Before showing how to use it in your RecyclerView, be sure to check out the other post in this series: Android Kotlin Basics – RecyclerView and Adapters unless you’re already familiar with how RecyclerViews and Adapters work 🙂

We can create a layout for our item in our list:

image_item.xml

<ImageView android:id="@+id/myImageView"
    android:layout_height="match_parent"
    android:layout_width="match_parent" />

Then a ViewHolder for this image:

ImageHolder.kt

class ImageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val myImageView: ImageView = itemView.findViewById<ImageView>(R.id.myImageView)

    fun updateWithUrl(url: String) {
        Picasso.with(itemView.context).load(url).into(myImageView)
    }
}

Now let’s create our Adapter with our datasource:

ImagesAdapter.kt

class ImageAdapter() : RecyclerView.Adapter<ImageHolder>() {
    val imageUrls: Array<string>

    init {
        imageUrls = arrayOf("https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg", "https://i.imgur.com/kUCMPWX.jpg")
    }

    override fun getItemCount(): Int {
        return imageUrls.size;
    }

    override fun onBindViewHolder(holder: ImageHolder?, position: Int) {
        var imageUrl = imageUrls[position]
        holder?.updateWithUrl(imageUrl)
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ImageHolder {
        var imageItem = LayoutInflater.from(parent?.context).inflate(R.layout.image_item, parent, false)
        return ImageHolder(imageItem)
    }
}

Last we just need to create our Activity and wireup the Adapter!

activity_main.xml

<RelativeLayout 
    android:layout_height="match_parent"
    android:layout_width="match_parent">    
    <android.support.v7.widget.RecyclerView
        android:id="@+id/imageRecycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        imageRecycler.layoutManager = LinearLayoutManager(this)
        imageRecycler.adapter = ImageAdapter()
    }
}

Now we can quickly scroll through our list and see the images loaded quickly and properly into our view!

dogs


Also, let me know what else you’d like to learn about with Android and Kotlin! Either drop a comment here or tweet at me @Suave_Pirate!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tip – No Bounce ScrollView in Xamarin.Forms

So on iOS, UIScrollView bounces by default so in Xamarin.Forms the ScrollView natrually bounces. But what if you don’t want your ScrollView to bounce? Android doesn’t bounce, so we won’t worry about it.

Let’s solve this problem with a custom renderer and control called NoBounceScrollView. This could also be done with an Effect but I like to have a custom control with the verbosity and flexibility with a renderer.

Let’s start by building a simple control in our Xamarin.Forms project:

NoBounceScrollView.cs

public class NoBounceScrollView : ScrollView { }

We don’t need anything in it since we are just going to assume it should never bounce and doesn’t affect the ScrollView in any other way. If you want, you can add a bindable property here to set Bouncable or something like that to true or false. Note: if you don’t want your ScrollView to bounce ever, then you don’t need this. Instead just have your renderer replace the default ScrollView renderer.

So we have our Xamarin.Forms control to build the renderer for, so now let’s create the iOS renderer:

NoBounceScrollViewRenderer.cs

public class NoBounceScrollViewRenderer : ScrollViewRenderer
{

    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        UpdateScrollView();
    }

    private void UpdateScrollView()
    {
        ContentInset = new UIKit.UIEdgeInsets(0, 0, 0, 0);
        if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
            ContentInsetAdjustmentBehavior = UIKit.UIScrollViewContentInsetAdjustmentBehavior.Never;
        Bounces = false;
        ScrollIndicatorInsets = new UIKit.UIEdgeInsets(0, 0, 0, 0);
    }
}

What this is doing is setting the content insets to 0 so we don’t have empty space on top or bottom that the bouncing adds, and we also set Bounces to false. Note that the ContentInsetAdjustmentBehavior is only available on iOS 11 and higher, so for that extra step, we need to check the current iOS version.

Lastly, be sure to register your renderer:

[assembly: ExportRenderer(typeof(NoBounceScrollView), typeof(NoBounceScrollViewRenderer))]

or if you are replacing all ScrollViews:

[assembly: ExportRenderer(typeof(ScrollView), typeof(NoBounceScrollViewRenderer))]

Now we have everything we need! So let’s use it in our page:

MainPage.xaml

<ContentPage ...>
    <components:NoBounceScrollView>
        <StackLayout>
            ...
        </StackLayout>
    </components:NoBounceScrollView>
</ContentPage>

Check out the difference!

You can see this control in action with the EF Go Ahead Tours Companion App


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.