anyone here used recycler view in forms? Why isn't it the gridview in xlabs?

GeorgeCookGeorgeCook PEUniversity ✭✭✭

I've been working with xlabs gridview, and finally given up on it, it's just got too many issues, so I want to rewrite their renderer for android from scratch.

RecyclerView to me looks like a far better control to use than GridView.

So 1. Has anyone done this? 2. Can anyone think why the archaic and limited GridView was elected for the GridView renderer in XLabs? (other than the name).

Best Answer

Answers

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    Ok. I'll go ahead and do it then. It's been there since 4.4. I don't know android at all so that could be feasible or not, I mean their latest is adoption is currently 0.8% (from androids website). Iosis100 times higher. Guess I'm spoiled asan iOS guy. ;)

  • NicolasHotterbeekxNicolasHotterbeekx USMember ✭✭✭

    Jups iOS guys are realy spoiled with the UI, but on the other hand, the object-c is hard learn. Luckly they brought out Swift :smile:
    If you put your project on a git repo, I'll be happy to help out in my free time :wink:

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Who needs Objective-C or Swift (or Java)? We're all C# programmers here. :)

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    I was wondering that too.. and plus, I'm fluent in objective c and have been for half a decade... but no amount of C# will get us less android fragmentation. I've never touched Android before, and I've no idea how anyone would want to do it without Xamarin. But then again, I'm an android as an afterthought, or my client needs it kind of guy. I still haven't found an android device I like, an OS that doesn't make me go yuk, or apis that don't make me want to vomit.

    But, as you say, at least I can write it in c#! And with forms, write pretty much no android code HURRAH! :D

  • chris_riesgochris_riesgo USUniversity ✭✭✭

    @GeorgeCook - I'd be happy to look if you can post the code and a little sample repro either up on github or here. Whichever is easier to manage.

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭
    edited June 2015

    I'll put it up with the rest of TwinFormsLib when I get a chance, https://github.com/twintechs/TwinTechsFormsLib so if you're following that (if, not why not? ;) ) then you'll know when it's up. I'll ping here too when I do it.. likely in a few hours. cheers!

  • NicolasHotterbeekxNicolasHotterbeekx USMember ✭✭✭

    @GeorgeCook, did you actualy implement the recyclerview with databinding.
    So I mean, do you have a general RecyclerViewRender to which you can pass a datatemplate and a list of object, and that will update the bindings when recycling the view (in the ViewHolder)

  • JoaquinGrechJoaquinGrech ESMember ✭✭

    @GeorgeCook do you have the code? I decided to also write the renderer and I'm getting a few crashes. I did a quick search and found this post. I see yours was working.

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    Yes it's in the twintechs forms library.

    https://github.com/twintechs/TwinTechsFormsLib

    Yoi might find the iOS blog post about this interesting. I'll be following that up with a blog about how issued recycler view too

    http://blog.twintechs.com/advanced-xamarin-forms-techniques-for-flexible-and-performant-cross-platform-apps-part-3

  • JoaquinGrechJoaquinGrech ESMember ✭✭

    where? I looked all over the GitHub and I couldn't the code for the RecyclerViewRender

  • JoaquinGrechJoaquinGrech ESMember ✭✭

    I found it... my version still crashes for what I see we have more or less the same code but it differs on the ViewHolder/Adapter. I get this exception with a crash:

    08-27 23:06:02.424 I/MonoDroid(10289): java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
    08-27 23:06:02.424 I/MonoDroid(10289): at android.view.ViewGroup.addViewInner(ViewGroup.java:4212)

    My guess I have something wrong with the way I instantiate the ViewHolder: I just want to display an image.

    My code:

    any ideas?

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    I would have to have a working project to debug it. If you share an isolated example I'll take a look over the weekend.

    The forum formatting works fine, by the way. Make sure that you indent ALL of your code by one tab and leave an empty line before it.

  • JoaquinGrechJoaquinGrech ESMember ✭✭

    I fixed it, the issue was to pass to the ViewHolder a new ImageView(parent.Context) instead of whatever it had there. I'm working on some layout issues now and ready. Hopefully I can share this whole thing soon.

    As for the formatting, it doesn't work for me. All I do is copy and paste from xamarin studio and it won't work. I also tried pasting into notepad and then into here and same thing... it goes invisible. Maybe it's the fact that I use windows 10 and something has changed on it, but it's a pain.

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    No it is nothing to do with what you use. You copy and paste into the text field hen select all of your code and press tab.

    It won't matter what browser or is you use. If you don't ensure what you paste is tabbed by one line and has an empty line before it it won't work.

    It's very easy for you to try that. And you can preview it too. It's a pet peeve of mine that posters dissuade others from properly formatting their forum post at by spreading fud so please ensure you have followed the instructions before you continue your claim that the formatting is broken. I find it implausible that it is broken only for you.

    Ok any case, I'm glad you fixed the issue though. I know from experience that the android recycler view Apis are pig ugly compared to their iOS counterpart.

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    Here's the code:

        public class PhotoViewHolder : RecyclerView.ViewHolder
        {
            public ImageView Image { get; private set; }
    
            public PhotoViewHolder (Android.Views.View itemView) : base (itemView)
            {
                // Locate and cache view references:
                Image = new ImageView(itemView.Context);
            }
        }
    
        public class DataSource : RecyclerView.Adapter
        {
            public MyImageGalleryRenderer mPhotoAlbum;
    
            public DataSource (MyImageGalleryRenderer photoAlbum)
            {
                mPhotoAlbum = photoAlbum;
            }
    
            public override RecyclerView.ViewHolder OnCreateViewHolder (ViewGroup parent, int viewType)
            {
                // Create a ViewHolder to hold view references inside the CardView:
                PhotoViewHolder vh = new PhotoViewHolder (parent);
                return vh;
            }
    
            public override void OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
            {
                PhotoViewHolder vh = holder as PhotoViewHolder;
    
                // Load the photo image resource from the photo album:
                vh.Image.SetImageURI(Uri.Parse("file://" + mPhotoAlbum.Element.ItemsSource.Cast<string>().ToArray()[position]));
                vh.Image.SetScaleType(ImageView.ScaleType.FitXy);
    
            }
    
            public override int ItemCount
            {
                get { return mPhotoAlbum.Element.ItemsSource.Cast<string>().Count(); }
            }
        }
    

    You have to put three back-ticks on an empty line before and after the code block, otherwise the whole post shows up empty. Merely indenting the code isn't enough...

    HTH

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭
    edited August 2015

    wow - I just tried it. you are right!! I've never had that happen before. Great tip! thanks!!!

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    I didn't think we needed the back-ticks either. However, given that this is a web site, it's possible some kind of "upgrade" was applied recently that now requires the back-ticks... just guessing. :smile:

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭
    edited August 2015

    In any case - the moral here is, let's ask each other how to format code and get it inline. Text documents break up the flow.

    @jingleton any explanation of why the formatting suddenly requires 3 backticks? This does look like some recent change, and a few of us including David here have been nagging people constantly to format their code correctly. If you guys change the way that works, it's going to lead to a lot more people pasting in bad code blocks and/or using text files all of which detract from forum business.

  • TektonTekton USMember ✭✭✭

    @GeorgeCook Not sure about the requirement, but since I've figured out using Markdown here, I've been using it pretty much religiously. Won't go too far off topic, but would be nice to see a better explanation for formatting posts (Wikipedia article is basically useless if all you want to do is post quickly).

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    Yeah. We've home miles off topic. But it was my question and there's a ton of info for any others searching so I think we are all good. I will start a new one tomorrow re the formatting wiki though

  • JoaquinGrechJoaquinGrech ESMember ✭✭

    Ok, so let's give this a try. In order:
    1) This is a full horizontal photo gallery using RecyclerView. You pass the datasource as a list of strings with the local path.
    2) My images on OnBindViewHolder need to be manually sized to 200x300. No clue how to get the images to size properly to the height+aspect ratio of the RecyclerView. I've tried all kind of combinations with no luck so I had to do it manually for my use case. If someone figures out an automatic way... share it.
    3) I added some code after reading @GeorgeCook library (his is a more generic version, mine is only for images). Mostly I just added the ScrollRecyclerView to listen for scroll movements.

    And here we go, I think this will be my first working paste in the history of my xamarin posts lol

        using Reel.Controls;
        using Reel.Droid.Controls;
        using Xamarin.Forms;
        using System.Collections.Specialized;
        using System.Linq;
        using System.Net;
    
        using Android.Graphics;
        using Android.Webkit;
        using Android.Widget;
    
        using Xamarin.Forms.Platform.Android;
        using Android.Net;
        using Android.Support.V7.Widget;
        using Android.Views;
        using System;
    
        [assembly: ExportRenderer(typeof(MyImageGallery), typeof(MyImageGalleryRenderer))]
        namespace Reel.Droid.Controls
        {
            public class MyImageGalleryRenderer : ViewRenderer<MyImageGallery, RecyclerView>
            {
    
                /// <summary>
                /// The gallery
                /// </summary>
                private ScrollRecyclerView _gallery;
                /// <summary>
                /// The source
                /// </summary>
                private DataSource _source;
    
                /// <summary>
                /// Gets the source.
                /// </summary>
                /// <value>The source.</value>
                private DataSource Source
                {
                    get
                    {
                        return _source ?? (_source = new DataSource(this));
                    }
                }
    
                /// <summary>
                /// Called when [element changed].
                /// </summary>
                /// <param name="e">The e.</param>
                protected override void OnElementChanged(ElementChangedEventArgs<MyImageGallery> e)
                {
                    base.OnElementChanged(e);
    
                    if (e.NewElement  != null) {
                        _gallery = new ScrollRecyclerView (Android.App.Application.Context);
                        var mLayoutManager = new LinearLayoutManager (Context, OrientationHelper.Horizontal, false);
                        mLayoutManager.Orientation = LinearLayoutManager.Horizontal;
                        _gallery.SetLayoutManager (mLayoutManager);
                        _gallery.SetItemAnimator (null);
                        _gallery.HasFixedSize = true;
                        _gallery.HorizontalScrollBarEnabled = true;
                        _gallery.VerticalScrollBarEnabled = false;
                        UpdatePadding ();
                        var scrollListener = new GridViewScrollListener (Element, _gallery); 
                        _gallery.AddOnScrollListener(scrollListener); 
    
    
                        Bind (e.NewElement);
                        _gallery.SetAdapter (Source);
                        SetNativeControl (_gallery);
                    }
                }
    
                protected override void OnSizeChanged (int w, int h, int oldw, int oldh) 
                        { 
                                base.OnSizeChanged (w, h, oldw, oldh); 
                                UpdatePadding (); 
                            } 
                void UpdatePadding() {
                    _gallery.SetPadding (0, 0, 0, 0);
                }
    
                /// <summary>
                /// Binds the specified new element.
                /// </summary>
                /// <param name="newElement">The new element.</param>
                private void Bind(MyImageGallery newElement)
                {
                    if (newElement != null)
                    {
                        newElement.PropertyChanging += ElementPropertyChanging;
                        newElement.PropertyChanged += ElementPropertyChanged;
                        ((INotifyCollectionChanged)newElement.ItemsSource).CollectionChanged += DataCollectionChanged;
                    }
                }
    
                /// <summary>
                /// Elements the property changing.
                /// </summary>
                /// <param name="sender">The sender.</param>
                /// <param name="e">The <see cref="PropertyChangingEventArgs"/> instance containing the event data.</param>
                private void ElementPropertyChanging(object sender, PropertyChangingEventArgs e)
                {
                    if (e.PropertyName == "ItemsSource")
                        ((INotifyCollectionChanged)Element.ItemsSource).CollectionChanged -= DataCollectionChanged;
                }
    
                /// <summary>
                /// Datas the collection changed.
                /// </summary>
                /// <param name="sender">The sender.</param>
                /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
                private void DataCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
                {
                    this.Source.NotifyDataSetChanged();
                }
    
                /// <summary>
                /// Elements the property changed.
                /// </summary>
                /// <param name="sender">The sender.</param>
                /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
                private void ElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
                {
                    if (e.PropertyName == "ItemsSource")
                        ((INotifyCollectionChanged)Element.ItemsSource).CollectionChanged += DataCollectionChanged;
                }
    
    
            }
    
    
            public class PhotoViewHolder : RecyclerView.ViewHolder
            {
                public ImageView Image { get; private set; }
    
                public PhotoViewHolder (ImageView itemView) : base (itemView)
                {
                    // Locate and cache view references:
                    Image = itemView;
                }
            }
    
            public class DataSource : RecyclerView.Adapter
            {
                public MyImageGalleryRenderer mPhotoAlbum;
    
                public DataSource (MyImageGalleryRenderer photoAlbum)
                {
                    mPhotoAlbum = photoAlbum;
                }
    
                public override RecyclerView.ViewHolder OnCreateViewHolder (ViewGroup parent, int viewType)
                {
                    ImageView Image = new ImageView(parent.Context);
                    PhotoViewHolder vh = new PhotoViewHolder (Image);
                    return vh;
                }
    
                public override void OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
                {
                    PhotoViewHolder vh = holder as PhotoViewHolder;
    
                    string path=mPhotoAlbum.Element.ItemsSource.Cast<string>().ToArray()[position];
                    if (!string.IsNullOrEmpty(path))
                        vh.Image.SetImageURI(Android.Net.Uri.Parse("file://" + path));
                    LinearLayout.LayoutParams parms = new LinearLayout.LayoutParams(200,300);
                    vh.Image.LayoutParameters=parms;
                //  vh.Image.RequestLayout ();
                    vh.Image.SetScaleType(ImageView.ScaleType.FitXy);
    
                }
    
                public override int ItemCount
                {
                    get { 
                        return mPhotoAlbum.Element.ItemsSource.Cast<string>().Count();
                    }
                }
            }
    
            public class ScrollRecyclerView : RecyclerView
            {
                public ScrollRecyclerView (IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) : base (javaReference, transfer)
                {
                }
    
    
                public ScrollRecyclerView (Android.Content.Context context, Android.Util.IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle)
                {
                }
    
    
                public ScrollRecyclerView (Android.Content.Context context, Android.Util.IAttributeSet attrs) : base (context, attrs)
                {
                }
    
    
                public ScrollRecyclerView (Android.Content.Context context) : base (context)
                {
                }
    
                public int GetVerticalScrollOffset ()
                {
                    return ComputeVerticalScrollOffset ();
                }
    
                public int GetHorizontalScrollOffset ()
                {
                    return ComputeHorizontalScrollOffset ();
                }
            }
    
            public class GridViewScrollListener : RecyclerView.OnScrollListener
            {
                MyImageGallery _gridView;
    
                ScrollRecyclerView _recyclerView;
    
                public GridViewScrollListener (MyImageGallery gridView, ScrollRecyclerView recyclerView)
                {
                    _gridView = gridView;
                    _recyclerView = recyclerView;
                }
    
                public override void OnScrolled (RecyclerView recyclerView, int dx, int dy)
                {
                    base.OnScrolled (recyclerView, dx, dy);
                    //_gridView.RaiseOnScroll (dx, _recyclerView.GetHorizontalScrollOffset ());
                //  Console.WriteLine (">>>>>>>>> movement: {0}, currentposition: {1}", dx, _recyclerView.GetHorizontalScrollOffset ());
                }
            }
        }
    
  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    Can you please provide a working example project. Im happy to help you debug; but you've got to help me out doing so. You're not asking me to spot a simple typo or answer a simple knowledge question. You're asking me to spend my time executing and debugging code. It's common etiquette to provide a zipped up sample project or git repo for that. If you do that I'll certainly look at it over the weekend.

  • JoaquinGrechJoaquinGrech ESMember ✭✭

    @GeorgeCook It works already. I'm posting it here just in case someone wants to use it. The only pending thing is the image sizing issue but I'm not in a hurry to fix it because I have to finish a whole bunch of things first. I've just let people know so if they use the code they know what's not working properly.

    As soon as I finish the urgent things, I'll post a sample project. The idea is to create a video editor component with thumbnails. The thumbnails are in this RecycleView.

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭

    Oh ok. Good for you then. Great.

Sign In or Register to comment.