Scroll Index method not firing consistently

In a Xamarin.Forms and Xamarin.Android project I create a Custom Render and Adapter for a ListView. The adapter implements BaseAdapter and ISectionIndexer. The custom render of this control is using FastScroll feature, in Android when you tap this scroll a bubble with a index letter appears. This works fine, but my idea is to have a way to catch the selected index after releasing scroll and that scroll "bubble" disappears. I thought with the following class (in the GetSectionForPosition method) could achieve that:

public class ListViewconIndexAdapter : BaseAdapter<string>, ISectionIndexer
    {
        string[] items;
        Activity context;

        string[] sections;
        Java.Lang.Object[] sectionsObjects;
        Dictionary<string, int> alphaIndex;

        public ListViewconIndexAdapter(Activity context, string[] items) : base()
        {
            this.context = context;
            this.items = items;

            alphaIndex = new Dictionary<string, int>();
            for (int i = 0; i < items.Length; i++)
            {
                var key = items[i][0].ToString();
                if (!alphaIndex.ContainsKey(key))
                    alphaIndex.Add(key, i);
            }
            sections = new string[alphaIndex.Keys.Count];
            alphaIndex.Keys.CopyTo(sections, 0);
            sectionsObjects = new Java.Lang.Object[sections.Length];
            for (int i = 0; i < sections.Length; i++)
            {
                sectionsObjects[i] = new Java.Lang.String(sections[i]);
            }

        }


        public override Java.Lang.Object GetItem(int position)
        {
            return position;
        }

        public override long GetItemId(int position)
        {
            return position;
        }

        public override string this[int position]
        {
            get { return items[position]; }
        }

        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            View view = convertView;
            if (view == null)
                view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
            view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position];
            return view;
        }

        //Fill in cound here, currently 0
        public override int Count
        {
            get { return items.Length; }
        }

        // -- ISectionIndexer --
        public int GetPositionForSection(int section)
        {
            return alphaIndex[sections[section]];
        }

        public int GetSectionForPosition(int position)
        {      // this method isn't called in this example, but code is provided for completeness
            int prevSection = 0;

            for (int i = 0; i < sections.Length; i++)
            {
                if (GetPositionForSection(i) > position)
                {
                    break;
                }

                prevSection = i;
            }

            Console.WriteLine(prevSection);
            Console.WriteLine(sections[prevSection]);

            //Toast.MakeText(context, sections[prevSection], ToastLength.Short).Show();
            Xamarin.Forms.MessagingCenter.Send<object,string>(this, "CambioSeccion", sections[prevSection]);
            return prevSection;
        }

    }

I put those Console.writeline for checking the index letter and that Message send is a way to send it back to PCL/NET Standard code (to show an DisplayAlert or something). But the problem is that method firing is not consistent, for example, sometimes you fast scroll down to 'C' but Console doesn't print anything after releasing it there, but after touching it again where you leave it, it fires up. But sometimes it works like i want, it prints after release the scroll at selected index.

1 answer

  • answered 2018-11-13 19:47 SushiHangover

    ListView has two different scroll listeners, AbsListView.IOnScrollListener and AbsListView.IOnScrollChangeListener (this one was added in API 23) and a touch listener (AbsListView.IOnTouchListener)

    I think based upon your use-case, you are looking for the OnScrollStateChanged and when it goes into idle state and you are not touching the listview, do something (or vice versa).

    Example (adjust to your needs of course):

        public class MyScrollListener : Java.Lang.Object, AbsListView.IOnTouchListener, AbsListView.IOnScrollListener, AbsListView.IOnScrollChangeListener //(API23)
        {
            bool touching;
            bool scrolling;
    
            public void OnScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
            {
            }
    
            public void OnScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
            {
            }
    
            public void OnScrollStateChanged(AbsListView view, [GeneratedEnum] ScrollState scrollState)
            {
                switch(scrollState)
                {
                    case ScrollState.Idle:
                        if (!touching)
                        {
                            scrolling = false;
                            GetSelection();
                        }
                        break;
                    default:
                        scrolling = true;
                        break;
                }
            }
    
            public bool OnTouch(View v, MotionEvent e)
            {
                switch (e.Action)
                {
                    case MotionEventActions.Up:
                        touching = false;
                        if (!scrolling)
                            GetSelection();
                        break;
                    default:
                        touching = true;
                        break;
                }
                return true;
            }
    
            void GetSelection()
            {
                // touch and srolling is done, do something
            }
        }
    

    Usage:

    var scrollListener = new MyScrollListener();
    listView.SetOnTouchListener(scrollListener);
    listView.SetOnScrollListener(scrollListener);
    listView.SetOnScrollChangeListener(scrollListener); // API23