English 中文(简体)
最新数据
原标题:LazyColumn flickers on data update
  • 时间:2023-12-10 15:00:02
  •  标签:

When I update the state it seems that the whole composable recomposes and lazy column seems to be created anew (it blinks and shows new content from the very beginning) At the same time layout inspector shows that recompositions for lazycolumn are skipped. Composable is added through XML in fragment. Full minimal app can be found here simple test app which reproduces that issue

搜查委员会

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun 搜查委员会(
    state: State<SearchCityState>, onTextChanged: (String) -> Unit, onItemSelected: (String) -> Unit
) {
    Box(
        Modifier
            .background(Color.White)
            .fillMaxSize(), contentAlignment = Alignment.TopCenter
    ) {

        Column(
            Modifier
                .fillMaxHeight()
                .padding(start = 16.dp, end = 16.dp, top = 16.dp)
                .widthIn(max = 640.dp)
        ) {


            Row(
                Modifier
                    .height(46.dp)
                    .background(
                        color = Color.White, shape = RoundedCornerShape(4.dp)
                    ), verticalAlignment = Alignment.CenterVertically
            ) {
                Spacer(modifier = (Modifier.width(8.dp)))
                Icon(
                    imageVector = Icons.Default.LocationOn,
                    contentDescription = "Search city input box",
                    Modifier
                        .size(40.dp)
                        .alpha(if (state.value.query.isEmpty()) 0.5f else 1f),
                    tint = Color.Black
                )
                Spacer(modifier = (Modifier.width(8.dp)))
                val textStyle = TextStyle(
                    color = Color.Black, textAlign = TextAlign.Start
                )
                BasicTextField(
                    value = state.value.query,
                    decorationBox = { innerTextField ->
                        if (state.value.query.isEmpty()) {
                            Text(
                                text = "Search",
                                style = textStyle,
                                modifier = Modifier.alpha(0.5f),
                                maxLines = 1,

                                )
                        }
                        innerTextField()
                    },
                    textStyle = textStyle,
                    maxLines = 1,
                    singleLine = true,
                    onValueChange = {

                        onTextChanged(it)
                    },
                    modifier = Modifier.weight(1f),

                    )
            }


            LazyColumn(
                Modifier
                    .fillMaxWidth()
                    .weight(1f),
                contentPadding = PaddingValues(top = 16.dp, bottom = 16.dp)
            ) {

                itemsIndexed(
                    state.value.searchCities.fromServer,
                ) { index, city ->
                    val bgType by remember {
                        derivedStateOf {
                            getBgType(index, state.value.searchCities.fromServer.size)
                        }
                    }
                    CityItem(modifier = Modifier.animateItemPlacement(),
                        value = city,
                        bgType = bgType,
                        onClick = { onItemSelected(it) })
                }


            }
        }
    }
}

private fun getBgType(index: Int, size: Int): BgType {
    return when {
        size == 1 -> BgType.SINGLE
        index == 0 && size > 1 -> BgType.TOP
        index == size - 1 && size > 0 -> BgType.BOTTOM
        else -> BgType.MIDDLE
    }
}

enum class BgType { TOP, MIDDLE, BOTTOM, SINGLE }


@Composable
private fun CityItem(
    modifier: Modifier, value: String, bgType: BgType, onClick: (String) -> Unit
) {
    val textStyle = TextStyle(
        color = Color.Black, textAlign = TextAlign.Start
    )
    Box(
        modifier
            .height(46.dp)
            .fillMaxWidth()
            .background(Color.White)
            .clickable { onClick(value) }, contentAlignment = Alignment.CenterStart
    ) {
        Text(
            text = value, style = textStyle, modifier = Modifier.padding(start = 16.dp, end = 16.dp)
        )

    }

}

ViewModel

@HiltViewModel
class SearchCityViewModel @Inject constructor(
    private val serverUseCase: 2. 搜索和救援,
) : ViewModel() {

    private var queryState = MutableStateFlow("")
    private val _state = MutableStateFlow(SearchCityState())
    val state: StateFlow<SearchCityState> = _state.asStateFlow()
    private var job: Job? = null

    init {
        viewModelScope.launch {
            queryState.debounce(350L).collect {
                job?.cancel()
                job = viewModelScope.launch {
                    serverUseCase.invoke(it).collect {
                        _state.update { state -> state.copy(searchCities = it) }
                    }
                }
            }
        }
    }

    fun searchCities(query: String) {
        _state.update { state -> state.copy(query = query) }

        queryState.value = query
    }
}

2. 搜索和救援

class 2. 搜索和救援 @Inject constructor(

) {
    suspend fun invoke(query: String): Flow<SearchCity> {
        val recents = cities.take(5)
        val flow = callbackFlow<SearchCity> {
            trySend(SearchCity(recent = recents))
            delay(300L)
            val c = if (query.isEmpty()) {
                cities
            } else {
                cities.filter { it.lowercase().contains(query.lowercase()) }
            }
            trySend(
                SearchCity(
                    recent = recents,
                    fromServer = c)
            )

            awaitClose()
        }
        return flow
    }

    val cities = listOf(
        "New York",
        "Los Angeles",
        "Chicago",
        "Houston",
        "Phoenix",
        "Philadelphia",
        "San Antonio",
        "San Diego",
        "Dallas",
        "San Jose",
        "Austin",
        "Jacksonville",
        "San Francisco",
        "Indianapolis",
        "Columbus",
        "Fort Worth",
        "Charlotte",
        "Seattle",
        "Denver",
        "Washington, D.C."
    )
}

“entergraph

问题回答

造成浮标和经常重新沉积的多种因素主要来自<代码>。 检索CityUseCase和的LazyColumn清单项目。 查询/编码。

参看<代码>。 搜索CityUseCase,第1次背书电话:trySend(SearchCity(recent = recents)和人工延迟delay(300L)是不必要的,因为recent 清单也在与“Server 清单”的第二次接驳中通过,该表是国际交易日志随后显示的清单。 这为可见浮油的原因,因为在第一次排放中,从Server清单中排出的“代码”是空洞的。

class SearchCityUseCase @Inject constructor(

) {
    suspend fun invoke(query: String): Flow<SearchCity> {
        val recents = cities.take(5)
        val flow = callbackFlow<SearchCity> {
//            trySend(SearchCity(recent = recents))
//            delay(300L)
            val c = if (query.isEmpty()) {
                cities
            } else {
                cities.filter { it.lowercase().contains(query.lowercase()) }
            }
            trySend(
                SearchCity(
                    recent = recents,
                    fromServer = c)
            )

            awaitClose()
        }
        return flow
    }

在<代码>SearchCityCompose中,LazyColumn项目没有工厂钥匙,因此,每当清单变更时,整个清单就重新编制。 列入每个项目的关键内容应至少减少重新处理。

LazyColumn(
    modifier = Modifier
        .fillMaxWidth()
        .weight(1f),
    contentPadding = PaddingValues(vertical = 16.dp)
) {

    itemsIndexed(
        items = state.value.searchCities.fromServer,
        key = { _, city -> "item_$city" }  // A factory key for each list item.
    ) { index, city ->

        val bgType by remember {
            derivedStateOf {
                getBgType(index, state.value.searchCities.fromServer.size)
            }
        }
                    
        CityItem(
            modifier = Modifier.animateItemPlacement(),
            value = city,
            bgType = bgType,
            onClick = onItemSelected
        )

    }

}

欢乐!





相关问题
热门标签