16 min read

In this article by Michael Williams, author of the book Xamarin Blueprints, will walk you through native development with Xamarin by building an iOS and Android application that will read from your local gallery files and display them in a UITableView and ListView

(For more resources related to this topic, see here.)

Create an iOS project

Let’s begin our Xamarin journey; firstly we will start by setting up our iOS project in Xamarin Studio:

  1. Start by opening Xamarin Studio and creating a new iOS project. To do so, we simply select File | New | Solution and select an iOS Single View App; we must also give it a name and add in the bundle ID you want in order to run your application.

    It is recommended that for each project, a new bundle ID be created, along with a developer provisioning profile for each project.

  2. Now that we have created the iOS project, you will be taken to the following screen:

Doesn’t this look familiar? Yes, it is our AppDelegate file, notice the .cs on the end, because we are using C-sharp (C#), all our code files will have this extension (no more .h or .m files).

Before we go any further, spend a few minutes moving around the IDE, expand the folders, and explore the project structure; it is very similar to an iOS project created in XCode.

Create a UIViewController and UITableView

Now that we have our new iOS project, we are going to start by creating a UIViewController. Right-click on the project file, select Add | New File, and select ViewController from the iOS menu selection in the left-hand box:

You will notice three files generated, a .xib, a .cs and a .designer.cs file. We don’t need to worry about the third file; this is automatically generated based upon the other two files:

Right-click on the project item and select Reveal in Finder,

This will bring up the finder where you will double-click on the GalleryCell.xib file; this will bring up the user-interface designer in XCode. You should see automated text inserted into the document to help you get started.

Firstly, we must set our namespace accordingly, and import our libraries with using statements. In order to use the iOS user interface elements, we must import the UIKit and CoreGraphics libraries. Our class will inherit the UIViewController class in which we will override the ViewDidLoad function:

namespace Gallery.iOS

{

    using System;

    using System.Collections.Generic;

 

    using CoreGraphics;

    using UIKit;

 

    public partial class MainController : UIViewController

    {

        private UITableView _tableView;

 

        private TableSource _source;

 

        private ImageHandler _imageHandler;

 

        public MainController () : base ("MainController", null)

        {

            _source = new TableSource ();

 

            _imageHandler = new ImageHandler ();

            _imageHandler.AssetsLoaded += handleAssetsLoaded;

        }

 

        private void handleAssetsLoaded (object sender, EventArgs e)

        {

            _source.UpdateGalleryItems (_imageHandler.CreateGalleryItems());

            _tableView.ReloadData ();

        }

 

        public override void ViewDidLoad ()

        {

            base.ViewDidLoad ();

 

            var width = View.Bounds.Width;

            var height = View.Bounds.Height;

 

            tableView = new UITableView(new CGRect(0, 0, width, height));

            tableView.AutoresizingMask = UIViewAutoresizing.All;

            tableView.Source = _source;

 

            Add (_tableView);

        }

    }

}

 

Our first UI element created is a UITableView. This will be used to insert into the UIView of the UIViewController, and we also retrieve width and height values of the UIView to stretch the UITableView to fit the entire bounds of the UIViewController. We must also call Add to insert the UITableView into the UIView. In order to have the list filled with data, we need to create a UITableSource to contain the list of items to be displayed in the list. We will also need an object called GalleryModel; this will be the model of data to be displayed in each cell.

Follow the previous process for adding in two new .cs files, one will be used to create our UITableSource class and the other for the GalleryModel class. In TableSource.cs, first we must import the Foundation library with the using statement:

using Foundation;

Now for the rest of our class. Remember, we have to override specific functions for our UITableSource to describe its behavior. It must also include a list for containing the item view-models that will be used for the data displayed in each cell:

public class TableSource : UITableViewSource

    {

        protected List<GalleryItem> galleryItems;

        protected string cellIdentifier = "GalleryCell";

 

        public TableSource (string[] items)

        {

            galleryItems = new List<GalleryItem> ();

        }

    }

We must override the NumberOfSections function; in our case, it will always be one because we are not having list sections:

 public override nint NumberOfSections (UITableView tableView)

        {

            return 1;

        }

To determine the number of list items, we return the count of the list:

public override nint RowsInSection (UITableView tableview, nint section)

        {

            return galleryItems.Count;

        }

Then we must add the GetCell function, this will be used to get the UITableViewCell to render for a particular row. But before we do this, we need to create a custom UITableViewCell.

Customizing a cells appearance

We are now going to design our cells that will appear for every model found in the TableSource class. Add in a new .cs file for our custom UITableViewCell.

We are not going to use a .xib and simply build the user interface directly in code using a single .cs file.

Now for the implementation:

public class GalleryCell: UITableViewCell 

    {

        private UIImageView _imageView;

 

        private UILabel _titleLabel;

 

        private UILabel _dateLabel;

 

        public GalleryCell (string cellId) : base (UITableViewCellStyle.Default, cellId)

        {

            SelectionStyle = UITableViewCellSelectionStyle.Gray;

 

            _imageView = new UIImageView()

            {

                TranslatesAutoresizingMaskIntoConstraints = false,

            };

 

            _titleLabel = new UILabel ()

            {

                TranslatesAutoresizingMaskIntoConstraints = false,

            };

 

            _dateLabel = new UILabel ()

            {

                TranslatesAutoresizingMaskIntoConstraints = false,

            };

 

            ContentView.Add (imageView);

            ContentView.Add (titleLabel);

            ContentView.Add (dateLabel);

        }

    }

Our constructor must call the base constructor, as we need to initialize each cell with a cell style and cell identifier. We then add in a UIImageView and two UILabels for each cell, one for the file name and one for the date. Finally, we add all three elements to the main content view of the cell.

When we have our initializer, we add the following:

public void UpdateCell (GalleryItem gallery)

        {

            _imageView.Image = UIImage.LoadFromData (NSData.FromArray (gallery.ImageData));

            _titleLabel.Text = gallery.Title;

            _dateLabel.Text = gallery.Date;

        }

 

        public override void LayoutSubviews ()

        {

            base.LayoutSubviews ();

 

            ContentView.TranslatesAutoresizingMaskIntoConstraints = false;

 

            // set layout constraints for main view

            AddConstraints (NSLayoutConstraint.FromVisualFormat("V:|[imageView(100)]|", NSLayoutFormatOptions.DirectionLeftToRight, null, new NSDictionary("imageView", imageView)));

            AddConstraints (NSLayoutConstraint.FromVisualFormat("V:|[titleLabel]|", NSLayoutFormatOptions.DirectionLeftToRight, null, new NSDictionary("titleLabel", titleLabel)));

            AddConstraints (NSLayoutConstraint.FromVisualFormat("H:|-10-[imageView(100)]-10-[titleLabel]-10-|", NSLayoutFormatOptions.AlignAllTop, null, new NSDictionary ("imageView", imageView, "titleLabel", titleLabel)));

            AddConstraints (NSLayoutConstraint.FromVisualFormat("H:|-10-[imageView(100)]-10-[dateLabel]-10-|", NSLayoutFormatOptions.AlignAllTop, null, new NSDictionary ("imageView", imageView, "dateLabel", dateLabel)));

        }

Our first function, UpdateCell, simply adds the model data to the view, and our second function overrides the LayoutSubViews method of the UITableViewCell class (equivalent to the ViewDidLoad function of a UIViewController).

Now that we have our cell design, let’s create the properties required for the view model. We only want to store data in our GalleryItem model, meaning we want to store images as byte arrays. Let’s create a property for the item model:

namespace Gallery.iOS

{

    using System;

 

    public class GalleryItem

    {

        public byte[] ImageData;

 

        public string ImageUri;

 

        public string Title;

 

        public string Date;

 

        public GalleryItem ()

        {

        }

    }

}

Now back to our TableSource class. The next step is to implement the GetCell function:

public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)

        {

            var cell = (GalleryCell)tableView.DequeueReusableCell (CellIdentifier);

            var galleryItem = galleryItems[indexPath.Row];

 

            if (cell == null)

            {

                // we create a new cell if this row has not been created yet

                cell = new GalleryCell (CellIdentifier);

            }

 

            cell.UpdateCell (galleryItem);

 

            return cell;

        }

