마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Paging3 로드 시 화면 번쩍 거림에 대해 질문합니다!!!

0 추천

PokeApi를 사용하며 이것저것 연습하고 있습니다! 

현재 PokeApi는 검색 Query를 요청 할 수 없는 상태입니다.

Paging3를 이용하여 데이터를 불러오고, 포켓몬이름을 검색하면 검색된 포켓몬 리스트를 보여주고싶은데요..

검색을 할때마다 화면이 번쩍거리게 됩니다.

제 추측상 검색을 하게되면서 데이터를 재 생성하게 되면서 UI가 다시 그려지게되어 이런현상이 발생했다고는 생각합니다만.. 정말 저녁부터 몇시간째 이 문제하나를 풀지 못하여 이렇게 질문을 남깁니다!!

시도해본것)

- ViewModel에서 바로 데이터를 가져와서 UI에서 조건을 걸어보려고해도 SnapShot 으로 밖에 Paging 데이터를 가져올 수 없어서 이에따라 로드된 데이터만 보여지게 됩니다. .ㅠㅠ

- 현재 코드로 ViewModel에서 분기처리를 하여 필연적으로 검색을하면 데이터가 재생성됩니다...

 

질문할것)

1. 서버에 Query를 요청하지 않고도 화면 번쩍임을 없앨 수 있나요?

2. 가능하다면 어떤식으로 해결해야할지.. 모르겠습니다 도와주세요!!

 

Code)

ViewModel

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val getPokemonListUseCase: GetPokemonListUseCase
) : ViewModel() {

    private val _searchQuery = MutableStateFlow("")
    val searchQuery: StateFlow<String> = _searchQuery.asStateFlow()

    private val _appliedQuery = MutableStateFlow("")
    val appliedQuery: StateFlow<String> = _appliedQuery.asStateFlow()

    @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
    val pokemonPagingData: Flow<PagingData<PokemonList>> =
        appliedQuery
            .debounce(300L)
            .distinctUntilChanged()
            .flatMapLatest { query ->
            getPokemonListUseCase().map { pagingData ->
                if (query.isEmpty()) {
                    pagingData
                } else {
                    pagingData.filter { it.name.contains(query, ignoreCase = true) }
                }
            }
        }.cachedIn(viewModelScope)

    fun updateSearchQuery(query: String) {
        _searchQuery.update { query }
    }

    fun applySearchQuery() {
        _appliedQuery.update { _searchQuery.value }
    }
}

Home

const val GRID_COLUMN = 2

@Composable
fun HomeRoute(
    navController: NavHostController,
    homeViewModel: HomeViewModel = hiltViewModel()
) {
    val pokemonList = homeViewModel.pokemonPagingData.collectAsLazyPagingItems()
    val searchQuery by homeViewModel.searchQuery.collectAsStateWithLifecycle()

    when (pokemonList.loadState.refresh) {
        is LoadState.Loading -> {
            LoadingScreen()
        }

        is LoadState.Error -> {
            val error = (pokemonList.loadState.refresh as LoadState.Error).error
            ErrorScreen(errorMessage = error.message ?: ErrorCode.UNKNOWN_ERROR.message) {
                pokemonList.retry()
            }
        }

        else -> {
            HomeScreen(
                pokemonList = pokemonList,
                searchQuery = searchQuery,
                onSearchQueryChange = { homeViewModel.updateSearchQuery(it) },
                onSearch = { homeViewModel.applySearchQuery() },
                onPokemonClick = { pokemonId ->
                    navController.navigate("detail/$pokemonId")
                }
            )
        }
    }
}

@Composable
fun HomeScreen(
    pokemonList: LazyPagingItems<PokemonList>,
    searchQuery: String,
    onSearchQueryChange: (String) -> Unit,
    onSearch: () -> Unit,
    onPokemonClick: (Int) -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        SearchCard(
            query = searchQuery,
            onQueryChange = onSearchQueryChange,
            onSearch = onSearch
        )

        Box(modifier = Modifier.fillMaxSize()) {
            LazyVerticalGrid(
                columns = GridCells.Fixed(GRID_COLUMN),
            ) {
                items(pokemonList.itemCount) { index ->
                    val pokemon = pokemonList[index]
                    if (pokemon != null) {
                        PokemonCard(
                            pokemon = pokemon,
                            onClick = { onPokemonClick(pokemon.id) }
                        )
                    }
                }
            }
            pokemonList.apply {
                when {
                    loadState.append is LoadState.Loading -> {
                        LoadingScreen()
                    }

                    loadState.append is LoadState.Error -> {
                        val error = (loadState.append as LoadState.Error).error
                        ErrorScreen(
                            errorMessage = error.message ?: ErrorCode.UNKNOWN_ERROR.message
                        ) {
                            retry()
                        }
                    }
                }
            }
        }
    }
}

SearchCard

@Composable
fun SearchCard(
    query: String,
    onQueryChange: (String) -> Unit,
    onSearch: () -> Unit
) {
    val focusManager = LocalFocusManager.current

    Row(
        modifier = Modifier.padding(8.dp)
    ) {
        OutlinedTextField(
            value = query,
            onValueChange = onQueryChange,
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            placeholder = {
                Text(
                    text = "검색어를 입력하세요",
                    color = Color.DarkGray
                )
            },
            textStyle = TextStyle(color = Color.LightGray),
            singleLine = true,
            trailingIcon = {
                IconButton(onClick = {
                    focusManager.clearFocus()
                    onSearch()
                }) {
                    Icon(
                        imageVector = Icons.Default.Search,
                        contentDescription = "검색"
                    )
                }
            },
            keyboardOptions = KeyboardOptions(
                imeAction = ImeAction.Search
            ),
            keyboardActions = KeyboardActions(
                onSearch = {
                    focusManager.clearFocus()
                    onSearch()
                }
            )
        )
    }
}

PokemonCard

@Composable
fun PokemonCard(
    pokemon: PokemonList,
    onClick: () -> Unit
) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp)
            .clickable { onClick() }
    ) {
        AsyncImage(
            model = pokemon.imageUrl,
            contentDescription = "포켓몬 이미지",
            modifier = Modifier
                .fillMaxWidth()
                .aspectRatio(1f),
            contentScale = ContentScale.Fit
        )
        Spacer(modifier = Modifier.height(8.dp))
        Text(
            text = pokemon.name,
            style = MaterialTheme.typography.bodyMedium,
            fontWeight = FontWeight.Bold,
            fontSize = 16.sp,
            modifier = Modifier
                .align(Alignment.CenterHorizontally)
                .padding(8.dp)
        )
    }
}

 

초보 안드 (200 포인트) 님이 2024년 12월 14일 질문

1개의 답변

0 추천
혹시 Stability 이슈는 아닌지 확인해 보세요.

https://developer.android.com/develop/ui/compose/performance/stability/fix
spark (230,170 포인트) 님이 2024년 12월 20일 답변
...