You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RecyclerView的OnScrollChanged方法获取参数始终为0问题

RecyclerView滚动时OnScrollChanged参数全为0?试试这个正确的实现方式

我明白你现在的困扰:想实现ActionBar随滚动渐变的效果,把原方案的ScrollView换成了Fragment里的RecyclerView,但重写的OnScrollChanged方法拿到的参数全是0,导致渐变逻辑完全失效。

问题根源

其实RecyclerView的OnScrollChanged方法和ScrollView的不一样——它监听的是RecyclerView自身在父容器中的滚动位置,而不是内部内容的滚动偏移。你的RecyclerView应该是占满父布局的,所以它自身根本没滚动,参数自然全是0。要获取内容的滚动偏移,得用RecyclerView专门的滚动监听器。

修复方案

下面是调整后的完整实现:

1. 重写自定义RecyclerView,改用正确的滚动监听方式

替换原来的NotifyingScrollRecyclerView,移除OnScrollChanged的重写,改用AddOnScrollListener来监听内容滚动:

public class NotifyingScrollRecyclerView : RecyclerView
{
    public delegate void ScrollChangedHandler(int scrollY, EventArgs e);
    public event ScrollChangedHandler ScrollChanged;
    private readonly EventArgs _defaultArgs = new EventArgs();

    // 保留所有构造函数
    public NotifyingScrollRecyclerView(Context context) : base(context) => Init();
    public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs) => Init();
    public NotifyingScrollRecyclerView(IntPtr ptr, JniHandleOwnership ownership) : base(ptr, ownership) => Init();
    public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) => Init();

    private void Init()
    {
        AddOnScrollListener(new InternalScrollListener(this));
    }

    private class InternalScrollListener : RecyclerView.OnScrollListener
    {
        private readonly NotifyingScrollRecyclerView _host;

        public InternalScrollListener(NotifyingScrollRecyclerView host) => _host = host;

        public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            base.OnScrolled(recyclerView, dx, dy);
            var scrollY = CalculateVerticalScrollOffset(recyclerView);
            _host.ScrollChanged?.Invoke(scrollY, _host._defaultArgs);
        }

        // 计算RecyclerView内容的垂直滚动偏移量(适配LinearLayoutManager)
        private int CalculateVerticalScrollOffset(RecyclerView recyclerView)
        {
            if (!(recyclerView.GetLayoutManager() is LinearLayoutManager layoutManager))
                return 0;

            var firstVisiblePos = layoutManager.FindFirstVisibleItemPosition();
            if (firstVisiblePos < 0)
                return 0;

            var firstVisibleView = layoutManager.FindViewByPosition(firstVisiblePos);
            if (firstVisibleView == null)
                return 0;

            // 计算已滚动的总高度:已完全滚动的item高度 + 当前可见item的偏移
            return firstVisiblePos * firstVisibleView.Height - firstVisibleView.Top;
        }
    }
}

2. 调整监听器类适配新的事件参数

修改ScrollViewChangedListener的事件处理方法,现在我们直接拿到内容的滚动Y值:

public class ScrollViewChangedListener
{
    private readonly Activity _activity;
    private readonly Android.Support.V7.App.ActionBar _actionBar;
    private readonly Drawable _actionBarDrawable;

    public ScrollViewChangedListener(Activity activity, NotifyingScrollRecyclerView recyclerView)
    {
        _activity = activity;
        _actionBar = ((UserPageActivity)activity).SupportActionBar;
        _actionBarDrawable = activity.GetDrawable(Resource.Drawable.actionbar_background);
        _actionBarDrawable.SetAlpha(0);

        // 订阅滚动事件
        recyclerView.ScrollChanged += OnRecyclerViewScrolled;
    }

    private void OnRecyclerViewScrolled(int scrollY, EventArgs e)
    {
        var profileBanner = _activity.FindViewById<ImageView>(Resource.Id.profilebanner);
        if (profileBanner == null)
            return;

        // 计算渐变比例:滚动距离 / (头部高度 - ActionBar高度)
        var headerHeight = profileBanner.Height - _actionBar.Height;
        if (headerHeight <= 0)
            return;

        var ratio = Math.Min(Math.Max((float)scrollY / headerHeight, 0f), 1f);
        var newAlpha = (int)(ratio * 255);
        
        _actionBarDrawable.SetAlpha(newAlpha);
        _actionBar.SetBackgroundDrawable(_actionBarDrawable);
    }
}

3. 在Fragment中初始化监听器

确保在Fragment的OnViewCreated方法中(此时RecyclerView已经初始化完成)创建监听器:

public override void OnViewCreated(View view, Bundle savedInstanceState)
{
    base.OnViewCreated(view, savedInstanceState);
    var recyclerView = view.FindViewById<NotifyingScrollRecyclerView>(Resource.Id.your_recycler_view_id);
    if (recyclerView != null)
    {
        new ScrollViewChangedListener(Activity, recyclerView);
    }
}

额外注意点

  • 如果你的RecyclerView用的是GridLayoutManager,需要修改CalculateVerticalScrollOffset方法的逻辑,适配网格布局的item高度计算。
  • 确保profilebanner已经完成布局测量,否则获取到的Height会是0。可以通过profileBanner.ViewTreeObserver.AddOnGlobalLayoutListener来监听布局完成事件,再初始化监听器。

内容的提问来源于stack exchange,提问作者iLikeKFC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:37:10