0

I want to implement fast scroll with bubble text view in xamarin.android. I took reference from the following link

https://github.com/xamarin/monodroid-samples/tree/master/FastScroll

This example is not properly working for fewer items. In this example, 5000 items are created for fast scroll and its working properly but in my case, there are only 20 items. The scrollbar cannot be able to scroll end of the page and its fluctuating also.

FastScoller.cs

public class FastScroller : LinearLayout
    {
        private static int BUBBLE_ANIMATION_DURATION=100;
        private static int TRACK_SNAP_RANGE=5;

        private TextView bubble;
        private View handle;
        private RecyclerView recyclerView;
        private MyScrollListener scrollListener;
        private int height;
        private ObjectAnimator currentAnimator = null;

        public FastScroller(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
        {
            init(context);
        }
        public FastScroller(Context context) : base(context)
        {
            init(context);
        }
        public FastScroller(Context context, IAttributeSet attrs) : base(context, attrs)
        {
            init(context);
        }

        private void init(Context context)
        {
            scrollListener = new MyScrollListener(this);

            Orientation = Orientation.Horizontal;
            SetClipChildren(false);
            LayoutInflater inflater = LayoutInflater.FromContext(context);
            inflater.Inflate(Resource.Layout.FastScroller, this, true);

            bubble = FindViewById<TextView>(Resource.Id.fastscroller_bubble);
            handle = FindViewById<View>(Resource.Id.fastscroller_handle);
            bubble.Visibility = ViewStates.Invisible;
        }

        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(w, h, oldw, oldh);
            height = h;
        }

        public override bool OnTouchEvent(MotionEvent e)
        {
            var action = e.Action;
            switch (action) {
            case MotionEventActions.Down:
                if (e.GetX() < handle.GetX())
                    return false;
                if (currentAnimator != null)
                    currentAnimator.Cancel();
                if (bubble.Visibility == ViewStates.Invisible)
                    showBubble();
                handle.Selected = true;
                setPosition(e.GetY());
                setRecyclerViewPosition(e.GetY());
                return true;

            case MotionEventActions.Move:
                setPosition(e.GetY());
                setRecyclerViewPosition(e.GetY());
                return true;

            case MotionEventActions.Up:
            case MotionEventActions.Cancel:
                handle.Selected = false;
                hideBubble();
                return true;
            }

            return base.OnTouchEvent(e);
        }

        public void SetRecyclerView(RecyclerView rv)
        {
            this.recyclerView = rv;
            this.recyclerView.SetOnScrollListener(scrollListener);
        }

        private void setRecyclerViewPosition(float y)
        {
            if (recyclerView != null) {
                var itemCount = recyclerView.GetAdapter().ItemCount;
                float proportion;
                if ((int)handle.GetY() == 0)
                    proportion = 0f;
                else if (handle.GetY() + handle.Height >= height - TRACK_SNAP_RANGE)
                    proportion = 1f;
                else
                    proportion = y / (float)height;

                int targetPos = getValueInRange(0, itemCount - 1, (int)(proportion * (float)itemCount));
                recyclerView.ScrollToPosition(targetPos);

                var adapter = recyclerView.GetAdapter() as BaseRecyclerAdapter;
                bubble.Text = adapter.GetTextToShowInBubble(targetPos);
            }
        }

        private int getValueInRange(int min,int max,int value)
        {
            int minimum=Math.Max(min,value);
            return Math.Min(minimum,max);
        }

        private void setPosition(float y)
        {
            int bubbleHeight=bubble.Height;
            int handleHeight=handle.Height;
            handle.SetY(getValueInRange(0,height-handleHeight,(int)(y-handleHeight/2)));
            bubble.SetY(getValueInRange(0,height-bubbleHeight-handleHeight/2,(int)(y-bubbleHeight)));
        }

        private void showBubble()
        {
            bubble.Visibility = ViewStates.Visible;
            if(currentAnimator!=null)
                currentAnimator.Cancel();
            currentAnimator = (ObjectAnimator)ObjectAnimator.OfFloat(bubble, "alpha", 0f, 1f).SetDuration(BUBBLE_ANIMATION_DURATION);
            currentAnimator.Start();
        }

        private void hideBubble()
        {
            if(currentAnimator != null)
                currentAnimator.Cancel();
            currentAnimator = (ObjectAnimator)ObjectAnimator.OfFloat(bubble,"alpha",1f,0f).SetDuration(BUBBLE_ANIMATION_DURATION);
            currentAnimator.AddListener(new MyListener(this));
            currentAnimator.Start();
        }

        internal class MyListener : AnimatorListenerAdapter
        {
            private FastScroller scroll;

            public MyListener(FastScroller scroller)
            {
                this.scroll = scroller;
            }

            public override void OnAnimationEnd(Animator animation)
            {
                base.OnAnimationEnd(animation);
                scroll.bubble.Visibility = ViewStates.Invisible;
                scroll.currentAnimator = null;
            }
            public override void OnAnimationCancel(Animator animation)
            {
                base.OnAnimationCancel(animation);
                scroll.bubble.Visibility = ViewStates.Invisible;
                scroll.currentAnimator = null;
            }
        }

        internal class MyScrollListener : RecyclerView.OnScrollListener
        {
            private readonly FastScroller scroll;

            public MyScrollListener(FastScroller scroller)
            {
                this.scroll = scroller;
            }

            public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
            {
                View firstVisibleView   = recyclerView.GetChildAt(0);
                int firstVisiblePosition = recyclerView.GetChildPosition(firstVisibleView);
                int visibleRange        = recyclerView.ChildCount;
                int lastVisiblePosition = firstVisiblePosition + visibleRange;
                int itemCount           = recyclerView.GetAdapter().ItemCount;
                int position;

                if(firstVisiblePosition==0)
                    position=0;
                else if(lastVisiblePosition==itemCount-1)
                    position = itemCount-1;
                else
                    position = firstVisiblePosition;

                float proportion=(float)position/(float)itemCount;
                this.scroll.setPosition(scroll.height*proportion);
            }
        }
    }

Some calculations are faulty in above class.

How to adjust scroll bar properly ?

RMR
  • 599
  • 3
  • 12
  • 35
  • By `Some calculations are faulty in above class.`, is here any error or exception? – Robbit Dec 12 '17 at 06:06
  • No, there is not any error or exception but its only working for more items (5000 items as an example) but the scroll is not properly adjusted for fewer items (20 items as an example) even fluctuating when dragging to bottom. – RMR Dec 12 '17 at 06:19
  • Download the demo from given link and change the constant value of items from 5000 to 100 / 20 / 60 whatever then try to scroll you will get the actual issue. – RMR Dec 12 '17 at 06:22
  • Please check the link, I have opened the `FastScroll` project, but it is different from you, and there is no `FastScoller.cs`. [And there is an `HomeScreen` extends `ListActivity`, it works pretty well](https://github.com/xamarin/monodroid-samples/blob/master/FastScroll/HomeScreen.cs)! – Robbit Dec 12 '17 at 07:19
  • I have recycler view and I need bubble alphabetic text view as indexer also. Is there any way to change the current code. – RMR Dec 12 '17 at 08:43

0 Answers0