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

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

七爪是什么(七爪源码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 具有三个强制参数:

  • state: SwipeRefreshState — 可以提升以控制和观察 SwipeRefresh 更改的状态对象
  • onRefresh: () -> Unit — 完成滑动刷新手势时调用的 Lambda
  • content: @Composable () -> Unit — 包含可组合滚动的内容

一些有趣的可选参数是:

  • swipeEnabled: Boolean - 布局是否应该对滑动手势做出反应
  • refreshTriggerDistance: Dp — 触发刷新的最小滑动距离
  • indicatorAlignment: Alignment — 指标的对齐方式。默认为 Alignment.TopCenter
  • indicatorPadding: PaddingValues — 指标的内容填充,如果需要可以插入指标
  • indicator:@Composable (state: SwipeRefreshState, refreshTrigger: Dp) — 表示当前状态的指标。默认情况下,这将使用 SwipeRefreshIndicator

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

它需要两个参数:

  • state: SwipeRefreshState — 传递到 SwipeRefresh 指示器块的 SwipeRefreshState
  • refreshTriggerDistance: Dp — 触发刷新的最小滑动距离

一些可选参数是:

  • fade: Boolean — 箭头在滚动时是否应该淡入/淡出,默认为 true
  • scale: Boolean — 指示器在滚动时是否应按比例放大/缩小,默认为 false
  • arrowEnabled: Boolean — 是否应在指标上绘制箭头,默认为 true
  • backgroundColor: Color — 指示器背景表面的颜色

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

这就是 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 为真,那么将有一个占位符来填充应用它的可组合项的大小,而不是内容。

可选参数有:

  • color: Color — 用于绘制占位符 UI 的颜色。如果提供了 Color.Unspecified,则占位符将使用 PlaceholderDefaults.color
  • shape: Shape — 占位符的所需形状。如果提供 null,占位符将使用 MaterialTheme.shapes 中设置的小形状
  • highlight: PlaceholderHighlight — 可选的高亮动画。有两个预先创建的占位符动画,淡入淡出和微光
  • placeholderFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> — 将占位符淡入/淡出屏幕时使用的转换规范。为过渡定义的布尔参数可见
  • contentFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> — 将内容淡入/淡出屏幕时使用的转换规范。为过渡定义的布尔参数可见

我们的 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/小程序/网站源码资源!

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页