如何用MotionLayout仅在RecyclerView超屏滚动时隐藏/显示视图
解决方案
要实现仅在RecyclerView内容超出屏幕时才允许折叠搜索栏和TextView,核心是动态控制MotionLayout的滑动触发权限——只在RecyclerView可滚动时启用折叠交互,否则禁用。以下是具体实现步骤:
1. 编写判断RecyclerView是否可滚动的工具方法
在你的SearchFragment中添加方法,用来检测RecyclerView内容是否超出屏幕:
private fun isRecyclerViewScrollable(): Boolean { val recyclerView = binding.listaDeportistasRv // 先判断RecyclerView是否可见且有数据 if (recyclerView.visibility != View.VISIBLE || recyclerView.adapter?.itemCount ?: 0 == 0) { return false } val layoutManager = recyclerView.layoutManager as LinearLayoutManager val firstVisibleItem = layoutManager.findFirstCompletelyVisibleItemPosition() val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition() // 检查是否所有item都能完全显示在屏幕内 return !(firstVisibleItem == 0 && lastVisibleItem == recyclerView.adapter!!.itemCount - 1) }
2. 动态控制MotionLayout的滑动交互
在Fragment的onViewCreated中,监听RecyclerView的内容和状态变化,实时更新MotionLayout的交互权限:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val binding = SearchFragmentBinding.bind(view) val motionLayout = binding.root as MotionLayout val recyclerView = binding.listaDeportistasRv // 初始化时先判断一次状态 updateMotionLayoutScrollEnabled(motionLayout) // 监听Adapter数据变化 recyclerView.adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onChanged() = updateMotionLayoutScrollEnabled(motionLayout) override fun onItemRangeInserted(positionStart: Int, itemCount: Int) = updateMotionLayoutScrollEnabled(motionLayout) override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) = updateMotionLayoutScrollEnabled(motionLayout) }) // 监听RecyclerView布局完成事件 recyclerView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { recyclerView.viewTreeObserver.removeOnGlobalLayoutListener(this) updateMotionLayoutScrollEnabled(motionLayout) } }) } private fun updateMotionLayoutScrollEnabled(motionLayout: MotionLayout) { val isScrollable = isRecyclerViewScrollable() // 启用/禁用展开<->折叠的滑动Transition motionLayout.setTransitionEnabled(R.id.expandido, R.id.recogido, isScrollable) motionLayout.setTransitionEnabled(R.id.recogido, R.id.expandido, isScrollable) // 如果不可滚动,强制回到展开状态 if (!isScrollable && motionLayout.currentState == R.id.recogido) { motionLayout.transitionToState(R.id.expandido) } }
3. 优化MotionScene配置(可选)
给Transition添加进度阈值,避免轻微滑动就触发动画,提升交互体验:
<Transition motion:constraintSetEnd="@id/recogido" motion:constraintSetStart="@id/expandido" motion:progressThreshold="0.2"> <OnSwipe motion:dragDirection="dragUp" motion:moveWhenScrollAtTop="false" motion:touchAnchorId="@id/lista_deportistas_Rv" motion:touchAnchorSide="top" /> </Transition>
关键说明
- 当RecyclerView为空(itemCount=0)或所有内容完全显示在屏幕内时,会直接禁用折叠滑动,并强制回到展开状态
- 针对Adapter数据变化、RecyclerView布局完成的监听,确保每次内容更新后都能正确判断交互权限
- 若使用GridLayoutManager,需修改
isRecyclerViewScrollable方法中的判断逻辑,通过计算总行数与可见行数的对比来判断是否可滚动
内容的提问来源于stack exchange,提问作者xerez
相关产品推荐
相关产品推荐

