android compose PullToRefreshBox 下拉刷新 使用
使用 PullToRefreshBox 可组合项来实现下拉刷新,该可组合项充当可滚动内容的容器。以下关键参数可控制刷新行为和外观:
isRefreshing:一个布尔值,用于指示刷新操作是否正在进行。onRefresh:当用户发起刷新时执行的 lambda 函数。indicator:自定义系统在下拉刷新时绘制的指示器。
代码要点
- 上一个代码段使用了库提供的
Indicator。此代码段会创建一个名为MyCustomIndicator的自定义指示器可组合项。在此可组合项中,pullToRefreshIndicator修饰符负责处理定位和触发刷新。 - 与上一个代码段一样,此示例提取了
PullToRefreshState实例,因此您可以将同一实例传递给PullToRefreshBox和pullToRefreshModifier。 - 此示例使用了
PullToRefreshDefaults类中的容器颜色和位置阈值。这样一来,您就可以重复使用 Material 库中的默认行为和样式,同时仅自定义您感兴趣的元素。 MyCustomIndicator使用 Crossfade 在云图标和CircularProgressIndicator之间进行过渡。当用户下拉时,云图标会放大,并在刷新操作开始时转换为CircularProgressIndicator。targetState使用isRefreshing来确定要显示的状态(云图标或圆形进度指示器)。animationSpec定义了过渡的tween动画,并指定了时长CROSSFADE_DURATION_MILLIS。state.distanceFraction表示用户下拉的距离,范围从0f(未下拉)到1f(完全下拉)。graphicsLayer修饰符可修改缩放比例和透明度。

package com.wn.androidcomposedemo1.basegoogle
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshState
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.wn.androidcomposedemo1.ui.theme.AndroidComposeDemo1Theme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* Author : wn
* Email : maoning20080808@163.com
* Date : 2026/6/27 16:15
* Description : 下拉刷新
*/
class PullToRefreshActivity : ComponentActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AndroidComposeDemo1Theme(
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
PullToRefreshDemo()
}
}
}
}
@Composable
fun PullToRefreshDemo(){
Column() {
Spacer(Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text("下拉刷新例子", color = Color.Red, fontSize = 30.sp)
}
//刷新状态
var isRefreshing by remember { mutableStateOf(false) }
val items = remember { (1..100).map { "item $it" } }
PullToRefreshCustomIndicatorSample(items, isRefreshing, {
isRefreshing = true
CoroutineScope(Dispatchers.IO).launch {
//5秒后消失
delay(5000)
withContext(Dispatchers.Main){
isRefreshing = false
}
}
}, Modifier.fillMaxSize())
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PullToRefreshCustomIndicatorSample(
items : List<String>,
isRefreshing: Boolean,
onRefresh:() -> Unit,
modifier: Modifier
){
val state = rememberPullToRefreshState()
PullToRefreshBox(
isRefreshing = isRefreshing,
onRefresh = onRefresh,
modifier = modifier,
state = state,
indicator = {
MyCustomIndicator(
state = state,
isRefreshing = isRefreshing,
modifier = Modifier.align(Alignment.TopCenter).padding(top = 20.dp)
)
}
) {
LazyColumn(Modifier.fillMaxSize()) {
items(items){
ListItem({
Text(text = it)
})
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyCustomIndicator(
state : PullToRefreshState,
isRefreshing : Boolean,
modifier: Modifier
){
Box(
modifier = modifier,
contentAlignment = Alignment.Center
) {
Crossfade(
targetState = isRefreshing,
animationSpec = tween(durationMillis = 300),
modifier = Modifier.align(Alignment.Center)
) { refreshing ->
if(refreshing){
CircularProgressIndicator(Modifier.size(50.dp))
} else {
val distanceFraction = {state.distanceFraction.coerceIn(0f, 1f)}
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = "Refresh",
modifier = Modifier
.size(18.dp)
.graphicsLayer{
val progress = distanceFraction()
this.alpha = progress
this.scaleX = progress
this.scaleY = progress
}
)
}
}
}
}
}