Soy nuevo en la redacción de jetpack y trato de implementar una tabla de datos desplazable usando jetpack LazyColumn/LazyRow. La primera fila y la primera columna de toda la tabla serán adhesivas.
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { BaseTheme { ExceptionsSpreadSheet(exceptions = generateData()) } } } } Esta es mi actividad, BaseTheme es solo el tema predeterminado que Android Studio generó para mí.
@OptIn(ExperimentalFoundationApi::class) @Preview(showBackground = true, widthDp = 960, heightDp = 540) @Composable fun ExceptionSpreadSheetHeader( exception: DropBoxException = DropBoxException(), state: LazyListState = rememberLazyListState() ) { LazyRow( modifier = Modifier.background(color = Color.DarkGray), state = state ) { stickyHeader { ExceptionSpreadSheetCell(text = "id") } items(1) { ExceptionSpreadSheetCell(text = "packageName") ExceptionSpreadSheetCell(text = "processName") ExceptionSpreadSheetCell(text = "packageVersionCode") ExceptionSpreadSheetCell(text = "packageVersionName") ExceptionSpreadSheetCell(text = "activity") ExceptionSpreadSheetCell(text = "romVersion") ExceptionSpreadSheetCell(text = "platformId") ExceptionSpreadSheetCell(text = "productName") ExceptionSpreadSheetCell(text = "modelName") ExceptionSpreadSheetCell(text = "exceptionSummary") ExceptionSpreadSheetCell(text = "exceptionType") ExceptionSpreadSheetCell(text = "exceptionDigest") ExceptionSpreadSheetCell(text = "androidId") ExceptionSpreadSheetCell(text = "occurTime") ExceptionSpreadSheetCell(text = "callStack") } } } @OptIn(ExperimentalFoundationApi::class) @Preview(showBackground = true, widthDp = 960, heightDp = 540) @Composable fun ExceptionSpreadSheetRow( exception: DropBoxException = DropBoxException(), state: LazyListState = rememberLazyListState() ) { LazyRow(state = state) { stickyHeader { ExceptionSpreadSheetCell( text = exception.id.toString(), modifier = Modifier.background(color = Color.DarkGray) ) } items(1) { ExceptionSpreadSheetCell(text = exception.packageName) ExceptionSpreadSheetCell(text = exception.processName) ExceptionSpreadSheetCell(text = exception.packageVersionCode) ExceptionSpreadSheetCell(text = exception.packageVersionName) ExceptionSpreadSheetCell(text = exception.activity) ExceptionSpreadSheetCell(text = exception.romVersion) ExceptionSpreadSheetCell(text = exception.platformId.toString()) ExceptionSpreadSheetCell(text = exception.productName) ExceptionSpreadSheetCell(text = exception.modelName) ExceptionSpreadSheetCell(text = exception.exceptionSummary) ExceptionSpreadSheetCell(text = exception.exceptionType) ExceptionSpreadSheetCell(text = exception.exceptionDigest) ExceptionSpreadSheetCell(text = exception.androidId) ExceptionSpreadSheetCell(text = exception.occurTime.toString()) ExceptionSpreadSheetCell(text = exception.callStack) } } } Creé un ExceptionSpreadSheetHeader para el título adhesivo, que mostrará algunos nombres estáticos para las columnas y un ExceptionSpreadSheetRow para las filas debajo del encabezado, cada uno tiene una columna de id adhesiva.
Luego combiné estos dos componentes juntos como ``
@OptIn(ExperimentalFoundationApi::class) @Composable fun ExceptionsSpreadSheet(exceptions: List<DropBoxException>) { val sharedScrollState = rememberLazyListState() if (sharedScrollState.isScrollInProgress) { sharedScrollState.firstVisibleItemIndex sharedScrollState.firstVisibleItemScrollOffset } LazyColumn { stickyHeader { ExceptionSpreadSheetHeader(exception = exceptions.first()) ExceptionSpreadSheetHeader(exception = exceptions.first(), state = sharedScrollState) ExceptionSpreadSheetHeader(state = sharedScrollState) ExceptionSpreadSheetRow(exception = exceptions.first(), state = sharedScrollState) ExceptionSpreadSheetRow(state = sharedScrollState) ExceptionSpreadSheetHeaderTest(state = sharedScrollState) ExceptionSpreadSheetHeaderTest(exception = exceptions.first(), state = sharedScrollState) } items(exceptions) { ExceptionSpreadSheetRow(exception = it, state = sharedScrollState) } } } sharedScrollState para sincronizar el estado de desplazamiento horizontal en las columnas, luego encontré 2 comportamientos extraños, cualquier ayuda o explicación sería excelente para mí, gracias de antemano.
en la sección de items , el estado de desplazamiento compartido no funciona al principio, solo cuando agregué la parte if (sharedScrollState.isScrollInProgress) , las filas en los items se sincronizarían; de lo contrario, parece items_not_sync . Me encantaría saber por qué funciona después de agregar la parte if o por qué no funciona antes.
en la sección stickyHeaders de ExceptionsSpreadSheet , agregué muchas filas para probar, y el resultado parece header_not_sync . Las primeras 6 líneas son encabezados que agregué, incluidos los 2 rojos como ExceptionSpreadSheetHeaderTest :
@OptIn(ExperimentalFoundationApi::class) @Preview(showBackground = true, widthDp = 960, heightDp = 540) @Composable fun ExceptionSpreadSheetHeaderTest( exception: DropBoxException = DropBoxException(), state: LazyListState = rememberLazyListState() ) { LazyRow( modifier = Modifier.background(color = Color.Red), state = state ) { stickyHeader { ExceptionSpreadSheetCell(text = "id") } items(1) { ExceptionSpreadSheetCell(text = "packageName") ExceptionSpreadSheetCell(text = "processName") ExceptionSpreadSheetCell(text = "packageVersionCode") ExceptionSpreadSheetCell(text = "packageVersionName") ExceptionSpreadSheetCell(text = "activity") ExceptionSpreadSheetCell(text = "romVersion") ExceptionSpreadSheetCell(text = "platformId") ExceptionSpreadSheetCell(text = "productName") ExceptionSpreadSheetCell(text = "modelName") ExceptionSpreadSheetCell(text = "exceptionSummary") ExceptionSpreadSheetCell(text = "exceptionType") ExceptionSpreadSheetCell(text = "exceptionDigest") ExceptionSpreadSheetCell(text = "androidId") ExceptionSpreadSheetCell(text = exception.occurTime.toString()) ExceptionSpreadSheetCell(text = "callStack") } } }En el gif, solo el encabezado 4 y 7 se sincronizan con los contenidos a continuación, así que aquí están los comportamientos extraños:
ExceptionSpreadSheetHeader(exception = exceptions.first(), state = sharedScrollState) ) y el séptimo encabezado ( ExceptionSpreadSheetHeaderTest(exception = exceptions.first(), state = sharedScrollState) ) no tienen nada diferente, pero entre el segundo última ExceptionSpreadSheetCell en la sección de items , parece que de alguna manera tengo que usar la exception pasada, entonces la sincronización funcionaría, eso me parece realmente extraño. La clase DropBoxException es una clase de datos simple
data class DropBoxException( var id: Int = 0, var packageName: String = "", var processName: String = "", var packageVersionCode: String = "", var packageVersionName: String = "", var activity: String = "", var romVersion: String = "", var platformId: Int = -1, var productName: String = "", var modelName: String = "", var exceptionSummary: String = "", var exceptionType: String = "", var exceptionDigest: String = "", var androidId: String = "", var occurTime: Int = 0, var callStack: String = "", ) y generateData() crear algunos datos ficticios para mí
fun generateData(): List<DropBoxException> = (1..300).map { DropBoxException( id = it, packageName = "$it packageName", processName = "$it processName" ) }Si cree que hice un mal uso de algunas de las interfaces o si la pregunta se hizo en otro lugar antes, no dude en publicar enlaces y los investigaré, ¡gracias de nuevo!
Después de revisar un montón de ejemplos, ahora me queda claro que cada fila/columna tendrá su propio estado. Así que aquí está mi solución actual, al hacer que lazyListState de cada fila escuche un valor mutable y cambie ese valor cuando cualquier fila se desplaza
class MainActivity : ComponentActivity() { // ... } val horizontalFirstVisibleItemIndex = mutableStateOf(0) val horizontalFirstVisibleItemScrollOffset = mutableStateOf(0) @OptIn(ExperimentalFoundationApi::class) @Composable fun ExceptionsSpreadSheet(exceptions: List<DropBoxException>) { // ... } @OptIn(ExperimentalFoundationApi::class) @Preview(showBackground = true, widthDp = 960, heightDp = 540) @Composable fun ExceptionSpreadSheetHeader() { val scrollState = rememberLazyListState() if (scrollState.isScrollInProgress) { horizontalFirstVisibleItemIndex.value = scrollState.firstVisibleItemIndex horizontalFirstVisibleItemScrollOffset.value = scrollState.firstVisibleItemScrollOffset } LazyRow(modifier = Modifier.background(color = Color.DarkGray), state = scrollState) { // ... } LaunchedEffect(horizontalFirstVisibleItemIndex.value, horizontalFirstVisibleItemScrollOffset.value) { Log.v("test", "launch title $horizontalFirstVisibleItemScrollOffset") scrollState.scrollToItem(horizontalFirstVisibleItemIndex.value, horizontalFirstVisibleItemScrollOffset.value) } } @OptIn(ExperimentalFoundationApi::class) @Preview(showBackground = true, widthDp = 960, heightDp = 540) @Composable fun ExceptionSpreadSheetRow( exception: DropBoxException = DropBoxException(), ) { val scrollState = rememberLazyListState() if (scrollState.isScrollInProgress) { horizontalFirstVisibleItemIndex.value = scrollState.firstVisibleItemIndex horizontalFirstVisibleItemScrollOffset.value = scrollState.firstVisibleItemScrollOffset } LazyRow(state = scrollState) { // ... } LaunchedEffect(horizontalFirstVisibleItemIndex.value, horizontalFirstVisibleItemScrollOffset.value) { scrollState.scrollToItem(horizontalFirstVisibleItemIndex.value, horizontalFirstVisibleItemScrollOffset.value) } }Espero que pueda ayudar a cualquiera que busque soluciones. Se me ocurre que hay un poco de retraso en la animación, pero lo suficientemente bueno para mi caso, por lo que no profundizaré más en este problema, pero se agradece una mayor optimización.