在 Jetpack Compose 中的 API 响应后更新 LazyColumn

xvl*_*aze 4 android lazy-loading kotlin retrofit2 android-jetpack-compose

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

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

  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
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

先感谢您!:)

Phi*_*hov 10

您的图像视图无法确定加载之前的纵横比,并且它不会开始加载,因为计算出的高度为零。请参阅此回复以获取更多信息。

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

  1. 在内部存储状态MainActivity是不好的做法,您可以使用视图模型。在视图模型中,您可以使用viewModelScope,它将绑定到您的屏幕:所有任务都将被取消,并且当屏幕关闭时对象将被销毁。
  2. 您不应该像使用searchByName. 该代码在重组过程中可能会被多次调用,因此您的调用将是重复的。你应该这样做,但会有副作用。在这种情况下,您可以使用LaunchedEffect,但您也可以在init视图模型中执行此操作,因为它将在屏幕出现时创建。
  3. 将修饰符作为最后一个参数传递非常方便,在这种情况下,您不需要在末尾添加逗号,并且可以轻松添加/删除修饰符。
  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 /sf/answers/4823587471/
                    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)
                }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @xvlaze你需要“导入androidx.lifecycle.viewmodel.compose.viewModel” (2认同)