kotlin下拉刷新在fragment中使用

xzbxzb 安卓 2026-01-04 113 0
在 Kotlin 的 Fragment 中实现下拉刷新功能,Android 官方推荐使用 SwipeRefreshLayout 组件来完成,它能完美适配 Fragment 场景,下面我将详细讲解完整的实现步骤和代码示例。

一、核心组件说明

实现下拉刷新的核心组件是 androidx.swiperefreshlayout.widget.SwipeRefreshLayout(属于 AndroidX 库,需确保项目已引入 AndroidX 依赖),它通过包裹可滚动视图(如 RecyclerView、ScrollView)来提供下拉刷新能力,且对 Fragment 的生命周期兼容性极佳。

二、步骤 1:编写 Fragment 布局文件

布局中需以 SwipeRefreshLayout 作为根布局(或外层布局),它只能包含一个直接子视图(通常是 RecyclerView,用于展示刷新的数据列表),示例布局如下(fragment_refresh.xml):
<?xml version="1.0" encoding="utf-8"?>
<!-- 下拉刷新核心布局:SwipeRefreshLayout -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 可滚动子视图:RecyclerView(唯一直接子视图) -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp"/>

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

三、步骤 2:Fragment 代码实现(Kotlin)

使用 ViewBinding 简化视图操作,核心步骤包括:初始化控件、设置刷新监听、异步处理刷新逻辑、停止刷新动画,完整代码如下:

1. 基础配置(启用 ViewBinding,可选但推荐)

若未启用 ViewBinding,需在模块 build.gradle(Module 级别)中添加配置:
android {
    // 其他配置...
    buildFeatures {
        viewBinding true // 启用ViewBinding
    }
}

2. Fragment 完整代码

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.xxx.databinding.FragmentRefreshBinding // 替换为你的包名

// 下拉刷新Fragment
class RefreshFragment : Fragment() {
    // ViewBinding实例
    private var _binding: FragmentRefreshBinding? = null
    private val binding get() = _binding!!

    // RecyclerView适配器(简单字符串适配器,可自定义)
    private lateinit var mAdapter: SimpleStringAdapter
    // 模拟数据列表
    private val dataList = mutableListOf<String>()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // 初始化ViewBinding
        _binding = FragmentRefreshBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 1. 初始化RecyclerView
        initRecyclerView()
        // 2. 初始化下拉刷新控件
        initSwipeRefreshLayout()
        // 3. 初始化模拟数据
        initData()
    }

    /**
     * 初始化RecyclerView
     */
    private fun initRecyclerView() {
        // 初始化适配器
        mAdapter = SimpleStringAdapter(dataList)
        // 设置布局管理器
        binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
        // 设置适配器
        binding.recyclerView.adapter = mAdapter
    }

    /**
     * 初始化下拉刷新控件
     */
    private fun initSwipeRefreshLayout() {
        // 可选:设置刷新圈颜色(支持多个颜色,循环显示)
        binding.swipeRefreshLayout.setColorSchemeResources(
            android.R.color.holo_blue_light,
            android.R.color.holo_red_light,
            android.R.color.holo_green_light
        )
        // 可选:设置刷新圈大小(DEFAULT / LARGE)
        binding.swipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE)

        // 3. 设置下拉刷新监听(核心)
        binding.swipeRefreshLayout.setOnRefreshListener {
            // 触发下拉刷新时,执行刷新逻辑
            performRefresh()
        }
    }

    /**
     * 执行刷新逻辑(核心:异步处理,避免阻塞主线程)
     */
    private fun performRefresh() {
        // 使用协程异步处理(Kotlin推荐,也可使用Thread+Handler)
        CoroutineScope(Dispatchers.IO).launch {
            // 模拟耗时操作:如网络请求、数据库查询(延迟2秒模拟耗时)
            delay(2000)
            // 刷新数据(模拟新增数据)
            val newData = "新增数据${System.currentTimeMillis() / 1000}"
            dataList.add(0, newData) // 插入到列表头部

            // 切换到主线程更新UI(RecyclerView适配器刷新)
            withContext(Dispatchers.Main) {
                mAdapter.notifyItemInserted(0) // 局部刷新
                binding.recyclerView.scrollToPosition(0) // 滚动到顶部

                // 4. 停止刷新动画(必须调用,否则刷新圈会一直转动)
                binding.swipeRefreshLayout.isRefreshing = false
            }
        }
    }

    /**
     * 初始化模拟数据
     */
    private fun initData() {
        for (i in 1..20) {
            dataList.add("原始数据$i")
        }
        mAdapter.notifyDataSetChanged()
    }

    /**
     * 销毁视图时,释放ViewBinding,避免内存泄漏
     */
    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    /**
     * 简单的RecyclerView字符串适配器(自定义适配器可替换)
     */
    inner class SimpleStringAdapter(private val data: List<String>) :
        androidx.recyclerview.widget.RecyclerView.Adapter<SimpleStringAdapter.ViewHolder>() {

        inner class ViewHolder(itemView: View) :
            androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
            val tvContent: android.widget.TextView = itemView as android.widget.TextView
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            // 创建TextView作为列表项
            val textView = android.widget.TextView(requireContext())
            textView.layoutParams = androidx.recyclerview.widget.RecyclerView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
            )
            textView.textSize = 16f
            textView.setPadding(0, 16, 0, 16)
            return ViewHolder(textView)
        }

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.tvContent.text = data[position]
        }

        override fun getItemCount(): Int = data.size
    }
}

四、关键注意事项

  1. SwipeRefreshLayout 的子视图限制:它只能有一个直接子视图(如 RecyclerView、ScrollView),若需多个视图,需先使用 LinearLayout/RelativeLayout 等包裹后再作为其子视图。

  2. 必须停止刷新动画:刷新逻辑完成后,务必调用 swipeRefreshLayout.isRefreshing = false,否则刷新圈会持续转动,影响用户体验。

  3. 刷新逻辑异步化:耗时操作(网络请求、数据库查询等)必须放在子线程 / 协程中执行,避免阻塞主线程导致 ANR,上述示例使用 Kotlin 协程(Dispatchers.IO)实现异步,更简洁高效。

  4. ViewBinding 内存泄漏防护:在 onDestroyView 中将 _binding 置为 null,避免 Fragment 视图销毁后仍持有引用导致内存泄漏。

  5. 样式自定义:可通过 setColorSchemeResources(设置刷新圈颜色)、setSize(设置刷新圈大小)、setProgressBackgroundColorSchemeResource(设置刷新圈背景色)自定义下拉刷新样式。

五、运行效果

  1. Fragment 加载后,显示原始数据列表;

  2. 下拉列表时,会触发刷新圈动画;

  3. 等待 2 秒(模拟耗时操作)后,列表头部插入新数据并自动滚动到顶部;

  4. 刷新圈自动停止动画,完成下拉刷新流程。


 您阅读本篇文章共花了: 

版权声明

本文章如果涉及侵权,请联系我。
部分文章系本人原创未经许可,不得转载。

喜欢0发布评论

评论列表

发表评论

  • 昵称(必填)
  • 邮箱
  • 网址