Saturday, July 19, 2014

ListView Using ActivityListItem Style with URLImageViewHelper to Retrieve Web Images


    This post will demonstrate a modified version of the previous ListView post that will download the images from a web URL for each item rather than using local images from the application's Asset folder.

    Be sure to enable "Internet" under the project application options.  If you run a test on the emulator without checking this option it will work, but it will fail when deployed to an actual device.

    We'll also use the URLImageViewHelper extension which can be downloaded from GitHub and included in the project:  https://github.com/Redth/MonoDroid.UrlImageViewHelper

    URLImageViewHelper will facilitate using asynchronous downloads of the images so that the UI is not blocked waiting for an image to be downloaded.  When the image becomes available, it will be added to the existing row.  URLImageViewHelper will also cache the images so they don't need to be downloaded again if they are re-used.




    The helper method in the data adapter will be changed in this example to return a web URL for the images rather than a path to the local assets.


            private List<People> PopulatePeopleData() {
                // this is just done in this demo for simplicity to create the People objects
                return new List<People> ()
                {
                    new People { FirstName = "Anita", LastName = "Drink" , ImgURL = "http://www.atavisticsoftware.com/demo/Anita.jpg"},
                    new People { FirstName = "Amanda", LastName = "Reconwith", ImgURL = "http://www.atavisticsoftware.com/demo/Amanda.jpg" },
                    new People { FirstName = "Warren", LastName = "Peace", ImgURL = "http://www.atavisticsoftware.com/demo/Warren.jpg" },
                    new People { FirstName = "Rhonda", LastName = "Corner" , ImgURL = "http://www.atavisticsoftware.com/demo/Rhonda.jpg"},
                    new People { FirstName = "Karen", LastName = "Feeding" , ImgURL = "http://www.atavisticsoftware.com/demo/Karen.jpg"},
                    new People { FirstName = "Rufus Lee", LastName = "King", ImgURL = "http://www.atavisticsoftware.com/demo/Rufus.jpg" },
                    new People { FirstName = "Winston", LastName = "Payne" , ImgURL = "http://www.atavisticsoftware.com/demo/Winston.jpg"},
                    new People { FirstName = "Marian", LastName = "Haste" , ImgURL = "http://www.atavisticsoftware.com/demo/Marian.jpg"},
                    new People { FirstName = "Augusta", LastName = "Wind", ImgURL = "http://www.atavisticsoftware.com/demo/Augusta.jpg" },
                    new People { FirstName = "Ben", LastName = "Dover", ImgURL = "http://www.atavisticsoftware.com/demo/Ben.jpg" },
                    new People { FirstName = "Dan", LastName = "Druff", ImgURL = "http://www.atavisticsoftware.com/demo/Dan.jpg" },
                    new People { FirstName = "Dick", LastName = "Tator", ImgURL = "http://www.atavisticsoftware.com/demo/Dick.jpg" },
                    new People { FirstName = "Marty", LastName = "Graw", ImgURL = "http://www.atavisticsoftware.com/demo/Marty.jpg" },
                    new People { FirstName = "Lauren", LastName = "Order", ImgURL = "http://www.atavisticsoftware.com/demo/Lauren.jpg" },
                    new People { FirstName = "Les", LastName = "More", ImgURL = "http://www.atavisticsoftware.com/demo/Les.jpg" },
                    new People { FirstName = "Molly", LastName = "Keull", ImgURL = "http://www.atavisticsoftware.com/demo/Molly.jpg" },
                    new People { FirstName = "Rhoda", LastName = "Booke", ImgURL = "http://www.atavisticsoftware.com/demo/Rhoda.jpg" },
                    new People { FirstName = "Rich", LastName = "Feller", ImgURL = "http://www.atavisticsoftware.com/demo/Rich.jpg" },
                    new People { FirstName = "Holly", LastName = "Day", ImgURL = "http://www.atavisticsoftware.com/demo/Holly.jpg" },
                    new People { FirstName = "Polly", LastName = "Ester", ImgURL = "http://www.atavisticsoftware.com/demo/Polly.jpg" },
                    new People { FirstName = "Paige", LastName = "Turner", ImgURL = "http://www.atavisticsoftware.com/demo/Paige.jpg" },
                    new People { FirstName = "Neil", LastName = "Down", ImgURL = "http://www.atavisticsoftware.com/demo/Neil.jpg" },
                    new People { FirstName = "Jack", LastName = "Pott", ImgURL = "http://www.atavisticsoftware.com/demo/Jack.jpg" },
                    new People { FirstName = "Miles", LastName = "Long", ImgURL = "http://www.atavisticsoftware.com/demo/Miles.jpg" },
                    new People { FirstName = "Joy", LastName = "Kil", ImgURL = "http://www.atavisticsoftware.com/demo/Joy.jpg" }
                };

            }


    We'll modify the GetView method in the data adapter class PeopleAdapter from the previous post example to use the extension method from URLImageViewHelper to load the ImageView asynchronously using the SetUrlDrawable method.  For each row, the Resource.Drawable.photo placeholder will be displayed until the people.ImgURL is available from the web.  The UI won't be blocked waiting for each download to complete.

                ImageView img = view.FindViewById<ImageView> (Android.Resource.Id.Icon);
                // In this example, we won't use the .SetImageDrawable method, but instead switch to the extension method from URLImageViewHelper
                // The Resource.Drawable.photo will be displayed temporarily until the actual image is finished downloading

                //    img.SetImageDrawable(GetImage.GetImageFromURL(people.ImgURL, context));
                img.SetUrlDrawable(people.ImgURL, Resource.Drawable.photo);
                return view;  // return the formated view




    The placeholder image is in the Resources | drawable folder.



    The placeholder image will appear briefly until the actual images are available. 
    The real images will "pop in" once they are downloaded in a background process.


    URLImageViewHelper adds extension methods to the ImageView class.  There are several variations that can be used to handle image processing.   

    In general, an extension method can be added to existing methods by creating a static class and method and then using the this keyword before the type that is being extended.   Additional parameters can be added at the end.  To use the extension methods, make sure to include them with the appropriate using statement, in this case using UrlImageViewHelper;  The extended methods will then appear in the intellisense options for the extended type (in this case, ImageView).  For more information on creating an extension method see http://www.dotnetperls.com/extension.


    namespace UrlImageViewHelper
    {
        public static class ExtensionMethods 
        {
            public static void SetUrlDrawable(this ImageView imageView, string url, int defaultResource)
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultResource);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url)
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable)
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url, int defaultResource, long cacheDurationMs)
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultResource, cacheDurationMs);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable, long cacheDurationMs)
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable, cacheDurationMs);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url, IUrlImageViewCallback callback) 
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, callback);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable, IUrlImageViewCallback callback) 
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable, callback);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url, int defaultResource, long cacheDurationMs, IUrlImageViewCallback callback) 
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultResource, cacheDurationMs, callback);
            }

            public static void SetUrlDrawable(this ImageView imageView, string url, Drawable defaultDrawable, long cacheDurationMs, IUrlImageViewCallback callback) 
            {
                UrlImageViewHelper.SetUrlDrawable(imageView, url, defaultDrawable, cacheDurationMs, callback);
            }
        }
    }






    As before, when an individual row is clicked, a new activity will be launched to display the larger image and name.  In this version, the code has been changed to also use the SetUrlDrawable
    method for the asynchronous download as well.

        [Activity (Label = "NewActivity",Theme = "@android:style/Theme.NoTitleBar")]            
        public class NewActivity : Activity
        {
            protected override void OnCreate (Bundle bundle)
            {
                base.OnCreate (bundle);

                SetContentView (Resource.Layout.People);

                var pList = Intent.Extras.GetStringArrayList ("People") ?? new string[0];

                TextView fn = FindViewById<TextView> (Resource.Id.FirstName);
                TextView ln = FindViewById<TextView> (Resource.Id.LastName);
                fn.Text = pList [0];
                ln.Text = pList [1];
                string pic = pList [2];

                ImageView img = FindViewById<ImageView> (Resource.Id.Picture);
    //            img.SetImageDrawable(GetImage.GetImageFromURL(pic, this));
                img.SetUrlDrawable(pic, Resource.Drawable.photo);


            }
        }