Notice the cell reuse on the if statement; you should be familiar with this type of approach, it is a common pattern for reusing cell views and is the same as the Objective-C implementation (this is a very basic cell reuse implementation). We also call the UpdateCell method to pass in the required GalleryItem data to show in the cell. Let’s also set a constant height for all cells. Add the following to your TableSource class:

public override nfloat GetHeightForRow (UITableView tableView, NSIndexPath indexPath)

        {

            return 100;

        }

So what is next?

public override void ViewDidLoad ()

{

..

table.Source = new TableSource();

..

}

Let’s stop development and have a look at what we have achieved so far. We have created our first UIViewController, UITableView, UITableViewSource, and UITableViewCell, and bound them all together. Fantastic!

We now need to access the local storage of the phone to pull out the required gallery items. But before we do this, we are now going to create an Android project and replicate what we have done with iOS.

Create an Android project

Let’s continue our Xamarin journey with Android. Our first step is to create new general Android app:

The first screen you will land on is MainActivity. This is our starting activity, which will inflate the first user interface; take notice of the configuration attributes:

[Activity (Label = "Gallery.Droid", MainLauncher = true, Icon = "@mipmap/icon")]

The MainLauncher flag indicates the starting activity; one activity must have this flag set to true so the application knows what activity to load first. The icon property is used to set the application icon, and the Label property is used to set the text of the application, which appears in the top left of the navigation bar:

