在 Jetpack Compose 中 API 响应后更新 LazyColumn
Update LazyColumn after API response in Jetpack Compose
我对 Jetpack Compose 和 Kotlin 完全陌生,但对 Java 中的 Android 开发不是。想要第一次接触这两种技术,我想制作一个非常简单的应用程序,用来自 Dog API.
的图像填充 LazyColumn
所有 Retrofit 连接部分工作正常,因为我已经设法用随机小狗填充一张卡片,但是当需要填充列表时,这是不可能的。这是发生了什么:
- 界面已创建并显示白屏。
- 调用API。
- 等待大约 20 秒(大约有 400 张图像!)。
dogImages
自动更新。
- LazyColumn 再也不会重新组合,因此白屏保持原样。
你有什么想法吗?我找不到关于这个问题的任何教程,只是关于滚动监听状态的模糊解释。
这是我的代码:
class MainActivity : ComponentActivity() {
private val dogImages = mutableStateListOf<String>()
@ExperimentalCoilApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PuppyWallpapersTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
DogList(dogImages)
searchByName("poodle")
}
}
}
}
private fun getRetrofit():Retrofit {
return Retrofit.Builder()
.baseUrl("https://dog.ceo/api/breed/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private fun searchByName(query: String) {
CoroutineScope(Dispatchers.IO).launch {
val call = getRetrofit().create(APIService::class.java).getDogsByBreed("$query/images")
val puppies = call.body()
runOnUiThread {
if (call.isSuccessful) {
val images = puppies?.images ?: emptyList()
dogImages.clear()
dogImages.addAll(images)
}
}
}
}
@ExperimentalCoilApi
@Composable
fun DogList(dogs: SnapshotStateList<String>) {
LazyColumn() {
items(dogs) { dog ->
DogCard(dog)
}
}
}
@ExperimentalCoilApi
@Composable
fun DogCard(dog: String) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(15.dp),
elevation = 10.dp
) {
Image(
painter = rememberImagePainter(dog),
contentDescription = null
)
}
}
}
提前致谢! :)
您的图像视图在加载之前无法确定纵横比,并且由于计算出的高度为零而未开始加载。有关详细信息,请参阅 。
还有一些关于您的代码的提示。
- 在
MainActivity
中存储状态是不好的做法,您可以使用 view models。在视图模型中,您可以使用 viewModelScope
,它将绑定到您的屏幕:所有任务将被取消,屏幕关闭时对象将被销毁。
- 您不应像使用
searchByName
那样直接从视图构造函数进行状态修改调用。这段代码在重构的时候可以调用很多次,所以你的调用会重复。您应该使用 side effects 执行此操作。在这种情况下,您可以使用 LaunchedEffect
,但您也可以在 init
视图模型中执行此操作,因为它会在您的屏幕出现时创建。
- 将Modifier作为最后一个参数传递非常方便,这种情况下你不需要在末尾添加逗号,你可以轻松地add/remove修饰符。
- 您可能有很多可组合项,将它们全部存储在
MainActivity
中不是很方便。一个好的做法是将它们简单地存储在一个文件中,并按文件逻辑将它们分开。
您的代码可以更新为:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PuppyWallpapersTheme {
DogsListScreen()
}
}
}
}
@Composable
fun DogsListScreen(
// pass the view model in this form for convenient testing
viewModel: DogsModel = viewModel()
) {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
DogList(viewModel.dogImages)
}
}
@Composable
fun DogList(dogs: SnapshotStateList<String>) {
LazyColumn {
items(dogs) { dog ->
DogCard(dog)
}
}
}
@Composable
fun DogCard(dog: String) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(15.dp),
elevation = 10.dp
) {
Image(
painter = rememberImagePainter(
data = dog,
builder = {
// don't use it blindly, it can be tricky.
// check out
size(OriginalSize)
},
),
contentDescription = null,
)
}
}
class DogsModel : ViewModel() {
val dogImages = mutableStateListOf<String>()
init {
searchByName("poodle")
}
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://dog.ceo/api/breed/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private fun searchByName(query: String) {
viewModelScope
.launch {
val call = getRetrofit()
.create(APIService::class.java)
.getDogsByBreed("$query/images")
val puppies = call.body()
if (call.isSuccessful) {
val images = puppies?.images ?: emptyList()
dogImages.clear()
dogImages.addAll(images)
}
}
}
}
我对 Jetpack Compose 和 Kotlin 完全陌生,但对 Java 中的 Android 开发不是。想要第一次接触这两种技术,我想制作一个非常简单的应用程序,用来自 Dog API.
的图像填充 LazyColumn所有 Retrofit 连接部分工作正常,因为我已经设法用随机小狗填充一张卡片,但是当需要填充列表时,这是不可能的。这是发生了什么:
- 界面已创建并显示白屏。
- 调用API。
- 等待大约 20 秒(大约有 400 张图像!)。
dogImages
自动更新。- LazyColumn 再也不会重新组合,因此白屏保持原样。
你有什么想法吗?我找不到关于这个问题的任何教程,只是关于滚动监听状态的模糊解释。
这是我的代码:
class MainActivity : ComponentActivity() {
private val dogImages = mutableStateListOf<String>()
@ExperimentalCoilApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PuppyWallpapersTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
DogList(dogImages)
searchByName("poodle")
}
}
}
}
private fun getRetrofit():Retrofit {
return Retrofit.Builder()
.baseUrl("https://dog.ceo/api/breed/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private fun searchByName(query: String) {
CoroutineScope(Dispatchers.IO).launch {
val call = getRetrofit().create(APIService::class.java).getDogsByBreed("$query/images")
val puppies = call.body()
runOnUiThread {
if (call.isSuccessful) {
val images = puppies?.images ?: emptyList()
dogImages.clear()
dogImages.addAll(images)
}
}
}
}
@ExperimentalCoilApi
@Composable
fun DogList(dogs: SnapshotStateList<String>) {
LazyColumn() {
items(dogs) { dog ->
DogCard(dog)
}
}
}
@ExperimentalCoilApi
@Composable
fun DogCard(dog: String) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(15.dp),
elevation = 10.dp
) {
Image(
painter = rememberImagePainter(dog),
contentDescription = null
)
}
}
}
提前致谢! :)
您的图像视图在加载之前无法确定纵横比,并且由于计算出的高度为零而未开始加载。有关详细信息,请参阅
还有一些关于您的代码的提示。
- 在
MainActivity
中存储状态是不好的做法,您可以使用 view models。在视图模型中,您可以使用viewModelScope
,它将绑定到您的屏幕:所有任务将被取消,屏幕关闭时对象将被销毁。 - 您不应像使用
searchByName
那样直接从视图构造函数进行状态修改调用。这段代码在重构的时候可以调用很多次,所以你的调用会重复。您应该使用 side effects 执行此操作。在这种情况下,您可以使用LaunchedEffect
,但您也可以在init
视图模型中执行此操作,因为它会在您的屏幕出现时创建。 - 将Modifier作为最后一个参数传递非常方便,这种情况下你不需要在末尾添加逗号,你可以轻松地add/remove修饰符。
- 您可能有很多可组合项,将它们全部存储在
MainActivity
中不是很方便。一个好的做法是将它们简单地存储在一个文件中,并按文件逻辑将它们分开。
您的代码可以更新为:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PuppyWallpapersTheme {
DogsListScreen()
}
}
}
}
@Composable
fun DogsListScreen(
// pass the view model in this form for convenient testing
viewModel: DogsModel = viewModel()
) {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
DogList(viewModel.dogImages)
}
}
@Composable
fun DogList(dogs: SnapshotStateList<String>) {
LazyColumn {
items(dogs) { dog ->
DogCard(dog)
}
}
}
@Composable
fun DogCard(dog: String) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(15.dp),
elevation = 10.dp
) {
Image(
painter = rememberImagePainter(
data = dog,
builder = {
// don't use it blindly, it can be tricky.
// check out
size(OriginalSize)
},
),
contentDescription = null,
)
}
}
class DogsModel : ViewModel() {
val dogImages = mutableStateListOf<String>()
init {
searchByName("poodle")
}
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://dog.ceo/api/breed/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private fun searchByName(query: String) {
viewModelScope
.launch {
val call = getRetrofit()
.create(APIService::class.java)
.getDogsByBreed("$query/images")
val puppies = call.body()
if (call.isSuccessful) {
val images = puppies?.images ?: emptyList()
dogImages.clear()
dogImages.addAll(images)
}
}
}
}