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
相关产品推荐
相关产品推荐