namespace Gallery.Droid

{

    using Android.App;

    using Android.Widget;

    using Android.OS;

 

    [Activity (Label = "Gallery.Droid", MainLauncher = true, Icon = "@mipmap/icon")]

    public class MainActivity : Activity

    {

        int count = 1;

 

        protected override void OnCreate (Bundle savedInstanceState)

        {

            base.OnCreate (savedInstanceState);

 

            // Set our view from the "main" layout resource

            SetContentView (Resource.Layout.Main);

        }

    }

}

The formula for our activities is the same as Java; we must override the OnCreate method for each activity where we will inflate the first XML interface Main.xml.

Creating an XML interface and ListView

Our starting point is the main.xml sheet; this is where we will be creating the ListView:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout 

    android_orientation="vertical"

    android_layout_width="fill_parent"

    android_layout_height="fill_parent">

    <ListView

        android_id="@+id/listView"

        android_layout_width="fill_parent"

        android_layout_height="fill_parent"

        android_layout_marginBottom="10dp"

        android_layout_marginTop="5dp"

        android_background="@android:color/transparent"

        android_cacheColorHint="@android:color/transparent"

        android_divider="#CCCCCC"

        android_dividerHeight="1dp"

        android_paddingLeft="2dp" />

</LinearLayout>

The main.xml file should already be in resource | layout directory, so simply copy and paste the previous code into this file.

Excellent! We now have our starting activity and interface, so now we have to create a ListAdapter for our ListView. An adapter works very much like a UITableSource, where we must override functions to determine cell data, row design, and the number of items in the list.

Xamarin Studio also has an Android GUI designer.

Right-click on the Android project and add in a new empty class file for our adapter class. Our class must inherit the BaseAdapter class, and we are going to override the following functions:

public override long GetItemId(int position);

public override View GetView(int position, View convertView, ViewGroup parent);

Before we go any further, we need to create a model for the objects used to contain the data to be presented in each row. In our iOS project, we created a GalleryItem to hold the byte array of image data used to create each UIImage. We have two approaches here: we could create another object to do the same as the GalleryItem, or even better, why don’t we reuse this object using a shared project?

Shared projects

We are going to delve into our first technique for sharing code between different platforms. This is what Xamarin tries to achieve with all of its development, and we want to reuse as much code as possible. The biggest disadvantage when developing Android and iOS applications in two different languages is that we can’t reuse anything.

Let’s create our first shared project:

Our shared project will be used to contain the GalleryItem model, so whatever code we include in this shared project can be accessed by both the iOS and Android projects:

In the preceding screenshot, have a look at the Solution explorer, and notice how the shared project doesn’t contain anything more than .cs code sheets. Shared projects do not have any references or components, just code that is shared by all platform projects. When our native projects reference these shared projects, any libraries being referenced via using statements come from the native projects.

Now we must have the iOS and Android projects reference the shared project; right-click on the References folder and select Edit References:

Select the shared project you just created and we can now reference the GalleryItem object from both projects.

Summary

In this article, we have seen a walkthrough of building a gallery application on both iOS and Android using native libraries. This will be done on Android using a ListView and ListAdapter.

Resources for Article:

 


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here