kotlin Jetpack编写-确保从详细信息屏幕导航回来后列表位于正确位置

z9smfwbn  于 2023-08-06  发布在  Kotlin
关注(0)|答案(1)|浏览(134)

我的Android应用程序是使用Jetpack Compose和MVVM架构编写的。这是一个基本的电影列表应用程序利用TMDB API。
该应用程序使用带有两列磁贴的LazyVerticalGrid显示项目列表。用户将滚动列表并单击其中一个,此时将导航到详细信息页面。我用的是NavController。这是一个独立的屏幕,而不是一个底表对话(虽然我正在考虑改变到这一点)。
导航是通过MainActivity处理的,每个屏幕都是一个单独的Screen文件,适当时使用viewModel和存储库。
用户滚动,假设他向下滚动了三对行并点击了第7项。当用户完成详细信息屏幕时,他/她将返回到前一个屏幕,即列表。我遇到的问题是这个屏幕被重绘,这意味着它会向云重新发送一个请求并重绘,用户被返回到列表的顶部。
我看不出这是什么原因,但我=。任何想法,请就如何解决这个问题,或者是我唯一的选择,记住最后滚动到索引,然后重新滚动到它?
这是MovieListScreen

@Composable
fun MovieListScreen(movieDetailsNavigationCallback: (Int) -> Unit) {
    val viewModel: MovieListViewModel = hiltViewModel()

    val movieDataList = viewModel.getMovieListPage().collectAsLazyPagingItems()

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(
                brush = Brush.verticalGradient(
                    colors = listOf(
                        SplashGradientStart,
                        SplashGradientEnd
                    )
                )
            ),
        contentAlignment = Alignment.TopCenter
    ) {
        VerticalGridButtons(movieDataList = movieDataList, movieDetailsNavigationCallback)
    }
}

@Composable
fun VerticalGridButtons(
    movieDataList: LazyPagingItems<MovieData>,
    navigationCallback: (Int) -> Unit,
) {
    LazyVerticalGrid(
        columns = GridCells.Fixed(2),
        contentPadding = PaddingValues(
            start = 8.dp,
            end = 8.dp,
            bottom = 8.dp,
            top = 100.dp
        ),
    ) {
        items(
            movieDataList.itemCount
        ) { index ->
            movieDataList[index]?.let { MenuItemTile(it, navigationCallback) }
        }
    }

    // TODO: Add error handling
}

@Composable
fun MenuItemTile(movieData: MovieData, navigationCallback: (Int) -> Unit) {
    Card(
        Modifier
            .padding(8.dp)
            .background(Color.Black.copy(alpha = 0.4f)),
        shape = RoundedCornerShape(8.dp),
        elevation = CardDefaults.cardElevation(
            defaultElevation = 8.dp
        ),
        colors = CardDefaults.cardColors(containerColor = Color.White)
    ) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier
                .padding(4.dp)
                .background(color = Color.Black.copy(alpha = 1.0f))
                .fillMaxWidth(),
        ) {
            AsyncImage(
                model = BASE_URL + movieData.posterPath,
                contentDescription = "Menu Thumbnail",
                contentScale = ContentScale.FillBounds,
                modifier = Modifier
                    .size(250.dp)
                    .padding(top = 0.dp)
                    .align(Alignment.CenterHorizontally)
                    .clickable(onClick = {
                        Timber.d("onClick event for movie ID == " + movieData.id)
                        navigationCallback(movieData.id)
                    }
                )
            )

            Spacer(modifier = Modifier.height(height = 15.dp))

            val movieTitleScroll = rememberScrollState(0)
            Text(modifier = Modifier
                .align(Alignment.CenterHorizontally)
                .padding(bottom = 10.dp)
                .horizontalScroll(movieTitleScroll),
                text = movieData.originalTitle,
                style = MaterialTheme.typography.displaySmall,
                color = Color.White,
                maxLines = 1
            )
        }
    }
}

@Preview(showBackground = true)
@Composable
fun MovieListScreenPreview() {
    DisneyMoviesTheme() {
        MovieListScreen({})
    }
}

字符串
这是MovieListViewModel

@HiltViewModel
class MovieListViewModel @Inject constructor(private val repository: MoviesListRepository) : ViewModel() {

    init {
        viewModelScope.launch(Dispatchers.IO) {
        }
    }

    fun getMovieListPage(): Flow<PagingData<MovieData>> = repository.getDiscoverMoviesPage().cachedIn(viewModelScope)
}


MainActivity

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val stringResourceProvider: StringResourceProviderImpl = StringResourceProviderImpl(resources)

        setContent {
            DMoviesTheme {
                DMDBApp(stringResourceProvider)
            }
        }
    }
}

@Composable
private fun DMDBApp(stringResourceProvider: StringResourceProviderImpl) {
    val navController = rememberNavController()

    NavHost(navController, startDestination = "splash_screen") {
        composable(route = "splash_screen") {
            SplashScreen {
                navController.navigate("movie_list_screen")
            }
        }
        composable(route = "movie_list_screen") {
            MovieListScreen() { id ->
                navController.navigate("destination_movie_details_screen/${id}")
            }
        }
        composable(
            route = "destination_movie_details_screen/{id}",
            arguments = listOf (
                navArgument("id") { type = NavType.IntType }
            )
        ) {
            val viewModel: MovieDetailsViewModel = hiltViewModel()
            MovieDetailsScreen(viewModel.movieDetailsState.value)
        }
    }
}

lxkprmvk

lxkprmvk1#

我离开了这个问题,因为我希望这将有助于其他人保存同样的问题。
我的另一段代码没有被引用,那就是Repository。

class MoviesListRepository(private val tmdbWebService: TMDBWebService = TMDBWebService()) {
    fun getDiscoverMoviesPage() = Pager(
        config = PagingConfig(
            pageSize = 20,
        ),
        pagingSourceFactory = {
            MoviesPagingSource(tmdbWebService)
        }
    ).flow
}

字符串
这就是问题的根源。每次调用compose父函数时,都将重新创建Pager。为了解决这个问题,我必须做出以下改变。
停止使用存储库类,改为更改viewModel,如下所示。

@HiltViewModel
class MovieListViewModel @Inject constructor(
    private val repository: MoviesListRepository,
    private val tmdbWebService: TMDBWebService) : ViewModel() {

    val items = Pager(
    config = PagingConfig(
    pageSize = 20,
    ),
    pagingSourceFactory = {
        MoviesPagingSource(tmdbWebService)
    }
    ).flow.cachedIn(viewModelScope)

    init {
        viewModelScope.launch(Dispatchers.IO) {
        }
    }

    //fun getMovieListPage(): Flow<PagingData<MovieData>> = repository.getDiscoverMoviesPage().cachedIn(viewModelScope)
}


然后简单地更新MovieListScreen如下:
//瓦尔movieDataList = viewModel.getMovieListPage().collectAsLazyPagingItems()val movieDataList = viewModel.items.collectAsLazyPagingItems()
结果是movieList然后在导航到描述屏幕和从描述屏幕返回之间保持在其先前位置。
我找到了以下文章,解决了我的问题:
Paging 3 list auto refresh on navigation back in jetpack compose navigation

相关问题