ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Android 下拉刷新:SwipeRefreshLayout 原理、用法与 API 详解

Android 下拉刷新:SwipeRefreshLayout 原理、用法与 API 详解 文章目录一、SwipeRefreshLayout 的定位二、布局使用详解2.1 布局骨架2.2 必须遵守的两条布局规则三、代码接入基本三步四、设置方法详解API4.1 状态控制4.2 监听器设置4.3 外观定制4.4 转圈位置与触发距离4.5 嵌套滚动五、进阶细节仅在滚动到顶部时允许刷新六、常见问题七、小结下拉刷新Pull-to-Refresh是移动端最常见的交互之一用户在可滚动区域的顶部继续下拉松手后触发数据重新加载顶部出现转圈动画作为加载反馈。Android 官方实现为SwipeRefreshLayoutandroidx.swiperefreshlayout.widget本文结合天气 App 的实际用法从布局结构到全部常用设置方法逐一详解。一、SwipeRefreshLayout 的定位SwipeRefreshLayout继承自ViewGroup是官方提供的一个通用容器控件自身不绘制业务内容职责只有两件监听手势当子 View 已滚动到顶部、用户继续向下拖动时判定为一次刷新意图状态反馈绘制转圈动画手势结束时回调刷新监听器由监听器执行真正的数据加载。因此布局上它必须作为最外层容器存在把需要支持刷新的内容整体包裹起来。二、布局使用详解2.1 布局骨架以我的天气 App 为例页面结构是SwipeRefreshLayout // 最外层下拉刷新的载体 └─ ConstraintLayout // 页面内容容器 ├─ 顶部标题栏城市名、搜索按钮 └─ ScrollView // 可滚动正文 └─ 温度、湿度、逐小时预报等内容androidx.swiperefreshlayout.widget.SwipeRefreshLayoutandroid:idid/swipe_refreshandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroidx.constraintlayout.widget.ConstraintLayoutandroid:idid/mainandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentTextViewandroid:idid/tv_city_nameandroid:layout_widthwrap_contentandroid:layout_heightwrap_content/ScrollViewandroid:idid/scroll_viewandroid:layout_width0dpandroid:layout_height0dpandroid:fillViewporttrueandroid:overScrollModeneverandroid:scrollbarsnone!-- 天气正文内容 --/ScrollView/androidx.constraintlayout.widget.ConstraintLayout/androidx.swiperefreshlayout.widget.SwipeRefreshLayout2.2 必须遵守的两条布局规则规则一SwipeRefreshLayout 只能有一个直接子 View。它需要明确该监听哪个 View 的滚动。若内容由多个控件并列组成必须先包一层容器LinearLayout/ConstraintLayout/ScrollView/RecyclerView再作为唯一直接子 View直接放置两个及以上子 View运行期会抛出IllegalArgumentException: SwipeRefreshLayout can host only one direct child规则二子内容必须是可滚动的。下拉刷新手势的前提是子 View 已滚到顶部无法继续上滚后仍向下拉。若内容短于一屏、本身不能滚动手势不成立下拉不会有反应。所以内部通常使用ScrollView或RecyclerView。三、代码接入基本三步publicclassMainActivityextendsAppCompatActivity{privateSwipeRefreshLayoutswipeRefresh;OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);swipeRefreshfindViewById(R.id.swipe_refresh);// ① 注册刷新监听下拉松手后触发数据重新加载swipeRefresh.setOnRefreshListener(this::startWeather);}// ② 刷新方法亮出转圈 → 发起请求privatevoidstartWeather(){swipeRefresh.setRefreshing(true);// 显示转圈// ... 发起网络请求OkHttp 等...}// ③ 请求结束无论成功失败必须收圈privatevoidonDataLoaded(){swipeRefresh.setRefreshing(false);// 隐藏转圈// ... 更新 UI ...}}最容易犯的错误忘记第 ③ 步。如果请求失败路径没有调setRefreshing(false)转圈将永久显示用户会认为应用卡死。正确写法应把收圈逻辑放在成功与失败两个回调里都执行。忘记关闭的效果如下正确效果如下四、设置方法详解API4.1 状态控制方法说明setRefreshing(boolean refreshing)程序化控制转圈显隐。传true立即显示转圈且不播放进入动画常用于代码主动刷新传false隐藏isRefreshing()返回当前是否处于刷新状态setEnabled(boolean enabled)是否允许下拉刷新手势。常用于仅滚动到顶部时允许刷新isEnabled()当前是否允许下拉手势示例进入页面时自动刷新一次无需用户手势swipeRefresh.setRefreshing(true);startWeather();4.2 监听器设置①setOnRefreshListener(OnRefreshListener listener)刷新监听内部只定义了一个方法onRefresh()在下拉松手或程序触发时回调swipeRefresh.setOnRefreshListener(()-{// 在这里重新加载数据fetchWeather();});注意onRefresh()回调发生时转圈已经由控件自动显示不需要也不应在回调里手动调setRefreshing(true)回调只负责发起加载。②setOnRefreshListener(OnChildScrollUpCallback callback, OnRefreshListener listener)带是否可以上滚回调的版本OnChildScrollUpCallback用于自定义何时判定为已到顶部适用于内置判断不满足需求的场景swipeRefresh.setOnRefreshListener(newSwipeRefreshLayout.OnChildScrollUpCallback(){OverridepublicbooleancanChildScrollUp(SwipeRefreshLayoutparent,Viewchild){// 返回 true 表示子 View 还可以继续上滚此时不触发刷新returnchild.canScrollVertically(-1);}},()-fetchWeather());③setOnChildScrollUpCallback(OnChildScrollUpCallback callback)仅设置上滚判定回调不重复注册刷新监听可动态更换判定逻辑swipeRefresh.setOnChildScrollUpCallback((parent,child)-child.getScrollY()0);// 例如ScrollView 滚动过就禁止刷新4.3 外观定制① 转圈颜色 ——setColorSchemeColors(int... colors)设置转圈动画的颜色序列转圈会按传入颜色依次渐变swipeRefresh.setColorSchemeColors(ContextCompat.getColor(this,R.color.colorPrimary),ContextCompat.getColor(this,R.color.colorAccent),0xFFFF3333);② 转圈颜色 ——setColorSchemeResources(int... colorResIds)等价资源写法swipeRefresh.setColorSchemeResources(R.color.blue,R.color.green,R.color.orange);③ 转圈背景圆盘颜色转圈默认显示在白色圆盘上若界面背景为深色可自定义圆盘颜色swipeRefresh.setProgressBackgroundColorSchemeColor(ContextCompat.getColor(this,R.color.white));// 或资源写法swipeRefresh.setProgressBackgroundColorSchemeResource(R.color.white);④ 转圈尺寸 ——setSize(int size)SwipeRefreshLayout.SIZE_DEFAULT默认与SIZE_LARGE大号二选一swipeRefresh.setSize(SwipeRefreshLayout.SIZE_LARGE);改动效果如下4.4 转圈位置与触发距离下拉时转圈会跟随手指下移可通过偏移方法调整它的初始位置与跟随范围。① 设置转圈起始偏移 ——setProgressViewOffset(boolean scale, int start, int end)scale是否以缩放动画出现start转圈在父容器中的起始偏移通常设为触发下拉前的停留位置一般取一个屏幕高度如screenHeightPx让它在手指下拉时才出现end转圈完全出现后的结束偏移。intscreenHeightPxgetResources().getDisplayMetrics().heightPixels;swipeRefresh.setProgressViewOffset(false,screenHeightPx/3,screenHeightPx/4);② 设置转圈距离父容器顶部的初始距离 ——setProgressViewEndTarget(boolean scale, int end)只指定转圈出现后的最终位置swipeRefresh.setProgressViewEndTarget(false,dp2px(96));③ 下拉触发刷新的最小距离 ——setDistanceToTriggerSync(int distance)手指下拉超过该像素距离松手才触发刷新。默认值由系统计算大致为一个屏幕高度数值越小越灵敏swipeRefresh.setDistanceToTriggerSync(dp2px(120));④ 松手回弹的最大下拉距离 ——setSlingshotDistance(int distance)限制转圈在手指持续下拉时能跟随的最大位移AndroidX 1.1.0swipeRefresh.setSlingshotDistance(dp2px(80));辅助换算方法privateintdp2px(floatdp){return(int)(dp*getResources().getDisplayMetrics().density0.5f);}4.5 嵌套滚动SwipeRefreshLayout默认开启了嵌套滚动支持可被外层NestedScrollView/CoordinatorLayout正确协调。只有内容自身实现了复杂拖动逻辑如自绘RecyclerView拖拽排序时才可能需要关闭swipeRefresh.setNestedScrollingEnabled(false);五、进阶细节仅在滚动到顶部时允许刷新页面内容较长时用户滚到中间继续下拉预期是继续浏览而非刷新。若不加限制会打断浏览体验因此应只在内容处于顶部时开启刷新。对ScrollView监听滚动位置scrollView.setOnScrollChangeListener((v,scrollX,scrollY,oldScrollX,oldScrollY)-swipeRefresh.setEnabled(scrollY0));scrollY 0表示尚未向下滚动此时允许下拉刷新一旦滚离顶部立即禁用刷新把下拉手势交还给滚动。对RecyclerView利用 LayoutManager 判断首项是否完全可见recyclerView.addOnScrollListener(newRecyclerView.OnScrollListener(){OverridepublicvoidonScrolled(NonNullRecyclerViewrv,intdx,intdy){LinearLayoutManagerlm(LinearLayoutManager)rv.getLayoutManager();booleanatToplm!nulllm.findFirstCompletelyVisibleItemPosition()0;swipeRefresh.setEnabled(atTop);}});如果没有设置刷新条件会出现不能向上滑而只能刷新的情况六、常见问题问题原因解决运行期崩溃can host only one direct child直接子 View 多于一个先包一层容器只保留一个直接子 View转圈一直不消失刷新结束后没调setRefreshing(false)成功与失败回调里都要收圈内容滚到一半下拉也弹转圈未做顶部判断setEnabled(scrollY 0)或设置OnChildScrollUpCallback下拉没反应内部不是可滚动控件手势不成立换成ScrollView/RecyclerView转圈颜色与主题不搭未自定义setColorSchemeResources(...)深色背景看不到转圈白色圆盘与背景融为一体setProgressBackgroundColorSchemeResource(...)改圆盘颜色与CoordinatorLayout冲突/手势异常嵌套滚动协调问题检查setNestedScrollingEnabled(true)是否被误关七、小结下拉刷新的接入路径清晰稳定布局SwipeRefreshLayout唯一子 View 可滚动内容容器 ↓ 代码setOnRefreshListener 注册刷新回调 ↓ 刷新中转圈由控件自动显示setRefreshing(true) 可程序化触发 ↓ 结束setRefreshing(false) 必须覆盖成功与失败两条路径控件负责手势监听、转圈动画与状态机真正的业务复杂度网络请求、解析、UI 更新全部收在onRefresh回调里——这是 Android 官方把通用交互沉淀为标准控件的典型设计。在此基础上善用setColorSchemeResources、setDistanceToTriggerSync、OnChildScrollUpCallback等配置方法即可做出与产品风格一致、交互手感合适的下拉刷新体验。
返回列表