recyclerviews w/ lots of images sucking up memory and getting out of memory exception?

KenYeeKenYee ✭✭USMember ✭✭

Has anyone else seen this?
I've tried the FFImage and Picasso libraries w/o luck...my Xamarin app keeps sucking up memory if you fling the lists around and it eventually crashes after a few minutes :-(

The viewholders should be freeing the imageviews quickly enough but it doesn't seem like they are :-P

Posts

  • rmaciasrmacias ✭✭✭✭✭ USBeta, University ✭✭✭✭✭

    The viewholders should be freeing the imageviews

    No not necessarily. The ViewHolder pattern recycles your views so that you gain a performance boot so you're not constantly reinflating your views. You'll still have to manage your image memory management. Since you didn't post any code, or a working example that re-produces the issue, I can only offer this link.

    http://developer.xamarin.com/recipes/android/resources/general/load_large_bitmaps_efficiently/

  • KenYeeKenYee ✭✭ USMember ✭✭

    I didn't include code because I thought it was fairly obvious because the design pattern of a recyclerview is well defined unlike the old listview/gridview...here's the view holder (ImageViewAsync for FFImage and ImageView for Picasso)

        public class ViewHolder : RecyclerView.ViewHolder {
            public TextView title { get; set; }
            public ImageViewAsync image { get; set; }
    
            public ViewHolder (View view) : base (view) {
            title = view.FindViewById<TextView>(Resource.Id.item_title);
            image = view.FindViewById<ImageViewAsync>(Resource.Id.item_background);
            }
        }
    

    Then in the onbind:
    public override void OnBindViewHolder(ViewHolder viewHolder, int position) {
    viewHolder.title.SetText(itemData[position].Title);

            // if using ffimage:
            viewHolder.image.Cancel_Loading();
            ImageService.LoadUrl(itemData[position]..ImageUrl).DownSample(width: 512).Into(viewHolder.image);
    
            // if using Picasso
            Picasso.With(viewHolder.image.Context)
                        .Load(itemData[position]..ImageUrl)
                        .Fit()
                        .CenterCrop()
                        .Into(viewHolder.image);
        }
    

    Notice I'm downsizing the images to save memory already.
    Memory usage keeps rising when you fling through the list however. With LargeHeap enabled, the app will go over 300MB and then get killed by the OS. The profiler is useless in indicating what is leaking...just mentions byte[] objects and no useless call stack. I suspect it's a leak from Xamarin holding Java imageview refs but there's no way to do a Dispose call on the view holder imageviews AFAIK.

  • KenYeeKenYee ✭✭ USMember ✭✭
    edited August 2015

    here's an example of the memory usage in xamarin profiler when using FFImage (the snapshot deltas are smaller w/ Picasso because the work is done on the Java side)...the initial snapshot is at around 100MB app memory. The memory diffs don't add up to the total app memory change up top so it seems like there are leaks on the Java side...

  • FabienMolinetFabienMolinet ✭✭ FRMember ✭✭

    @KenYee did you solve this in the end?

  • KenYeeKenYee ✭✭ USMember ✭✭

    FYI, we finally tracked this down...was because we used a lot of delegates but never unregistered them.
    Some of the delegates held references to Activity objects so they were never released. duhh.
    Seems obvious now. There should be a big warning when using EventHandlers in C#...remember to unregister them! :-)

  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    @KenYee I am facing the same problem using Picasso and trying to load all the photo gallery.
    If I open the activity which contains the gridview I use to show the library multiple times I received a out of memory exception, and obiouvsly I see the heap increasing without decreasing.

    I only have one event on the gridview

    mGridGallery.ItemClick += OnItemClicked;

    which I removed at onDestroy

    mGridGallery.ItemClick += OnItemClicked;

    the only solution I have found that keep the heap under control is to call

    GC.Collect();

    at onDestroy()

    but the heap is still very high, and I don't think it is a good idea to force the Garbage collector.

    I have tried different libraries (UniversalImage loader, etc) but the problems is the same.

    It seems that Xamarin doesn't manage image in a good way.

    Ps notice that my photo gallery contains at about 5000 photos, but shouldn't be a problem, let's think about instagram, facebook ...

  • KenYeeKenYee ✭✭ USMember ✭✭

    @Luca: show your Picasso code. Make sure you tell it to fit the image into the imageview so the cached images are smaller. I don't think it's that bad that you need to call GC.collect() in the onDestroy of your viewholders...that'd crazy :-(

  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    @KenYee: this my Picasso code, i use in the getView method of my custom adapter:

       Picasso.With(ApplicationContext)
                                .Load("file://"+ path)
                                .Placeholder(Resource.Drawable.placeholder)
                                .Error(Resource.Drawable.ic_empty)
                                .Resize(100,100).CenterCrop()
                                .Tag(ApplicationContext)
                                .Into(ViewHolder.ImgQueue ); 
    

    I use resize because I am showing griditem of 100x100dpi

    and on the GridActivity I do this

    protected override void OnDestroy()
            {
                mGridGallery.ItemClick -= OnItemClicked;
                mGridGallery.ScrollStateChanged -= ScrollStateChanged;
                Picasso.With(ApplicationContext).CancelTag(MyApplication.GetContext(ApplicationContext);
                GC.Collect();
                base.OnDestroy();
    
            }
    

    in this way it seems working even if the heap doesn't go down as at first opening, something remains in memory..but at least it seems that out of memory exception is avoided..

    do you have any suggestions ?

  • KenYeeKenYee ✭✭ USMember ✭✭

    That should be enough...Picasso should see that the imageview is no longer visible after you scroll around and autocancel/destroy everything.
    Odd that you're having issues w/ excessive memory usage still...if you have to GC.Collect on the Xamarin side, it's almost like Xamarin is holding references longer than it should which is something I suspected w/ my recyclerview...

  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    Yes I think you are right, also because I never faced this problem with native android development...
    In my implementation I am using a GridView with a custom adapter ..I use the ViewHolder paradigm ..but I don't use RecyclerView but I don't think the problem is here. Probably Xamarin keeps some references in memory more than necessary

  • FabienMolinetFabienMolinet ✭✭ FRMember ✭✭

    Did you give a try to FFImageLoading? https://github.com/molinch/FFImageLoading
    We just rewrote a large part of the image cache/bitmap pool so it limits allocations, this part is based on TangoAndCache.

  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    Hi @FabienMolinet I gave a very quick try to that library but it seems to be less performant than Picasso.
    But on Monday I can try in a deeper way and I let you know..have you ever tried to use it to retrive all the photo gallery from phone ?

  • FabienMolinetFabienMolinet ✭✭ FRMember ✭✭

    Latest release, https://goo.gl/O3LblY, should definitely help to achieve higher performances. I'm happy to help if you have any issue.

    Regarding photo library currently there is no helper to do that. You can use these image sources: https://goo.gl/mGpfSV on Android.

  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    Hi @FabienMolinet as I promised to to you I try your library to retrive the photo gallery.
    I made this simple changes:
    in RecyclerActivity.cs (Simple.android.Sample):

    //apter.Items = Config.Images;
    adapter.Items = (List<string>) GalleryPhotos;
    

    where GalleryPhotos retrive the phone gallery

    private IEnumerable<string> GalleryPhotos
            {
                get
                {
                    List<string> galleryList = new List<string>();
    
                    try
                    {
                        string[] columns = 
                            new string[]
                            { 
                                MediaStore.Images.ImageColumns.Data, 
                                MediaStore.Images.ImageColumns.Id 
                            };
    
                        string orderBy = MediaStore.Images.ImageColumns.Id;
    
    
    
    
                        Android.Database.ICursor imagecursor = ContentResolver.Query(  MediaStore.Images.Media.ExternalContentUri,  columns,
                            null,
                            null, 
                            orderBy);
    
                        if (imagecursor != null && imagecursor.Count > 0)
                        {
    
    
                            while (imagecursor.MoveToNext())
                            {
    
    
                                int dataColumnIndex = imagecursor.GetColumnIndex(MediaStore.Images.ImageColumns.Data);
    
                               string item = imagecursor.GetString(dataColumnIndex);
    
                                galleryList.Add(item);
                            }
    
                        }
                    }
                    catch (Exception e)
                    {
                        throw;
                    }
    
                    // show newest photo at beginning of the list
                    galleryList.Reverse();
                    // galleryList = galleryList.Take(30).ToList(); take only last photos
                    return galleryList;
                }
            }
    

    and obiuvsly in the adapter

     ImageService.Loadl(item) //instead of LoadUrl
    
                   .DownSample(100, 100)
                   .LoadingPlaceholder(Config.LoadingPlaceholderPath, FFImageLoading.Work.ImageSource.ApplicationBundle)
                   .ErrorPlaceholder(Config.ErrorPlaceholderPath, FFImageLoading.Work.ImageSource.ApplicationBundle)
                   .Into(vh.Image);
    

    So performance are quite slower than Picasso: I can see image with an higher delay.
    Memory management seems better because in the Log Cat I only see this kind of messages

    12-21 10:31:08.752: I/dalvikvm-heap(2014): Grow heap (frag case) to 50.997MB for 36864016-byte allocation

    With Picasso I usually reach 70/80 MB

    but after scrolling the listview for a while I get the following error, and app crashes

    mono-rt] Stacktrace:
    [mono-rt] 
    [mono-rt]   at <unknown> <0xffffffff>
    [mono-rt]   at (wrapper managed-to-native) object.wrapper_native_0x417e3c6d (intptr,intptr,intptr,Android.Runtime.JValue*) <0xffffffff>
    [mono-rt]   at (wrapper delegate-invoke) <Module>.invoke_intptr_intptr_intptr_intptr_JValue* (intptr,intptr,intptr,Android.Runtime.JValue*) <0xffffffff>
    [mono-rt]   at Android.Runtime.JNIEnv.CallStaticObjectMethod (intptr,intptr,Android.Runtime.JValue*) <0x00097>
    [mono-rt]   at Android.Graphics.BitmapFactory.DecodeStream (System.IO.Stream,Android.Graphics.Rect,Android.Graphics.BitmapFactory/Options) <0x001eb>
    [mono-rt]   at FFImageLoading.Work.ImageLoaderTask/<GetDrawableAsync>c__async3/<GetDrawableAsync>c__AnonStoreyE/<GetDrawableAsync>c__asyncD.MoveNext () <0x00e6b>
    [mono-rt]   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<FFImageLoading.Work.ImageLoaderTask/<GetDrawableAsync>c__async3/<GetDrawableAsync>c__AnonStoreyE/<GetDrawableAsync>c__asyncD> (FFImageLoading.Work.ImageLoaderTask/<GetDrawableAsync>c__async3/<GetDrawableAsync>c__AnonStoreyE/<GetDrawableAsync>c__asyncD&) <0x000ef>
    [libc] heap corruption detected by tmalloc_large
    [libc] Fatal signal 6 (SIGABRT) at 0x00006cf9 (code=-6), thread 27943 (eLoading.Sample)
    

    sometimes I also receive this other error

    [skia] GFXPNG PNG bitmap created width:135 height:240 bitmap id is 368 
    [BitmapFactory] bitmap marked for reuse (64800 bytes) can't fit new bitmap (129600 bytes)
    [skia] --- decoder->decode returned false
    [libc] heap corruption detected by dlmalloc
    [libc] Fatal signal 6 (SIGABRT) at 0x000070b9 (code=-6), thread 28908 (eLoading.Sample)
    

    Hope this can help you to improve the library, let me know if I can help.

  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    Hi @FabienMolinet as I promised to to you I try your library to retrive the photo gallery.
    I made this simple changes:
    in RecyclerActivity.cs (Simple.android.Sample):

    //apter.Items = Config.Images;
    adapter.Items = (List<string>) GalleryPhotos;
    
  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    where GalleryPhotos retrive the phone gallery

    private IEnumerable<string> GalleryPhotos
            {
                get
                {
                    List<string> galleryList = new List<string>();
                    try
                    {
                        string[] columns = 
                            new string[]
                            { 
                                MediaStore.Images.ImageColumns.Data, 
                                MediaStore.Images.ImageColumns.Id 
                            };
                        string orderBy = MediaStore.Images.ImageColumns.Id;
                        Android.Database.ICursor imagecursor = ContentResolver.Query(  MediaStore.Images.Media.ExternalContentUri,  columns,
                            null,
                            null, 
                            orderBy);
                        if (imagecursor != null && imagecursor.Count > 0)
                        {
                            while (imagecursor.MoveToNext())
                            {
                                int dataColumnIndex = imagecursor.GetColumnIndex(MediaStore.Images.ImageColumns.Data);    
                               string item = imagecursor.GetString(dataColumnIndex);    
                                galleryList.Add(item);
                            }    
                        }
                    }
                    catch (Exception e)
                    {
                        throw;
                    }    
                    // show newest photo at beginning of the list
                    galleryList.Reverse();
                    // galleryList = galleryList.Take(30).ToList(); take only last photos
                    return galleryList;
                                    }
            }
    
  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    and obiuvsly in the adapter

     ImageService.Loadl(item) //instead of LoadUrl
    
                   .DownSample(100, 100)
                   .LoadingPlaceholder(Config.LoadingPlaceholderPath, FFImageLoading.Work.ImageSource.ApplicationBundle)
                   .ErrorPlaceholder(Config.ErrorPlaceholderPath, FFImageLoading.Work.ImageSource.ApplicationBundle)
                   .Into(vh.Image);
    

    So performance are quite slower than Picasso: I can see image with an higher delay.
    Memory management seems better because in the Log Cat I only see this kind of messages

    12-21 10:31:08.752: I/dalvikvm-heap(2014): Grow heap (frag case) to 50.997MB for 36864016-byte allocation

    With Picasso I usually reach 70/80 MB

  • Luca.7923Luca.7923 ✭✭ USMember ✭✭

    Here log of the crashes..
    sorry for all this posts but I wasn't able to post in a single comment..

  • FabienMolinetFabienMolinet ✭✭ FRMember ✭✭

    @Luca.7923 I propose to follow it here: https://github.com/molinch/FFImageLoading/issues/102
    I created a bug entry

  • DanielLDanielL ✭✭✭✭ PLInsider ✭✭✭✭

    So performance are quite slower than Picasso: I can see image with an higher delay.

    @Luca.7923 Performance will be better soon, see this: https://github.com/molinch/FFImageLoading/issues/97

  • DanielLDanielL ✭✭✭✭ PLInsider ✭✭✭✭

    Performance should be a lot better on all platforms. Bug also fixed. Please try 2.0.1-alpha1.

  • SagarPanwalaSagarPanwala ✭✭✭ USMember ✭✭✭

    @KenYee : Can you able to solve the OOM ? Please give us info about it if you solved it.

  • KenYeeKenYee ✭✭ USMember ✭✭

    our problem was not unsubscribing event delegates like we should have...

  • ParthaSarathyParthaSarathy ✭✭ USMember ✭✭

    Hi
    mGridGallery.ItemClick -= OnItemClicked;
    mGridGallery.ItemClick += OnItemClicked;

    try this it will help you...

  • PandukaWeerasekaraPandukaWeerasekara ✭✭ USMember ✭✭

    Hi,
    Im having issue with growing memory problem , sometimes after few minits the app crashed due to outof memory exception. , bacically i retreve the images from SQl server database and in converts from bytes in a seperate class , my XAML view as below

    <?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="TestProject.Views.DetailViews.JsonDesertPage"
                 xmlns:local ="clr-namespace:TestProject.Data">
        <ContentPage.Resources>
            <ResourceDictionary>
                <local:ByteArrayToImageConverter x:Key="severityTypeImageConvertertwo"/>
            </ResourceDictionary>
        </ContentPage.Resources>
        <ListView x:Name="listviewConactstwo" RowHeight="100" HorizontalOptions="FillAndExpand" HasUnevenRows="True" ItemSelected="listviewContacts_ItemSelected">
            <ActivityIndicator x:Name="ProgressLoadertwo" IsRunning="True"/>
            <ListView.ItemTemplate >
                <DataTemplate>
                    <ViewCell>
                        <StackLayout  Orientation="Vertical" Padding="5">
                            <StackLayout Orientation="Horizontal"  BackgroundColor="Ivory" Opacity="0.9">
                                <Image Source="{Binding Image,Converter={StaticResource severityTypeImageConvertertwo}}" HeightRequest="120" WidthRequest="120"/>
                                <StackLayout Orientation="Vertical">
                                    <Label Text="{Binding Name}" FontSize="Medium" TextColor="Gray" FontAttributes="Bold"/>
                                    <BoxView HeightRequest="2" Margin="0,10,10,0" BackgroundColor="Gray" HorizontalOptions="FillAndExpand" />
                                    <Label Text="{Binding Description}" FontSize="Micro" TextColor="Gray" FontAttributes="Bold"/>
                                    <StackLayout Orientation="Horizontal" VerticalOptions="Start" HorizontalOptions="Start">
                                        <Label Text="$" FontSize="Micro" VerticalOptions="Start" HorizontalOptions="Start" TextColor="Gray" FontAttributes="Bold"/>
                                        <Label Text="{Binding Price}" FontSize="Micro" TextColor="Gray" FontAttributes="Bold"/>
                                    </StackLayout>
                                </StackLayout>
                                <Image Source="arrowtwo.png" BackgroundColor="Transparent" WidthRequest="25" Margin="0,10,10,0"/>
                            </StackLayout>                        
                        </StackLayout>                    
                    </ViewCell>                
                </DataTemplate>            
            </ListView.ItemTemplate>        
        </ListView>
    </ContentPage>
    

    and my code behind as below

    using Newtonsoft.Json;
    using Plugin.Connectivity;
    using System;
    using System.IO;
    using System.Net.Http;
    using TestProject.Data;
    using TestProject.Models;
    using Xamarin.Forms;
    
    namespace TestProject.Views.DetailViews
    {
        public partial class JsonDesertPage : ContentPage
        {
            public JsonDesertPage ()
            {
                InitializeComponent ();   
    
                this.BackgroundImage = "background.png";
                this.Title = "Soup Menu";
                GetJSON();
              //  CrossConnectivity.Current.ConnectivityChanged += Current_ConnectivityChanged;
            }
    
    
            protected async override void OnAppearing()
            {
                base.OnAppearing();
    
                if (!CrossConnectivity.Current.IsConnected)
                {
                    await DisplayAlert("fail", "No Internet Connection.Offline Menu Activated", "Ok");
                    await Navigation.PushAsync(new MainTabbed());
                }
                else
                {
                    // await DisplayAlert("sucess", " Network Is Available.", "Ok");
                    GetJSON();
                }
            }
    
            public async void GetJSON()
            {                
                var client = new HttpClient();
                // var response = await client.GetAsync("http://192.168.43.226/GetContactsDesert.php");
                var response = await client.GetAsync(Constants.BaseUrlpos + "GetContactsDesert.php");    
    
                string contactsJson = response.Content.ReadAsStringAsync().Result;   
                ContectList ObjContactList = new ContectList();
    
                if (response.IsSuccessStatusCode)
                {                    
                    ObjContactList = JsonConvert.DeserializeObject<ContectList>(contactsJson);
                    listviewConactstwo.ItemsSource = ObjContactList.contacts;
                }
    
                else
                {
                    var textReader = new JsonTextReader(new StringReader(contactsJson));
                    dynamic responseJson = new JsonSerializer().Deserialize(textReader);
                    contactsJson = "Deserialized JSON error message: " + responseJson.Message;
                    await DisplayAlert("fail", "no Network Is Available.", "Ok");
                }
    
                ProgressLoadertwo.IsVisible = false;            
    
            }
    
            private void listviewContacts_ItemSelected(object sender, SelectedItemChangedEventArgs e)
            {
                var itemSelectedData = e.SelectedItem as Contactone;
                Navigation.PushAsync(new JsonDetailsPage(itemSelectedData.ID, itemSelectedData.Image, itemSelectedData.Name, itemSelectedData.Code, itemSelectedData.Description, itemSelectedData.Price,itemSelectedData.isservicecharge, itemSelectedData.CostPrice));
    
            }
        }
    }
    

    This is one of the pages i am using for further information i will include the converter as below

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    
    namespace TestProject.Data
    {
        public class ByteArrayToImageConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                ImageSource retSource = null;
                if (value != null)
                {
                    byte[] imageAsBytes = (byte[])value;
                    // byte[] decodedByteArray = System.Convert.FromBase64String(Encoding.UTF8.GetString(imageAsBytes, 0, imageAsBytes.Length));
                    // var stream = new MemoryStream(decodedByteArray);
                    retSource = ImageSource.FromStream(() => new MemoryStream(imageAsBytes));
                }
                return retSource;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                //return null;
                throw new NotImplementedException();
            }
    
        }
    }
    

    when i run the profile i can see the bye[ ] is growing up as i move among pages , i tryed the FFimageloading option and it dosent seems to support for the byte array image converters , can anyone kindly suggest solution or method to get rid of the memory growing issue, thank you in advance for your support,

    Pan

  • DooksDooks ✭✭✭ ZAMember ✭✭✭

    In your recyclerview adapter, you need to override another method and clear all your text, images, etc.

    public override void OnViewRecycled(Java.Lang.Object holder)
    {
        if (holder is MyViewHolder)
        {
            var h = holder as MyViewHolder;
            h.myInnerLayout.Visibility = ViewStates.Gone;
            h.myLayout.SetBackgroundResource(0);
            h.myLabel.Text = null;
            h.myEditText.Text = null;
    
            h.myImage.SetImageResource(0);
    
            //This is also a good time to clear Picasso/Glide/... for the ImageView if you have used it to load an image in the first place
    
            //Picasso.With(h.myImage.Context).Invalidate(h.image_path);
            //Glide.With(h.myImage.Context).Clear(h.myImage);
        }
        base.OnViewRecycled(holder);
    }
    
  • LearnEverythingLearnEverything ✭✭✭ USMember ✭✭✭
    edited August 13

    Reusable recycleview adapters for all types of datamodels

     public class GenericRecyclerViewAdapter<T> : RecyclerView.Adapter
        {
            /// <summary>
            /// You can set this for different custom cardview
            /// </summary>
            public int CardViewResourceId { get; set; }
            public ObservableCollection<T> Items { get; private set; }
    
            public event EventHandler<RecyclerViewViewHolder> ItemViewTemplated;
            public GenericRecyclerViewAdapter(IEnumerable<T> items, int cardViewResourceId)
            {
                this.Items = new ObservableCollection<T>(items);
                this.CardViewResourceId = cardViewResourceId;
                this.Items.CollectionChanged += (sender, e) =>
                {
                    this.NotifyDataSetChanged();
                };
            }
    
            public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
            {
                View itemView = LayoutInflater.From(parent.Context).Inflate(CardViewResourceId, parent, false);
                RecyclerViewViewHolder vh = new RecyclerViewViewHolder(itemView);
                return vh;
            }
    
            public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
            {
                RecyclerViewViewHolder vh = holder as RecyclerViewViewHolder;
                vh.ItemPosition = position;
                this.ItemViewTemplated?.Invoke(this, vh);
            }
    
            public override int ItemCount
            {
                get { return this.Items.Count; }
            }
        }
    
        public class RecyclerViewViewHolder : RecyclerView.ViewHolder
        {
            public View TemplateView { get; private set; }
            public int ItemPosition { get;  set; }
    
            public RecyclerViewViewHolder(View itemView) : base(itemView)
            {
                // Locate and cache view references:
                this.TemplateView = itemView;
            }
        }
    

    Assign event

    GenericRecyclerViewAdapter<IWordModel> mAdapter = new GenericRecyclerViewAdapter<IWordModel>(this.Result.DefinitionList, Resource.Layout.custom_detail_cardview);
                mAdapter.ItemViewTemplated += (sender, holder) =>
                  {
                      if(mAdapter.CardViewResourceId == Resource.Layout.custom_detail_cardview)
                      {
                          //wordtype
                          var wordView = holder.TemplateView.FindViewById<TextView>(Resource.Id.wordtype);
                          if (!string.IsNullOrEmpty(this.Result.DefinitionList[holder.ItemPosition].WordType))
                              wordView.Text = this.Result.DefinitionList[holder.ItemPosition].WordType;
                          else
                              wordView.Visibility = ViewStates.Gone;
    
                      }                  
                  };
                mRecyclerView.SetAdapter(mAdapter);     
    
Sign In or Register to comment.