LazyColumn повторно компонуется при выборе для передачи URL в AsyncImage

Вопрос или проблема

Новичок в Jetpack Compose, может кто-то объяснить, почему, когда я выбираю элемент в LazyRow и передаю этот URL для изображения, вся строка снова перестраивается? Как мне следует использовать переменную movieSelector?

@Composable
fun MovieHorizontalCarousel(viewModel: MovieViewModel = MovieViewModel()) {
    val movies: LazyPagingItems<Movie> = viewModel.movies.collectAsLazyPagingItems()

    Column(
        modifier = Modifier
            .fillMaxSize()
    ) {

        val movieSelector = remember { mutableStateOf("") }
        Carousel(
            movies = movies,
            onMovieSelected = { url -> movieSelector.value = url },
            Modifier
                .fillMaxWidth()
                .height(100.dp)
        )

        MoviePreview(
            movieSelector.value,
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f)
        )
    }

}

@Composable
fun MoviePreview(url: String,
                 modifier: Modifier = Modifier) {
    AsyncImage(
        modifier = modifier
            .fillMaxSize()
            .background(Purple40),
        model = url,
        contentDescription = "Предпросмотр"
    )
}

@Composable
fun Carousel(movies: LazyPagingItems<Movie>,
             onMovieSelected: (String) -> Unit,
             modifier: Modifier = Modifier) {

    LazyRow(
        modifier = modifier
            .wrapContentSize()
            .background(Purple80),
        //horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        items (
            count = movies.itemCount,
            key = { index -> movies[index]?.id ?: index }
            //key = movies.itemKey{ item -> item.id }
        ) { index ->
            movies[index]?.let {
                MovieCarouselItem(movie = it,
                                  onMovieSelected = onMovieSelected)
            }
        }
    }
}

@Composable
fun MovieCarouselItem (movie: Movie,
                       onMovieSelected: (String) -> Unit,
                       modifier: Modifier = Modifier) {

    AsyncImage(
        modifier = modifier
            .size(81.dp)
            .clickable {
                onMovieSelected("https://image.tmdb.org/t/p/w500${movie.posterPath}")
            },
        model = "https://image.tmdb.org/t/p/w500${movie.posterPath}",
        contentDescription = "Миниатюра фильма",
        contentScale = ContentScale.Crop,
        placeholder = painterResource(id = R.drawable.photo)
    )
}

Я пытаюсь отобразить изображение, которое пользователь выбрал из lazyrow, изображение обновляется, но также перестраивается список фильмов.

Ответ или решение

Оптимизация работы с LazyColumn в Jetpack Compose: Проблема с переработкой компонента при выборе

Когда вы работаете с Jetpack Compose, особенно с компонентами, такими как LazyRow, важно понимать основные принципы работы с состоянием и переработкой (recomposition). В вашем случае проблема возникает из-за того, что при выборе элемента в LazyRow происходит переработка всего списка, а не только выбранного элемента. Давайте рассмотрим, как можно оптимизировать этот процесс.

Понимание проблемы

В приведенном вами коде вы используете mutableStateOf для хранения текущего выбранного URL в переменной movieSelector. Это приводит к тому, что каждый раз, когда вы обновляете значение movieSelector, произойдет переработка всей композиции, связанной с MovieHorizontalCarousel, включая все элементы LazyRow. Реактивные компоненты Jetpack Compose, такие как LazyRow, на самом деле пересчитывают все свои дочерние элементы при изменении состояния, если не указаны уникальные ключи для каждого элемента.

Как избежать лишней переработки

  1. Используйте ключи в LazyRow:
    Первое, что вы можете сделать для оптимизации, это задать уникальные ключи для каждого элемента в LazyRow. Это позволяет Compose следить за каждым элементом списка индивидуально и избежать переработки всего списка. Вы уже используете параметр key в вашем items, однако подумайте о том, как вы определяете ключ для каждого элемента.

  2. Измените состояние только для нужных компонентов:
    Вместо того чтобы использовать mutableStateOf для хранения URL, попробуйте использовать remember для хранения состояния в MoviePreview и передавайте URL только туда, где он нужен. Это предотвратит ненужные переработки.

  3. Оптимизация MoviePreview:
    MoviePreview должен быть отдельным составным компонентом, который реагирует только на изменения URL. Давайте обновим ваш код:

@Composable
fun MovieHorizontalCarousel(viewModel: MovieViewModel = MovieViewModel()) {
    val movies: LazyPagingItems<Movie> = viewModel.movies.collectAsLazyPagingItems()
    var selectedMoviePoster by remember { mutableStateOf("") }

    Column(modifier = Modifier.fillMaxSize()) {
        Carousel(
            movies = movies,
            onMovieSelected = { url -> selectedMoviePoster = url },
            modifier = Modifier
                .fillMaxWidth()
                .height(100.dp)
        )

        MoviePreview(
            url = selectedMoviePoster,
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f)
        )
    }
}
  1. Иммунитет к изменениям состояния:
    Применяя состояния и ключи правильно, вы сможете собрать элементы по отдельности, так что каждый элемент в LazyRow будет уметь независимо реагировать на изменения, что будет означать более быструю и высокоэффективную работу.

Заключение

Состояние компонентов в Jetpack Compose должно быть отслеживаемым и правильно структурированным для обеспечения быстрой и точной переработки. Избегайте глобального изменения состояния и используйте локальные изменения для управления отдельными компонентами. Также не забудьте про ключи для многократного использования компонентов с одинаковыми параметрами.

Применив предложенные выше рекомендации, вы значительно улучшите производительность вашего приложения и уменьшите количество лишних переработок.

Оцените материал
Добавить комментарий

Капча загружается...