OnScrollChanged doesn't get the RecyclerView parameters

128 views Asked by At

I'm trying to implement this code here in my project:

The differences are that instead of a ScrollView I'm using a RecyclerView and that instead of having my RecyclerView directly in the Activity I have inside a fragment that sets the listener I need. The problem is that while the OnScrollChanged(int l, int t, int oldl, int oldt) gets actually called everytime I scroll, the parameters it obtains are always 0 and I don't understand why, so ScrollChangedTarget doesn't work as intended.

This is the custom RecyclerView:

 public class NotifyingScrollRecyclerView : RecyclerView
{
    private Activity activity;
    private View headerView;
    private View footerView;

    public delegate void ScrollChangedHandler(int l, int t, int oldl, int oldt, EventArgs e);

    public event ScrollChangedHandler scrollChanged;
    public EventArgs e = null;

    //Enabling all required constructors
    public NotifyingScrollRecyclerView(Context context) : base(context)
    {

    }

    public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs)
    {

    }

    public NotifyingScrollRecyclerView(IntPtr ptr, JniHandleOwnership ownership) : base(ptr, ownership)
    {

    }

    public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
    {

    }

    //This method attribute allows us to register the inbuilt OnScrollChanged event that fires when scrolling a ScrollView
    [Android.Runtime.Register("onScrollChanged", "(IIII)V", "GetOnScrollChanged_IIIIHandler")]
    protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
    {
        base.OnScrollChanged(l, t, oldl, oldt);
        scrollChanged(l, t, oldl, oldt, e);
    }



}


//Set an event listener
public class ScrollViewChangedListener
{
    private Activity activity;
    private NotifyingScrollRecyclerView scrollView;
    private Android.Support.V7.App.ActionBar actionBar;
    private Drawable actionBarDrawable;
  be changed
    public ScrollViewChangedListener(Activity a, NotifyingScrollRecyclerView n)
    {
        n.scrollChanged += ScrollChangedTarget;
        this.activity = a;
        this.scrollView = n;
        this.actionBar = ((UserPageActivity)a).SupportActionBar;

        this.actionBarDrawable = a.GetDrawable(Resource.Drawable.actionbar_background);
        this.actionBarDrawable.SetAlpha(0);

    }

    //Handle the changing of the scroll
    public void ScrollChangedTarget(int l, int t, int oldl, int oldt, EventArgs e)
    {
        //You set the View you want to be your header as a header height, and then get it's height
        int headerHeight = activity.FindViewById<ImageView>(Resource.Id.profilebanner).Height -
        this.actionBar.Height;
        float ratio = (float)Math.Min(Math.Max(t, 0), headerHeight) / headerHeight;
        int newAlpha = (int)(ratio * 255);

            this.actionBarDrawable.SetAlpha(newAlpha);
            this.actionBar.SetBackgroundDrawable(this.actionBarDrawable);


    }

}

This is the Activity which calls the fragment (it doesn't do anything in particular aside calling the fragment in this case):

[Activity(Label = "UserPageActivity")]
public class UserPageActivity : BaseActivity
{
    protected override int LayoutResource => Resource.Layout.activity_user_page;
    UserViewModel viewModel;
    TextView username;
    TextView usernameToolbar;
    Button followButton;


    ViewPager pager;
    UserTabsAdapter adapter;
    bool IsLoggedUser;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        IsLoggedUser = Intent.GetStringExtra("userId").Equals(LoginController.GetInstance().CurrentUser.Email);
        base.OnCreate(savedInstanceState);
        viewModel = new UserViewModel();
        viewModel.UserLoaded += new UserViewModel.UserLoadedHandler(OnUserLoaded);
        viewModel.LoadUserCommand.Execute(Intent.GetStringExtra("userId"));
        username = FindViewById<TextView>(Resource.Id.profilename);
        usernameToolbar = FindViewById<TextView>(Resource.Id.usernamePage);
        followButton = FindViewById<Button>(Resource.Id.followButton);

        username.Text = Intent.GetStringExtra("username");
        usernameToolbar.Text = Intent.GetStringExtra("username");

        adapter = new UserTabsAdapter(this, SupportFragmentManager);
        pager = FindViewById<ViewPager>(Resource.Id.user_viewpager);
        var tabs = FindViewById<TabLayout>(Resource.Id.tabs);
        pager.Adapter = adapter;
        tabs.SetupWithViewPager(pager);
        pager.OffscreenPageLimit = 5;


        pager.PageSelected += (sender, args) =>
        {
            var fragment = adapter.InstantiateItem(pager, args.Position) as IFragmentVisible;

            fragment?.BecameVisible();
        };
    }

    private void OnUserLoaded(bool loaded)
    {

    }

    protected override void OnStart()
    {
        base.OnStart();

        if (IsLoggedUser)
        {
            followButton.Visibility = ViewStates.Gone;

        }
        else
        {
            bool following;
            if (LoginController.GetInstance().CurrentUser.FollowsUsers.ContainsKey(Intent.GetStringExtra("userId")))
            {
                followButton.Text = "Unfollow";
                following = true;
            }
            else
            {
                followButton.Text = "Follow";
                following = false;
            }

            followButton.Click += (object sender, EventArgs e) =>
            {
                followButton.Enabled = false;
                if (following)
                {
                    UserService service = ServiceLocator.Instance.Get<UserService>();

                    service.SetUser(LoginController.GetInstance().CurrentUser);
                    service.RemoveFollowsUserCommand.Execute(viewModel.LoadedUser.Email);

                    service.SetUser(viewModel.LoadedUser);
                    service.RemoveFollowedByUserCommand.Execute(LoginController.GetInstance().CurrentUser.Email);

                    followButton.Text = "Follow";
                    following = false;
                }
                else
                {
                    UserService service = ServiceLocator.Instance.Get<UserService>();

                    service.SetUser(LoginController.GetInstance().CurrentUser);
                    service.AddFollowsUserCommand.Execute(viewModel.LoadedUser);

                    service.SetUser(viewModel.LoadedUser);
                    service.AddFollowedByUserCommand.Execute(LoginController.GetInstance().CurrentUser);

                    followButton.Text = "Unfollow";
                    following = true;
                }
                followButton.Enabled = true;
            };
        }
    }
}

