Are you getting out of memory exception when your running your Android app? It’s common that the reason is that a large image is loaded. If it’s the case, take a look at this article at the Xamarin developer portal, http://developer.android.com/training/displaying-bitmaps/load-bitmap.html.
But how to implement it when you’re building your apps with Xamarin.Forms? In this post I will show one solution how to implement it. We will do it with an custom view and a custom renderer.
First we will create the new view that will inherit from the standard Image view, I will name it LargeImage. While we doesn’t want the default behavior we need to create our own source property of type string, I name it ImageSource. While we just want to change the behavior for the Android app and not for Windows and iOS we will set the base Source property in the property changed handler of the ImageSource property if the code not is running on Android.
publicclass LargeImage : Image{publicstaticreadonly BindableProperty ImageSourceProperty = BindableProperty.Create("ImageSource", typeof(string), typeof(LargeImage), default(string), propertyChanged:(bindable, oldValue, newValue)=>{if(Device.OS!= TargetPlatform.Android){var image =(LargeImage)bindable;var baseImage =(Image)bindable; baseImage.Source= image.ImageSource;}});publicstring ImageSource {get{return GetValue(ImageSourceProperty)asstring;}set{ SetValue(ImageSourceProperty, value);}}} |
Next step is to create a renderer for our new view. While we need the default Image behavior except for handling the source the renderer will inherit from ImageRenderer.
This renderer will only work for images in the Drawable folder, so if you have other type of image sources you need to modify the code.
In the renderer we need to handle the ImageSource property, we will do that in the OnPropertyChanged method. While we doesn’t want to run the code before the image has width and height we added a if-statement that check if width and height is greater than zero. But we just want it to run once because of that width and height is greater than zero, because of that i have added a flag that I named _isDecoded. If ImageSource changed the code will run because that e.PropertyName will be ImageSource.
[assembly: ExportRenderer(typeof(LargeImage), typeof(LargeImageRenderer))]namespace SampleApp.Droid.Renderers{publicclass LargeImageRenderer : ImageRenderer {protectedoverridevoid OnElementChanged(ElementChangedEventArgs<Image> e){base.OnElementChanged(e);}privatebool _isDecoded;protectedoverridevoid OnElementPropertyChanged(object sender, PropertyChangedEventArgs e){base.OnElementPropertyChanged(sender, e);var largeImage =(LargeImage)Element;if((Element.Width>0&& Element.Height>0&&!_isDecoded)||(e.PropertyName=="ImageSource"&& largeImage.ImageSource!=null)){ BitmapFactory.Options options =new BitmapFactory.Options(); options.InJustDecodeBounds=true;//Get the resource id for the imagevar field =typeof(Resource.Drawable).GetField(largeImage.ImageSource.Split('.').First());varvalue=(int)field.GetRawConstantValue(); BitmapFactory.DecodeResource(Context.Resources, value,options);//The with and height of the elements (LargeImage) will be used to decode the imagevar width =(int)Element.Width;var height =(int)Element.Height; options.InSampleSize= CalculateInSampleSize(options, width, height); options.InJustDecodeBounds=false;var bitmap = BitmapFactory.DecodeResource(Context.Resources, value, options);//Set the bitmap to the native control Control.SetImageBitmap(bitmap); _isDecoded =true;}}publicint CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){// Raw height and width of imagefloat height = options.OutHeight;float width = options.OutWidth;double inSampleSize = 1D;if(height > reqHeight || width > reqWidth){int halfHeight =(int)(height /2);int halfWidth =(int)(width /2);// Calculate a inSampleSize that is a power of 2 - the decoder will use a value that is a power of two anyway.while((halfHeight / inSampleSize)> reqHeight &&(halfWidth / inSampleSize)> reqWidth){ inSampleSize *=2;}}return(int)inSampleSize;}}} |