关于如何在加载项目时实现滑动刷新功能以及占位符的简短指南

七爪是什么(七爪源码JetpackCompose)(1)

今天,许多应用程序都有需要在某个时候刷新的数据。 您可以在一段时间后刷新数据或使用套接字来始终拥有最新的数据,但是如果您想要允许用户开始刷新数据的功能怎么办?

这可以通过一个按钮来完成,但在某些情况下,更好的用户体验将是滑动刷新。 今天,我们将使用 Accompanist 库来实现它。

滑动刷新

首先,让我们添加一个依赖项:

implementation "com.google.accompanist:accompanist-swiperefresh:0.25.1"

注意:检查是否有此依赖项的更新版本。

接下来是创建一个简单的ViewModel,它将保存我们的数据和刷新逻辑。 在这里,项目将包含随机图像和数字。 这是它的样子:

class MainViewModel : ViewModel() { private val _isRefreshing = MutableStateFlow(false) val isRefreshing = _isRefreshing.asStateFlow() private val _currentTime = MutableStateFlow(Instant.now()) val currentTime = _currentTime.asStateFlow() private val _items = MutableStateFlow(generateItems()) val items = _items.asStateFlow() fun refresh() = viewModelScope.launch { _isRefreshing.update { true } // Simulate API call delay(2000) _currentTime.value = Instant.now() _items.value = generateItems() _isRefreshing.update { false } } private fun generateItems(): List<RowItem> { val list = mutableListOf<RowItem>() for (i in 1 until 20) { list.add( RowItem( rowImage = randomImage(), number = Random.nextInt(1, 1000) ) ) } return list } private fun randomImage( seed: Int = (0..100000).random(), width: Int = 300, height: Int = width, ): String { return "https://picsum.photos/seed/$seed/$width/$height" } } data class RowItem( val rowImage: String = "", val number: Int = -1 )

isRefreshing 是一个布尔值,我们将在 swipeRefreshState 中使用它,我们将在后面解释。 items 只是包含随机图像和数字的 20 个项目的列表。

现在,让我们创建我们的屏幕:

@Composable fun MainScreen( viewModel: MainViewModel = viewModel() ) { val isRefreshing = viewModel.isRefreshing.collectAsState().value val currentTime = viewModel.currentTime.collectAsState().value val items = viewModel.items.collectAsState().value val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing) SwipeRefresh( state = swipeRefreshState, onRefresh = viewModel::refresh, modifier = Modifier .fillMaxSize() .padding( vertical = 32.dp, horizontal = 16.dp ) ) { Column { Text( text = "Welcome to Swipe-to-Refresh!", style = MaterialTheme.typography.h5, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(32.dp)) Text( text = currentTime.toString(), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.End ) Spacer(modifier = Modifier.height(8.dp)) LazyColumn { items(items) { Item( rowItem = it ) } } } } } @Composable fun Item( rowItem: RowItem ) { Card( modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), elevation = 4.dp ) { Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = Alignment.CenterVertically, ) { Image( painter = rememberAsyncImagePainter(rowItem.rowImage), contentDescription = rowItem.number.toString(), modifier = Modifier.size(64.dp) ) Spacer(modifier = Modifier.width(16.dp)) Text(text = "Number: ${rowItem.number}") } } }

我们正在收集我们的状态并使用 isRefreshing 的值创建 swipeRefreshState。我们将这个状态传递给 SwipeRefresh,但如果需要,我们也可以访问它的属性 isRefreshing 和 isSwipeInProgress。在 SwipeRefresh 中,我们有一个标题、当前时间和项目列表。每行项目只显示一个图像和数字。

SwipeRefresh 具有三个强制参数:

一些有趣的可选参数是:

对于指标参数,您可以创建自己的可组合项,但该库为我们提供了 SwipeRefreshIndicator,这是我们可以使用的非常好的可组合项。

它需要两个参数:

一些可选参数是:

还有更多参数,但不需要全部遍历。如果您想了解更多信息,请务必在官方文档中查看。

这就是 SwipeRefresh 的全部内容,现在让我们实现占位符,这是来自 Accompanist 的另一个不错的库。

占位符

通常,项目的加载由某种加载微调器显示。另一种显示项目正在加载的方法是使用占位符。

