[Xamarin Blog] Tips & Tricks for Highly Performing Android ListViews

JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai
edited April 2014 in Xamarin.Android

Just posted: Tips & Tricks for Highly Performing Android ListViews on the Xamarin Blog.

Read the full article to learn a few optimizations to ensure you are getting the optimal performance in your ListView and GridView Adapters.

Use this thread to discuss or ask questions.

Posts

  • Hi, I was wondering if MVVM cross does these optimisations too, when using binding.

  • CheesebaronCheesebaron DKInsider, University mod

    MvvmCross does not use the ViewHolder stuff, but it does recycle the views using the convert view. Why do you ask?

  • If they would use the ViewHolder pattern, it would mean that I shouldn't write adapters any more.
    Depending on the complexity of a rowview, this is a big difference in performance.

  • CheesebaronCheesebaron DKInsider, University mod

    You don't have to write your own adapters in MvvmCross. You just bind an ItemsSource to a ListView. I suggest you go look at it some more ;)

  • Right! But what I can do if I have more than one type o view?

    Depending of my type of information I want to inflate a different layout.

    In this case how I know what layout was used and how I know that the convertView have the layout correct to me reuse or create again?

    if a have a sequence in the listView

    view with layout 1
    view with layout 2
    view with layout 1

    and the result of the convertView is not null how I know what the layout was used in the recycled view?

  • CheesebaronCheesebaron DKInsider, University mod

    Either way you will need to have some kind of map or some index that tells at what position you have what type.

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai

    @Romao that is coming in part two, but basically what @Cheesebaron has said. It is based on index and type. It is very similar though.

  • What I'm saying is, if MvvmCross will findbyid on every view for complex row views, I'll have to write the adapter myself and not use the MvvmCross way.

  • CheesebaronCheesebaron DKInsider, University mod

    @SNiels you are still not making sense :D

  • Cool writeup. Just as a follow on from this article, I just had my Cheeseknife component approved in the Xamarin Component Store which has an example ViewHolder pattern but using Cheeseknife to do the view injection. There is a sample app in the component that you can rip the adapter class from if you want - even if you don't want to use Cheeseknife itself.

  • can you share open source sample

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai

    @mustafahalukyilmaz‌ I don't have more source then what is in the post. However I do have an example in one of my apps:

    https://github.com/jamesmontemagno/My-StepCounter/blob/master/StepCounter.Android/Adapters/HistoryItemAdapter.cs

  • anton.5994anton.5994 ESMember
    edited May 2014

    Hi @Romao‌, maybe this link can help you:
    https://github.com/MvvmCross/MvvmCross-Tutorials/blob/master/Working With Collections/Collections.Droid/Views/PolymorphicListItemTypesView.cs

    @SNiels‌, I'm trying to understand your question. @JamesMontemagno‌ describes two ways to optimice the row creation.

    • Trying to use a previous created row (Mvvmcross do the same task)

    • Avoiding to call FindViewById every time you have to fill a row with values ( With Mvvmcross you don't have to find any component because the binding mechanism do it for you. You have to define the binding properties in the row's xml file.)

  • I wonder how ViewHolder interfere with GC/JNI overflow ... so many Java objects...

  • CheesebaronCheesebaron DKInsider, University mod

    You would only get a ViewHolder instance for how many visible items you have in the ListView. These are reused, so i.e. if 8 items are visible then you have 8 instances of a ViewHolder.

  • RicardoRomoSoaresRicardoRomoSoares BRBeta ✭✭
    edited May 2014
  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai
  • DevinRoseDevinRose USMember ✭✭

    @JamesMontemagno‌, for Xamarin Forms, does Xamarin perform the ViewHolder optimization under the hood? I am curious because I am hitting an out of memory exception on Android with a listview that has an image in it. If I remove the image from the listview (and just have a label, for instance), I don't hit the out of memory exception.

    If Xamarin Forms does not do this optimization, I wonder if it is possible for me to create a custom renderer (adapter?) for the Android listview and do this optimization. Perhaps it would solve the out of memory exception. Thoughts?

  • CheesebaronCheesebaron DKInsider, University mod

    How exactly are you working with the image in your ListView? Care to show a sample, you might not be releasing the image ever which might be why it goes boom.

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai

    @Cheesebaron‌ Look like @DevinRose‌ is talking a bit more about Xamarin.Forms. I believe they do cell recycling as well. I haven't seen too many issues with images. I will ask them. What specific error is in the output?

  • DevinRoseDevinRose USMember ✭✭

    Hi guys,

    Yes this is Xamarin Forms, and it does look like it recycles cells. I went ahead and filed a bug to Xamarin Forms on it. The issue also may be device specific (it crashes on my Samsung Galaxy S5). https://bugzilla.xamarin.com/show_bug.cgi?id=22510

    Output:

    [skia] GFXPNG PNG bitmap created width:1280 height:337 bitmap id is 774 [dalvikvm-heap] Clamp target GC heap from 137.483MB to 128.000MB [dalvikvm-heap] Forcing collection of SoftReferences for 6901776-byte allocation [dalvikvm-heap] Clamp target GC heap from 137.483MB to 128.000MB [dalvikvm-heap] Out of memory on a 6901776-byte allocation. [skia] --- allocation failed for scaled bitmap [skia] GFXPNG PNG bitmap created width:166 height:166 bitmap id is 775 [Mono] GC_OLD_BRIDGE num-objects 512 num_hash_entries 27784 sccs size 19873 init 0.00ms df1 52.23ms sort 9.64ms dfs2 28.44ms setup-cb 1.98ms free-data 21.61ms links 48935/48935/144427/26 dfs passes 77231/68808 [Mono] GC_MINOR: (Nursery full) pause 70.05ms, total 70.25ms, bridge 90.70ms promoted 656K major 2672K los 493K

  • Hello
    I'm using this tutorial but i have a very strange problem.I have a checkbox for my Item layout and when I start my program it shows everything in place but when i check a row's checkbox and then scroll the page down i see that a row's checkbox at them bottom(one or two above the bottom) is checked too.when i uncheck that the row above become uncheck too.somehow they are synch.can someone help me figure this out? this is my adapte:

    `class HumberAdapter : BaseAdapter
    {
    public List _HumberList;
    private Activity _context;

        public HumberAdapter(Activity context,List<Humberger> hlist )
        {
            _context = context;
            _HumberList = hlist;
        }
        private class MyViewHolder : Java.Lang.Object
        {
            public TextView HName { get; set; }
            public CheckBox Hchb { get; set; }
            public RadioButton Hrb { get; set; }
            public TextView HPrice { get; set; }
            public TextView HPName { get; set; }
            public Spinner Hspinner { get; set; }
        }
        public override Object GetItem(int position)
        {
    
            throw new NotImplementedException();
        }
    
        public override long GetItemId(int position)
        {
            throw new NotImplementedException();
        }
    
        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            MyViewHolder holder=null;
            var view = convertView;
            if (view != null)
                holder = view.Tag as MyViewHolder;
            if (holder == null)
            {
                holder = new MyViewHolder();
                view = _context.LayoutInflater.Inflate(Resource.Layout.HumberItemLayout, null);
                holder.HName = view.FindViewById<TextView>(Resource.Id.txt_humbername);
                holder.Hchb = view.FindViewById<CheckBox>(Resource.Id.chbhumber);
                holder.Hrb = view.FindViewById<RadioButton>(Resource.Id.rb_humber);
                holder.HPrice = view.FindViewById<TextView>(Resource.Id.txt_hprice);
                holder.HPName = view.FindViewById<TextView>(Resource.Id.txt_tomansuffix);
                holder.Hspinner = view.FindViewById<Spinner>(Resource.Id.spinnerhumber);
                view.Tag = holder;
            }
    
            holder.HName.Text = _HumberList[position].Name;
            holder.HPrice.Text = _HumberList[position].Price.ToString();
            var adapter = ArrayAdapter.CreateFromResource(
                  _context, Resource.Array.Numbers, Android.Resource.Layout.SimpleSpinnerItem);
            adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
            holder.Hspinner.Adapter = adapter;
            return view;
        }
    
        public override int Count
        {
            get { return _HumberList.Count; }
        }
    }`
    
  • BFipsBFips PTMember
    edited September 2014

    @saeedek2002‌

    I had a similar problem with checkboxes and ListViews. I can´t reproduce the error again now, but I think this is what did the trick for me. In my GetView I handle the checkbox this way:

    CheckBox checkBox = view.FindViewById<CheckBox> (Resource.Id.checkBox);
    checkBox.Checked = item.Chk;
    checkBox.CheckedChange += delegate(object sender, CompoundButton.CheckedChangeEventArgs e) {
            item.Chk = checkBox.Checked;
    }
    

    In your example, I think it woud be something like this (sorry if I'm wrong):

    holder.Hchb = _HumberList[position].Hchb;
    holder.Hchb.CheckedChange += delegate(object sender, CompoundButton.CheckedChangeEventArgs e) {
             _HumberList[position].Hchb; = holder.Hchb;
    }
    

    Tell me if it works.

    On a related topic, @JamesMontemagno‌ I also have a ListView (ExpandableListView actually) in which items seem to be sync too. This is only visible when deleting, i.e., when I remove some item from the list, others are removed too.
    I found that I can prevent this from happening if I don´t reuse views in the GetChildView function (the delete button is in the child). But I know this is not the best approach. Have you ever seen this situation, or have any idea about it?

    Thanks in advance.

    PS: For each parent I only have one child. Does that make any difference?

  • Mehdi.AMehdi.A USMember

    @Cheesebaron‌ About @SNiels‌ question, I'm wondering in a long infinite listview, shall we write our custom adapter to use view holder pattern for better performance or MvxListView doesn't require it?

  • AaronLiangAaronLiang USMember

    Hi, everyone

    I have a memory overflow problem with using this tutorial and GridView. I tried to get every pictures in my device. I first obtain all paths to all the photos and store them into photoList.
    When getting bitmap of the picture in method decodeSampledBitmapFromUri, I will first check if there is thumbnail for the photo in current path. If there is thumbnail, use the thumbnail. Otherwise, use the actually and make it fit the GridView.

    But no matter what I do, I can't avoid memory overflow. Seems like when I scrolled down, the previous views that disappeared are still occupying the memory. Can anyone give me a hint about how I solve this problem?

    public override View GetView (int position, View convertView, ViewGroup parent)
    {
    viewholder holder = null;
    var view = convertView;
    int widthAndHeight = ((int)context.Resources.DisplayMetrics.WidthPixels / 3);
    if (view != null)
    holder = view.Tag as viewholder;

            if (holder == null) {
                holder = new viewholder ();
                view = context.LayoutInflater.Inflate (Resource.Layout.PhotoPickerGrid, parent, false);
                holder.view = view.FindViewById<ImageView> (Resource.Id.GridPhoto);
                view.Tag = holder;
                }
    
            holder.view.SetMinimumWidth (widthAndHeight);
            holder.view.SetMinimumHeight (widthAndHeight);
            holder.view.SetScaleType (ImageView.ScaleType.CenterCrop);
    
            if (photoList.Count != 0) {
            Bitmap bm = decodeSampledBitmapFromUri (photoList [position], widthAndHeight, widthAndHeight);
    
            holder.view.SetImageBitmap (bm);
            bm = null;
                }
    
            return view;
    

    }

        public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
    
            Bitmap bm = null;
    
            BitmapFactory.Options options = new BitmapFactory.Options {
                InJustDecodeBounds = true
            };
    
            options.InSampleSize = 2;//calculateInSampleSize(options, reqWidth, reqHeight);
    
            options.InJustDecodeBounds = false;
    
            ExifInterface exif = new ExifInterface (path);
            byte[] imageData = exif.GetThumbnail ();
            if (imageData != null)
                return BitmapFactory.DecodeByteArray (imageData, 0, imageData.Length);
            else {
                bm = BitmapFactory.DecodeFile(path, options); 
                return bm;
            }
        }
    
  • fufu520fufu520 AWMember

    Although strength of the university or college can be doubtful for a real go, the particular act associated with Snooki talk throughout alone benefitted Rutgers. However the majority is upward throughout arms covering the relative expenses for you to Snooki and the honour winner, in fact which the judgement was a new [b][url=http://www.ottomanorsmahallesi.com/]Aqua 11s for sale[/url][/b] wise personal go.

    This particular full week the particular UN granted a new Zoom above Libya. This particular order was developed so that you can preserve affordable the particular getting rid of regarding tranquil protesters so to steer clear of [b][url=http://www.ottomanorsmahallesi.com/]Cheap Jordan Shoes[/url][/b] needless physical violence. NATO includes stepped upward and also decided [b][url=http://www.ottomanorsmahallesi.com/]Bred 11s Online[/url][/b] to cope with the particular zoom above Libya, seeing that there may be bit of for you to simply no chance regarding missile commences. At the same time, america and [b][url=http://www.ottomanorsmahallesi.com/]Gamma Blue 11s[/url][/b] also Europe have been lifetime get a treatment [b][url=http://www.ottomanorsmahallesi.com/]low Infrared 23 11s[/url][/b] for maintain the brewing war in hand, when together assaulting Libyan floor forces, tanks, and also artillery outside the house Misurata, Ajdabiya and also Zintan.

    Air flow affects have been as well conducted for you to structure regarding verbal exchanges regarding Libyan floor forces so to remove air-strikes with civilians. "Our principles for you to program troops can be basic. halt [b][url=http://www.ottomanorsmahallesi.com/]Infrared 23 11s Cheaper[/url][/b] arguing, halt getting rid of your own personal individuals, halt obeying the particular purchases regarding Colonel Qaddafi, " Admiral Gortney explained. "To their education which you ditch these calls for, you will continue to hit people and also allow it to be near impossible so that you can keep going. ".

  • How can i get the position of the holder for onclick? I try to retrieve it by getting the tag but its return nothing.

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai
  • Chris.3256Chris.3256 USMember, University

    Hi everyone,

    O got a strange problem while using ViewHolder. I have a GridView. Each column contains a Imageview. I have a AsycTask to load image to the imageView. When I scrolled fast, some of the imageView still contain previous Images.

    For example, I am at position 10. The 10th column in GridView has image A. When I scroll fast to , i.e., 15th or 17th column, the image A is there! But after a few seconds, the AsycTask would replace Image A with correct image.

    Can anyone share a thought, idea, or hint with me? Thanks a lot!

  • VolkanAkkusVolkanAkkus TRMember ✭✭

    hi, I have solved the same problem by copying images from .Droid\Resources\drawable folder to other folders; drawable-hdpi etc.

  • @JamesMontemagno Hey James, a question while looking at your ViewHolder.

    How can you be sure that your properties get properly disposed? When you get the reference for

    private class MyViewHolder : Java.Lang.Object 
    {
      public TextView Name { get; set; }
      public TextView Description { get; set; }
      public ImageView Image { get; set; }
    }
    

    by calling FindViewById(someId) - I don't see any of these being disposed anywhere? Does this leak? Especially the ImageView. Cheers

  • SpudSpud ITMember

    Just wanted to say that if you are struggling with learning at the same time C#, Java, Android, and on top of that optimization patterns like ViewHolder pattern, maybe you can just leave it out for now. Unless you have a lot of content to show it will not make a huge difference!
    In my case, since I am a newbie with above technologies, I preferred to not use it, and save the state data I was trying to save on the ViewHolder in the underlying data structure itself. Example I had a button on a listview item that had to be set as disabled, I just saved the state as a bool on the array of items passed to the adapter.

Sign In or Register to comment.