七爪源码:Jetpack Compose 刷卡刷新

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

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

这可以通过一个按钮来完成,但在某些情况下,更好的用户体验将是滑动刷新。 今天,我们将使用 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 {
        val list = mutableListOf()

        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 {
        val list = mutableListOf()

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

展开阅读全文

页面更新:2024-03-13

标签:组合   指示器   布尔   源码   屏幕   指标   状态   参数   项目   内容   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top