class UserTabsAdapter : FragmentStatePagerAdapter
{
    string[] titles;

    public override int Count => titles.Length;

    public UserTabsAdapter(Context context, Android.Support.V4.App.FragmentManager fm) : base(fm)
    {
        titles = context.Resources.GetTextArray(Resource.Array.user_sections);
    }

    public override Java.Lang.ICharSequence GetPageTitleFormatted(int position) =>
                        new Java.Lang.String(titles[position]);

    public override Android.Support.V4.App.Fragment GetItem(int position)
    {
        switch (position)
        {
            case 0: return UserContestsFragment.NewInstance();
            case 1: return UserPartecipationsFragment.NewInstance();
            case 2: return GlobalContestFragment.NewInstance();
            case 3: return MessagesFragment.NewInstance();
        }
        return null;
    }

    public override int GetItemPosition(Java.Lang.Object frag) => PositionNone;
}

This is the fragment which setups the listener for the recyclerview:

      public class UserContestsFragment : AbstractRefresherFadingToolbarFragment<Contest>
{
    public static UserContestsFragment NewInstance() =>
        new UserContestsFragment { Arguments = new Bundle() };


    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
    }

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        ContestViewModel viewModel = new ContestViewModel();
        base.ViewModel = viewModel;
        base.LoadItemsCommand = viewModel.LoadAllByCreatorUserCommand;
        base.param = Activity.Intent.GetStringExtra("userId");
        base.adapter = new ContestsAdapter(Activity, viewModel);
        var view = base.OnCreateView(inflater, container, savedInstanceState);

        ScrollViewChangedListener listener = new ScrollViewChangedListener(Activity, recyclerView);
        return view;
    }

And this is the abstract fragment needed by that fragment which is in charge of setting up the layout:

  public abstract class AbstractRefresherFadingToolbarFragment<T> : Android.Support.V4.App.Fragment, IFragmentVisible
{
    public ICollectionViewModel<T> ViewModel;
    public ICommand LoadItemsCommand;
    public object param; //parametro per il LoadItemsCommand
    public ItemsAdapter<T> adapter;
    public SwipeRefreshLayout refresher;
    public ProgressBar progress;
    public NotifyingScrollRecyclerView recyclerView;
    //LruCache cache = new LruCache((int)(Runtime.GetRuntime().MaxMemory() / 4));

    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
    }

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.Inflate(Resource.Layout.fragment_fading_toolbar, container, false);
        recyclerView = view.FindViewById<NotifyingScrollRecyclerView>(Resource.Id.recyclerView);
        //ScrollViewChangedListener listener = new ScrollViewChangedListener((UserPageActivity)Activity, recyclerView);
        //adapter.cache = cache;
        recyclerView.HasFixedSize = true;
        recyclerView.SetAdapter(adapter);
        recyclerView.SetItemViewCacheSize(4);
        //recyclerView.ChildViewAttachedToWindow += new EventHandler<RecyclerView.ChildViewAttachedToWindowEventArgs>(OnChildViewAttached);
        //recyclerView.ChildViewDetachedFromWindow += new EventHandler<RecyclerView.ChildViewDetachedFromWindowEventArgs>(OnChildViewDetached);

        refresher = view.FindViewById<SwipeRefreshLayout>(Resource.Id.refresher);
        refresher.SetColorSchemeColors(Resource.Color.accent);

        progress = view.FindViewById<ProgressBar>(Resource.Id.progressbar_loading);
        progress.Visibility = ViewStates.Gone;

        return view;
    }


}
1

There are 1 answers

4
masoud On BEST ANSWER

I didn't read your entire code but I looked at the site you linked in your question. The functionality of the hidden action bar you want to use is handled by CollapsingToolbarLayout in the support library. To know how to use it, go to Cheesesquare. It is a complete example of the support library widgets and can be built and run without any changes.

Edit: RecyclerView has a method named AddOnScrollListener. Use it instead of the OnScrollChanged. If you use an inherited class from RecyclerView, you can call it in all of the constructors of that class.