SwipeRefreshLayout如何仅在从顶部下拉时触发刷新?
限制SwipeRefreshLayout仅在初始顶部下拉时触发刷新
我有一个嵌套RecyclerView的SwipeRefreshLayout,布局代码如下:
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swipeRefreshLayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/catalog_recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
需求:仅在从可滚动区域初始顶部位置开始下拉时触发刷新;若用户先滚动过RecyclerView内容,再回到顶部后下拉,则不触发刷新。
实现方案
SwipeRefreshLayout没有原生配置项支持该逻辑,需要通过监听RecyclerView滚动状态,结合自定义View或动态控制刷新组件的启用状态来实现。
方案一:自定义SwipeRefreshLayout(推荐)
通过重写嵌套滚动的拦截方法,精准控制触发刷新的时机:
- 自定义继承SwipeRefreshLayout的类:
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout { private RecyclerView mRecyclerView; private boolean hasRecyclerViewScrolled = false; public CustomSwipeRefreshLayout(@NonNull Context context) { super(context); } public CustomSwipeRefreshLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public void setRecyclerView(RecyclerView recyclerView) { mRecyclerView = recyclerView; mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // 只要RecyclerView发生过滚动,标记状态 if (dx != 0 || dy != 0) { hasRecyclerViewScrolled = true; } } }); } @Override public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) { // 仅当RecyclerView在顶部,且从未被滚动过,才允许触发下拉刷新 boolean isAtTop = !mRecyclerView.canScrollVertically(-1); return isAtTop && !hasRecyclerViewScrolled && super.onStartNestedScroll(child, target, axes, type); } // 重置滚动状态(比如刷新完成后,允许再次从初始顶部触发刷新) public void resetScrollState() { hasRecyclerViewScrolled = false; } }
- 替换布局中的SwipeRefreshLayout:
<com.yourpackage.CustomSwipeRefreshLayout android:id="@+id/swipeRefreshLayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/catalog_recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.yourpackage.CustomSwipeRefreshLayout>
- 在代码中初始化并设置监听:
CustomSwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout); RecyclerView catalogRecyclerView = findViewById(R.id.catalog_recycler_view); swipeRefreshLayout.setRecyclerView(catalogRecyclerView); swipeRefreshLayout.setOnRefreshListener(() -> { // 执行刷新逻辑 // ... // 刷新完成后停止加载动画 swipeRefreshLayout.setRefreshing(false); // 可选:重置滚动状态,允许下次从初始顶部触发刷新 swipeRefreshLayout.resetScrollState(); });
方案二:动态控制SwipeRefreshLayout的启用状态
无需自定义View,通过RecyclerView的滚动监听器动态设置SwipeRefreshLayout是否可用:
private boolean hasRecyclerViewScrolled = false; private SwipeRefreshLayout swipeRefreshLayout; private RecyclerView catalogRecyclerView; // 初始化组件 swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout); catalogRecyclerView = findViewById(R.id.catalog_recycler_view); // 监听RecyclerView滚动状态 catalogRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (dx != 0 || dy != 0) { hasRecyclerViewScrolled = true; } // 仅当RecyclerView在顶部且未滚动过时,启用下拉刷新 boolean isAtTop = !recyclerView.canScrollVertically(-1); swipeRefreshLayout.setEnabled(isAtTop && !hasRecyclerViewScrolled); } }); // 设置刷新监听器 swipeRefreshLayout.setOnRefreshListener(() -> { // 执行刷新逻辑 // ... swipeRefreshLayout.setRefreshing(false); // 重置滚动状态 hasRecyclerViewScrolled = false; });
内容的提问来源于stack exchange,提问作者Pierre de LESPINAY
相关产品推荐
相关产品推荐

