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

如何修改原生SwipeRefreshLayout实现底部上拉刷新而无需引入第三方库

原生SwipeRefreshLayout修改实现上拉底部刷新方案

核心改造逻辑

无需对整个布局或者子View做旋转处理,仅需要修改3处核心逻辑即可实现:1. 子View滚动边界的判断逻辑反转;2. 触摸滑动方向的触发条件反转;3. 刷新指示器的位置调整到底部。

具体实现步骤

  • 第一步:自定义类继承SwipeRefreshLayout,重写滚动边界判断方法
    原生canChildScrollUp方法用来判断子View是否滚动到顶部,未滚动到顶部时拦截下拉事件,我们将其改为判断子View是否滚动到底部,未滚动到底部时拦截上拉事件:

    override fun canChildScrollUp(): Boolean {
        val childView = getChildAt(0) ?: return super.canChildScrollUp()
        // canScrollVertically(1) 返回true代表子View还能向下滚动,即未到达底部
        return childView.canScrollVertically(1)
    }
    
  • 第二步:调整刷新指示器的显示位置
    重写onLayout方法,将原生顶部的刷新指示器移动到布局底部,可选择旋转指示器适配上拉交互的视觉逻辑:

    init {
        // 旋转指示器,让箭头方向适配上拉操作
        val progressView = findViewById<View>(androidx.swiperefreshlayout.R.id.swipe_refresh_progress)
        progressView.rotation = 180f
    }
    
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        val progressView = findViewById<View>(androidx.swiperefreshlayout.R.id.swipe_refresh_progress)
        val params = progressView.layoutParams as MarginLayoutParams
        // 将指示器的topMargin设为布局高度减去自身高度,固定在底部
        params.topMargin = measuredHeight - progressView.measuredHeight
        progressView.layoutParams = params
    }
    
  • 第三步:修改触摸事件的滑动触发逻辑
    重写onTouchEvent方法,将原生向下滑动的触发判断反转,改为向上滑动距离超过阈值时触发刷新:

    private var initialDownY = 0f
    private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
    private var isBeingDragged = false
    
    override fun onTouchEvent(ev: MotionEvent): Boolean {
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                initialDownY = ev.y
                isBeingDragged = false
            }
            MotionEvent.ACTION_MOVE -> {
                val dy = initialDownY - ev.y
                if (dy > touchSlop && !isBeingDragged && !canChildScrollUp()) {
                    isBeingDragged = true
                }
                if (isBeingDragged) {
                    // 原生滑动距离计算逻辑反转,传入向上滑动的距离更新指示器进度
                    setProgressViewOffset(false, 0, (dy * 0.5f).toInt())
                    isRefreshing = dy > triggerDistance
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                isBeingDragged = false
            }
        }
        return isBeingDragged || super.onTouchEvent(ev)
    }
    

    以上代码为Kotlin版本,Java实现逻辑完全一致,仅语法存在差异。

注意事项

  • 原生setOnRefreshListener回调可直接使用,无需额外适配
  • 触发阈值直接复用SwipeRefreshLayout原生的triggerDistance变量,保证交互手感和原生下拉刷新一致
  • 该改造仅修改触发逻辑和指示器位置,不会影响内部子View的滚动、点击等交互逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 04:45:02