Book Image

Xamarin Blueprints

By : Michael Williams
Book Image

Xamarin Blueprints

By: Michael Williams

Overview of this book

Do you want to create powerful, efficient, and independent apps from scratch that will leverage the Xamarin framework and code with C#? Well, look no further; you’ve come to the right place! This is a learn-as-you-build practical guide to building eight full-fledged applications using Xamarin.Forms, Xamarin Android, and Xamarin iOS. Each chapter includes a project, takes you through the process of building applications (such as a gallery Application, a text-to-speech service app, a GPS locator app, and a stock market app), and will show you how to deploy the application’s source code to a Google Cloud Source Repository. Other practical projects include a chat and a media-editing app, as well as other examples fit to adorn any developer’s utility belt. In the course of building applications, this book will teach you how to design and prototype professional-grade applications implementing performance and security considerations.
Table of Contents (14 chapters)
Xamarin Blueprints
Credits
About the Author
About the Reviewer
www.PacktPub.com
Preface

Custom row appearance


Let's get back to the ListAdapter implementation and design our ListView row appearance. Open the Resources | Layout folder, create a new .xml file for the cell appearance, call it CustomCell.xml, and copy in the following XML code:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:weightSum="4"> 
    <LinearLayout 
        android:orientation="vertical" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:layout_weight="1"> 
        <ImageView 
            android:id="@+id/image" 
            android:layout_width="100dp" 
            android:layout_height="100dp" 
            android:adjustViewBounds="true" /> 
    </LinearLayout> 
    <LinearLayout 
        android:orientation="vertical" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:layout_weight="3" 
        android:weightSum="2"> 
        <TextView 
            android:id="@+id/title" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_weight="1" /> 
        <TextView 
            android:id="@+id/date" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_weight="1" /> 
    </LinearLayout> 
</LinearLayout> 

We are creating the same layout as the custom cell made for iOS, but in Android we will use the ImageView and TextView objects. Now that we have our custom cell, we can implement the the GetView function. The GetView function is exactly like the GetCell function in the preceding UITableSource implementation. Open up the ListAdapter.cs file and continue with the list adapter implementation:

public class ListAdapter : BaseAdapter 
    { 
        private List<GalleryItem> _items; 
        private Activity _context; 
 
        public ListAdapter(Activity context) : base() 
        { 
            _context = context; 
            _items = new List<GalleryItem>(); 
        } 
 
        public override Java.Lang.Object GetItem (int position) 
        { 
            return null; 
        } 
 
        public override long GetItemId(int position) 
        { 
            return position; 
        } 
 
        public override int Count 
        { 
            get 
            { 
                return items.Count;  
            } 
        } 
} 

We override the Count property and functions GetItemId and GetItem, to return the number of gallery items in our list. These override functions are exactly the same as the overrides in Java for any BaseAdapter inherited class. Now for the GetView function:

public override View GetView(int position, View convertView, ViewGroup parent) 
        { 
            View view = convertView; // re-use an existing view, if one is available 
 
            if (view == null) 
            { 
                // otherwise create a new one 
                view = context.LayoutInflater.Inflate(Resource.Layout.CustomCell, null); 
            } 
 
            // set image 
            var imageView = view.FindViewById<ImageView> (Resource.Id.image); 
            BitmapHelpers.CreateBitmap (imageView, _items [position].ImageData); 
 
            // set labels 
            var titleTextView = view.FindViewById<TextView> (Resource.Id.title); 
            titleTextView.Text = _items[position].Title; 
            var dateTextView = view.FindViewById<TextView> (Resource.Id.date); 
            dateTextView.Text = _items[position].Date; 
 
            return view; 
        } 
 
        private async void createBitmap(ImageView imageView, byte[] imageData) 
        { 
            try 
            { 
                if (imageData != null)  
                { 
                    var bm = await BitmapFactory.DecodeByteArrayAsync(imageData, 0, imageData.Length); 
                    if (bm != null)  
                    { 
                        imageView.SetImageBitmap(bm); 
                    } 
                } 
            } 
            catch (Exception e)  
            { 
                Console.WriteLine ("Bitmap creation failed: " + e); 
            } 
        } 

Notice in the GetView function we are using the CustomCell layout for each row; we also have a private method for creating our bitmaps from each model's byte array.

If we have a look at the current implementation, what do we notice here?

We are creating a bitmap every time the cell requires this data again for the view; is this efficient? No, we should be reusing bitmaps and memory as much as possible.

This tends to be a common issue with Android ListView.

What is the most memory efficient way to reuse bitmaps across hundreds of items in a ListView while scrolling and staying smooth as we move down the list at various speeds? How can we tackle this problem? Let's have a look at how we can approach this problem.

Firstly, we need to implement an object called ImageHandler. This will contain the logic for retrieving byte arrays from all gallery images on an Android device. Create a new file, name it ImageHandler, and start importing these namespaces:

namespace Gallery.Droid 
{ 
    using System; 
    using System.Collections.Generic; 
 
    using Android.Database; 
    using Android.Content; 
    using Android.Provider; 
 
    using Gallery.Shared; 
 
    public static class ImageHandler 
    { 
    } 
} 

This class will include a function, GetFiles, which will create gallery items based upon the items pulled from any device's gallery using the ContentResolver interface:

public static IEnumerable<GalleryItem> GetFiles(Context context) 
        { 
            ContentResolver cr = context.ContentResolver; 
 
            string[] columns = new string[]  
            { 
                MediaStore.Images.ImageColumns.Id, 
                MediaStore.Images.ImageColumns.Title, 
                MediaStore.Images.ImageColumns.Data, 
                MediaStore.Images.ImageColumns.DateAdded, 
                MediaStore.Images.ImageColumns.MimeType, 
                MediaStore.Images.ImageColumns.Size, 
            }; 
             
            var cursor = cr.Query(MediaStore.Images.Media.ExternalContentUri, columns, null, null, null); 
 
            int columnIndex = cursor.GetColumnIndex(columns[2]); 
 
            int index = 0; 
 
            // create max 100 items 
            while (cursor.MoveToNext () && index < 100)  
            { 
                index++; 
 
                var url = cursor.GetString(columnIndex); 
 
                var imageData = createCompressedImageDataFromBitmap (url); 
 
                yield return new GalleryItem ()  
                { 
                    Title = cursor.GetString(1), 
                    Date = cursor.GetString(3), 
                    ImageData = imageData, 
                    ImageUri = url, 
                }; 
            } 
        } 

Using ContentResolver (used to access the content model), we resolve URIs to specific content providers. A content provider provides queries to content, in our case image files. We simply create an access query off the main context's ContentResolver instance, and we provide an array of columns for the query to retrieve (for example, file titles, file data, file size, and so on). The first parameter is as follows:

"MediaStore.Images.Media.ExternalContentUri" 

This is used for retrieving the URI to each piece of content returned from the query. Finally, we now have a cursor to iterate through, exactly like an Enumerable, which will loop to the end until there are no more items, and for each iteration we pull the data and URI columns and create a new GalleryItem. You will notice a little trick here with the yield keyword: if we call this function, it will actually return the entire Enumerable from start to finish. Calling the function starts for each-ing over the object; the function is called again until it yields. In the return from calling this function, we get an Enumerable of all the items retrieved from the query as gallery items with image information and local URI.