Forum Xamarin Xamarin.Forms

Detect visibility of item on ListView

I'm trying to detect when a ViewCell in a ListView is fully visible on screen, so I can do an action. Imagine something like what Vine or Facebook does that when you scroll up and down, the videos are set to auto play as soon as they are visible on screen. But you won't ever get two at the same time.

My app needs to do something similar. My ViewCell is pretty large, so it won't never have two cells fully visible on the screen. When one of them is fully visible I want to change the title of the page (or whatever other event, it's just an example use case).

I've tried using
this.Appearing += PageItem_Appearing;
this.Disappearing += PageItem_Disappearing;

inside a ViewCell, but it doesn't work. They seem to get called as soon as you get a bit of one of the cells into view, not when they are fully visible. They also don't seem to work well with disappearing (They are not on screen and they don't get called after a long while).

So, any idea how to achieve this? I've looked into some visible on screen method for the controls, so that I could manually detect when Labels and other things on the corners of the cells are visible, but I didn't find anything. So i'm at lost on how to achieve this.

Tagged:

Answers

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    If your ViewCells are fixed size, you could subclass the ListViewRenderer on each platform to make it report scroll position, then have an even that fires when the ViewCell is fully on-screen.

  • DavidDancyDavidDancy AUMember ✭✭✭✭

    You can use something like the technique shown here to subclass the ListViewRenderer. The post shows the iOS renderer, but the Android version is simpler.

  • JoaquinGrechJoaquinGrech ESMember ✭✭

    How can I know the size of a viewcell? It can be fixed size but changes depending on device (wider devices will wrap some labels differently and change the height of it too)

  • Hi,

    I've tried to use the ItemAppearing and ItemDisappearing from Xamarin.Forms ListView.

    The ItemAppearing works fine, but the ItemDisappearing works in a weird way...

    I've tested the behaviour in the android build.

    The solution I found uses the native components to detect the visibility of the items.

    I'll try to explain here.

    First you need to create an interface in the shared project:

    public interface ListViewHelper
    {
        // YourDataClass is the data class you put in your ListView Item
        List<YourDataClass> GetListViewVisibleItems(Xamarin.Forms.ListView lv);
    }
    

    After that you need to create the implementation in android project and the implementation in the iOS project.

    Android implementation:

    [assembly: Xamarin.Forms.Dependency(typeof(MyProject.Droid.ListViewHelper_Android))]
    namespace MyProject.Droid
    {
        public class ListViewHelper_Android : ListViewHelper
        {
            //need to cast the JavaObject to the desired C# class
            public static T Cast<T>(Java.Lang.Object obj) where T : class
            {
                var propertyInfo = obj.GetType().GetProperty("Instance");
                return propertyInfo == null ? null : propertyInfo.GetValue(obj, null) as T;
            }
    
            public List<YourDataClass> GetListViewVisibleItems(Xamarin.Forms.ListView lv)
            {
    
                List<YourDataClass> visibleItems = new List<YourDataClass>();
    
                Xamarin.Forms.Platform.Android.ListViewRenderer renderer =
                    (Xamarin.Forms.Platform.Android.ListViewRenderer)Xamarin.Forms.Platform.Android.Platform.GetRenderer(lv);
    
                Android.Widget.ListView nativeListView = renderer.Control;
    
                for (int i = 0; i < nativeListView.ChildCount; i++)
                {
                    View view = nativeListView.GetChildAt(i);
    
                    if (view.Visibility != ViewStates.Visible)
                        continue;
    
                    int pos = nativeListView.GetPositionForView(view);
    
                    if ( pos < 0 || pos == AdapterView.InvalidPosition || pos >= nativeListView.Adapter.Count)
                        continue;
    
                    var obj = nativeListView.Adapter.GetItem(pos);
    
                    if (obj == null)
                        continue;
    
                    YourDataClass visibleElement = Cast<YourDataClass>(obj);
    
                    if (visibleElement != null && ! visibleItems.Contains(visibleElement))
                        visibleItems.Add(visibleElement);
                }
                return visibleItems;
            }
        }
    }
    

    iOS implementation:

    [assembly: Xamarin.Forms.Dependency(typeof(MyProject.iOS.ListViewHelper_iOS))]
    namespace MyProject.iOS
    {
        public class ListViewHelper_iOS : ListViewHelper
        {
            public List<YourDataClass> GetListViewVisibleItems(Xamarin.Forms.ListView lv)
            {
    
                List<YourDataClass> visibleItems = new List<YourDataClass>();
    
                var array = lv.ItemsSource.OfType<YourDataClass>();
    
                Xamarin.Forms.Platform.iOS.ListViewRenderer renderer = 
                    (Xamarin.Forms.Platform.iOS.ListViewRenderer)Xamarin.Forms.Platform.iOS.Platform.GetRenderer(lv);
    
                UITableView nativeListView = renderer.Control;
    
                NSIndexPath[] index =  nativeListView.IndexPathsForVisibleRows;
    
                if (index != null)
                    for (int i = 0; i < index.Length;i++)
                        if (index[i].Row >=0 && index[i].Row < array.Count())
                            visibleItems.Add(array.ElementAt(index[i].Row));
    
                return visibleItems;
            }
        }
    }
    

    After that you need just to use the class interface in any place you want the visible list of items. You can use this in the shared library project.

    For Example:

    var listViewHelper = DependencyService.Get<ListViewHelper>();
    // MyListView is the Xamarin.Forms.ListView instance
    List<YourDataClass> currentVisibleList = listViewHelper.GetListViewVisibleItems(MyListView);
    

    I've tested this solution in Android and iOS projects.

    With some effort you can use generics instead of using the YourDataClass hardcoded.

Sign In or Register to comment.