Accompanist 创建了一个库,为我们提供了用于显示占位符的修饰符。实际上有两个占位符库。一个是基础,另一个是材料。建议我们使用 Material,但可以随意使用您需要的任何东西。没有太大区别,API 大多是等价的。在本博客中,我们使用的是 Material。所以,让我们用这个命令导入它:

implementation "com.google.accompanist:accompanist-placeholder-material:0.25.1"

注意:检查是否有此依赖项的更新版本。

在继续 MainScreen 之前,让我们快速编辑 MainViewModel。 添加 init 和 isLoading StateFlow。 此外,使用 20 个默认 RowItem 初始化项目。

private val _items = MutableStateFlow(List(size = 20) { RowItem() }) val items = _items.asStateFlow() private val _isLoading = MutableStateFlow(true) val isLoading = _isLoading.asStateFlow() init { viewModelScope.launch { delay(2000) _items.value = generateItems() _isLoading.value = false } }

我们的 ViewModel 现在看起来像这样:

class MainViewModel : ViewModel() { private val _isRefreshing = MutableStateFlow(false) val isRefreshing = _isRefreshing.asStateFlow() private val _currentTime = MutableStateFlow(Instant.now()) val currentTime = _currentTime.asStateFlow() private val _items = MutableStateFlow(List(size = 20) { RowItem() }) val items = _items.asStateFlow() private val _isLoading = MutableStateFlow(true) val isLoading = _isLoading.asStateFlow() init { viewModelScope.launch { delay(2000) _items.value = generateItems() _isLoading.value = false } } fun refresh() = viewModelScope.launch { _isRefreshing.update { true } // Simulate API call delay(2000) _currentTime.value = Instant.now() _items.value = generateItems() _isRefreshing.update { false } } private fun generateItems(): List<RowItem> { val list = mutableListOf<RowItem>() for (i in 1 until 20) { list.add( RowItem( rowImage = randomSampleImageUrl(), number = Random.nextInt(1, 1000) ) ) } return list } private fun randomSampleImageUrl( seed: Int = (0..100000).random(), width: Int = 300, height: Int = width, ): String { return "https://picsum.photos/seed/$seed/$width/$height" } } data class RowItem( val rowImage: String = "", val number: Int = -1 )

接下来是在我们的屏幕中收集 isLoading,然后将其用作我们的占位符。 我们正在向 Item 可组合项添加一个新参数 childModifier:Modifier。

Item( rowItem = it, childModifier = Modifier.placeholder( visible = isLoading, highlight = PlaceholderHighlight.fade(), ) )

如您所见,该库为占位符提供了一个修饰符。必需的参数是可见的:布尔值,它确定是否应显示占位符或内容。如果 visible 为真,那么将有一个占位符来填充应用它的可组合项的大小,而不是内容。

可选参数有:

我们的 MainScreen 现在看起来像这样:

@Composable fun MainScreen( viewModel: MainViewModel = viewModel() ) { val isRefreshing = viewModel.isRefreshing.collectAsState().value val isLoading = viewModel.isLoading.collectAsState().value val currentTime = viewModel.currentTime.collectAsState().value val items = viewModel.items.collectAsState().value val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing) SwipeRefresh( state = swipeRefreshState, onRefresh = viewModel::refresh, modifier = Modifier .fillMaxSize() .padding( vertical = 32.dp, horizontal = 16.dp ) ) { LazyColumn { item { Text( text = "Welcome to Swipe-to-Refresh!", style = MaterialTheme.typography.h5, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(32.dp)) Text( text = currentTime.toString(), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.End ) Spacer(modifier = Modifier.height(8.dp)) } items(items) { Item( rowItem = it, childModifier = Modifier.placeholder( visible = isLoading, highlight = PlaceholderHighlight.fade(), ) ) } } } } @Composable fun Item( rowItem: RowItem, childModifier: Modifier = Modifier, ) { Card( modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp), elevation = 4.dp ) { Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = Alignment.CenterVertically, ) { Image( painter = rememberAsyncImagePainter(rowItem.rowImage), contentDescription = rowItem.number.toString(), modifier = childModifier.size(64.dp) ) Spacer(modifier = Modifier.width(16.dp)) Text( text = "Number: ${rowItem.number}", modifier = childModifier.fillMaxWidth() ) } } }

就这样。 我希望你喜欢它。

关注七爪网,获取更多APP/小程序/网站源码资源!

,