在 Jetpack Compose 中 API 响应后更新 LazyColumn

Update LazyColumn after API response in Jetpack Compose

我对 Jetpack Compose 和 Kotlin 完全陌生,但对 Java 中的 Android 开发不是。想要第一次接触这两种技术,我想制作一个非常简单的应用程序,用来自 Dog API.

的图像填充 LazyColumn

所有 Retrofit 连接部分工作正常,因为我已经设法用随机小狗填充一张卡片,但是当需要填充列表时,这是不可能的。这是发生了什么:

  1. 界面已创建并显示白屏。
  2. 调用API。
  3. 等待大约 20 秒(大约有 400 张图像!)。
  4. dogImages 自动更新。
  5. 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
            )
        }
    }
}

提前致谢! :)

您的图像视图在加载之前无法确定纵横比,并且由于计算出的高度为零而未开始加载。有关详细信息,请参阅

还有一些关于您的代码的提示。

  1. MainActivity 中存储状态是不好的做法,您可以使用 view models。在视图模型中,您可以使用 viewModelScope,它将绑定到您的屏幕:所有任务将被取消,屏幕关闭时对象将被销毁。
  2. 您不应像使用 searchByName 那样直接从视图构造函数进行状态修改调用。这段代码在重构的时候可以调用很多次,所以你的调用会重复。您应该使用 side effects 执行此操作。在这种情况下,您可以使用 LaunchedEffect,但您也可以在 init 视图模型中执行此操作,因为它会在您的屏幕出现时创建。
  3. 将Modifier作为最后一个参数传递非常方便,这种情况下你不需要在末尾添加逗号,你可以轻松地add/remove修饰符。
  4. 您可能有很多可组合项,将它们全部存储在 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)
                }
            }
    }
}