一、核心组件说明
二、步骤 1:编写 Fragment 布局文件
<?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)
1. 基础配置(启用 ViewBinding,可选但推荐)
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
}
}四、关键注意事项
SwipeRefreshLayout 的子视图限制:它只能有一个直接子视图(如 RecyclerView、ScrollView),若需多个视图,需先使用 LinearLayout/RelativeLayout 等包裹后再作为其子视图。
必须停止刷新动画:刷新逻辑完成后,务必调用 swipeRefreshLayout.isRefreshing = false,否则刷新圈会持续转动,影响用户体验。
刷新逻辑异步化:耗时操作(网络请求、数据库查询等)必须放在子线程 / 协程中执行,避免阻塞主线程导致 ANR,上述示例使用 Kotlin 协程(Dispatchers.IO)实现异步,更简洁高效。
ViewBinding 内存泄漏防护:在 onDestroyView 中将 _binding 置为 null,避免 Fragment 视图销毁后仍持有引用导致内存泄漏。
样式自定义:可通过 setColorSchemeResources(设置刷新圈颜色)、setSize(设置刷新圈大小)、setProgressBackgroundColorSchemeResource(设置刷新圈背景色)自定义下拉刷新样式。
五、运行效果
Fragment 加载后,显示原始数据列表;
下拉列表时,会触发刷新圈动画;
等待 2 秒(模拟耗时操作)后,列表头部插入新数据并自动滚动到顶部;
刷新圈自动停止动画,完成下拉刷新流程。
版权声明
本文章如果涉及侵权,请联系我。
部分文章系本人原创未经许可,不得转载。



蒙公网安备 15090202000037号
评论列表
发表评论