Redactar: elementos de composición del contenedor

1. Lista diferida LazyColumn, LazyRow

        Desplazable, similar a RecyclerView (Columna y Fila son equivalentes a ScrollView cuando se usa Modificador para configurar el desplazamiento).

@Composable
fun Demo(dataList: List<String>) {
    val state = rememberLazyListState()
    LazyColumn(
        modifier = Modifier.height(100.dp),
        state = state,  //管理列表的状态,可通过这个操作列表滚动
        contentPadding = PaddingValues(horizontal = 5.dp),    //内容内边距
        reverseLayout = false,  //是否反转显示
        verticalArrangement = Arrangement.Top,    //排列方式,使用Arrangement.spacedBy(4.dp)就是设置间距
        horizontalAlignment = Alignment.Start,    //对齐方式
        flingBehavior = ScrollableDefaults.flingBehavior(),
        userScrollEnabled = true,   //是否允许滑动
    ) { }
}

1.1 Elemento elemento (), elementos ()

El DSL LazyListScope proporciona una variedad de funciones para describir elementos en una lista, item() agrega un solo elemento y items() agrega varios elementos.

fun Demo(dataList: List<String>) {
    LazyColumn {
        //添加单个条目
        item { Text(text = "单个条目") }
        //添加多个条目
        items(5) {index -> Text(text = "条目索引:$index") }
        //根据数据源创建
        items(
            items = dataList,    //可传入、数组、集合、Paging的LazyPagingItems
            key = { element ->
                element.id    //key设为element的唯一值
            }
        ) { item ->
            //对条目布局使用该修饰符来对列表的更改添加动画效果
            Row(Modifier.animateItemPlacement()) {}
        }
        //带索引
        itemsIndexed(dataList) { index: Int, item: String ->
            Text(text = "$index:$item", modifier = Modifier.padding(1.dp).background(Color.Red))
        }
    }
}

1.2 Relleno de contenido

Se agrega relleno alrededor de los bordes del contenido, que se agrega al contenido en lugar de a la lista en sí. Se agregan márgenes verticales al primer y último elemento, y márgenes horizontales a la izquierda y a la derecha de todos los elementos.

LazyColumn(
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
) { }

1.3 Disposición vertical del espaciado del contenido

LazyColumn(
    //使用Arrangement.Top就是排列方式
    verticalArrangement = Arrangement.spacedBy(4.dp)
) { }

 1.4 clave mejora el desempeño de la reorganización

A diferencia de RecyclerView, que notificará manualmente los cambios de datos (desplazamiento, nueva adición, eliminación, contenido) a través del Adaptador después de modificar la fuente de datos, el elemento de combinación de la lista declarativa solo pasa la recopilación de la fuente de datos, lo que provocará la actualización completa sin pensarlo. , y todos los elementos serán reorganizados. La clave se establece en el valor único del elemento (como id), de modo que la lista puede detectar si la posición del elemento ha cambiado o si se ha agregado o eliminado. La lista puede detectar automáticamente si el contenido ha cambiado. a través de los iguales del propio objeto elemento. De esta manera, cuando cambia la fuente de datos, la lista se puede reorganizar de manera eficiente y solo es necesario procesar las combinaciones correspondientes a los elementos modificados, en lugar de todas las actualizaciones.

items(
    items = dataList,
    key = { it.id }    //it是dataBean
) { }

1.5 contentType mejora el rendimiento de la reorganización

A partir de la versión 1.2, cuando una lista se compone de elementos de varios tipos diferentes, puede especificar un tipo de elementos para garantizar que Compose no intente combinar elementos de tipo A en elementos combinados de tipo B.

LazyColumn {
    items(elements, contentType = { it.type }) {...}
}

1.6 Animación de elementos

Utilice modificadores en el diseño del elemento para animar los cambios en la lista.

items(dataList, key = { it.id }) {
    Row(Modifier.animateItemPlacement()) { }
}

1.7 Estado de la lista recuerdaLazyListState()

1.7.1 Monitoreo simple de la posición de desplazamiento

Por lo general, solo necesita monitorear la información del primer elemento visible. LazyListState proporciona las propiedades firstVisibleItemIndex (índice del primer elemento visible) y firstVisibleItemScrollOffset (desplazamiento del primer elemento visible).

@Composable
fun MessageList(messages: List<Message>) {
    Box {
        val listState = rememberLazyListState()
        LazyColumn(state = listState) {
            //...
        }
        val showButton by remember {
            derivedStateOf { listState.firstVisibleItemIndex > 0 }
        }
        AnimatedVisibility(visible = showButton) {
            ScrollToTopButton()
        }
    }
}

1.7.2 Monitoreo complejo de la posición de desplazamiento

Leer el estado directamente en la composición funciona bien cuando necesitas actualizar otros elementos componibles de la interfaz, pero en algunos casos el sistema no necesita manejar este evento en la misma composición. Un ejemplo común es cuando el sistema envía eventos analíticos después de que el usuario ha pasado de cierto punto. Para resolver este problema de manera eficiente, podemos usar  snapshotFlow() :

val listState = rememberLazyListState()

LazyColumn(state = listState) {
    // ...
}

LaunchedEffect(listState) {
    snapshotFlow { listState.firstVisibleItemIndex }
        .map { index -> index > 0 }
        .distinctUntilChanged()
        .filter { it == true }
        .collect {
            MyAnalyticsService.sendScrolledPastFirstItemEvent()
        }
}

1.7.3 Controlar la posición de desplazamiento

LazyListState se desplaza inmediatamente a través de scrollToItem() y utiliza desplazamiento animado (desplazamiento suave) con animateScrollToItem(). 

@Composable
fun MessageList(messages: List<Message>) {
    val listState = rememberLazyListState()
    val coroutineScope = rememberCoroutineScope()
    LazyColumn(state = listState) {
        // ...
    }
    ScrollToTopButton(
        onClick = {
            coroutineScope.launch {
                listState.animateScrollToItem(index = 0)
            }
        }
    )
}

1.8 Encabezado fijo stickyHeader()

No seguirá el desplazamiento y el fondo se mostrará a través de la diapositiva transparente de forma predeterminada.

1.8.1 Título fijo único 

@Composable
fun Dode() {
    LazyColumn(Modifier.height(100.dp)) {
        stickyHeader {
            Text(text = "头部", modifier = Modifier.background(Color.Gray))
        }
        items(10) { Text(text = "条目索引:$it") }
    }
}

1.8.2 Múltiples títulos fijos

Vídeo: desplácese hacia arriba y hacia abajo en la lista de contactos de su teléfono

@Composable
fun ContactsList(grouped: Map<Char, List<Contact>>) {
    LazyColumn {
        grouped.forEach { (lastName, contactsForLastName) ->
            stickyHeader {
                CharacterHeader(lastName)
            }
            items(contactsForLastName) { contact ->
                ContactListItem(contact)
            }
        }
    }
}

1.9 Grandes conjuntos de datos (paginación)

Paging 3.0 y posteriores  androidx.paging:paging-compose brindan soporte para Compose a través de bibliotecas.

1.10 Anidamiento deslizante

Evite anidar un elemento de subcombinación desplazable en la misma dirección sin un tamaño fijo, lo que generará una IllegalStateException. Se puede encapsular en el mismo LazyColumn y usar LazyListScope DSL para pasar diferentes tipos de contenido para lograr el mismo efecto.

// Throws IllegalStateException
Column(modifier = Modifier.verticalScroll(state)) {
    LazyColumn {...}
}
LazyColumn {
    item { Header() }
    items(data) { item ->
        Item(item)
    }
    item { Footer() }
}

Dos, el retraso de LazyVerticalGrid, LazyHorizontalGrid

@Preview(showBackground = true, name = "Fixed")
@Composable
fun Show1() {
    Demo(GridCells.Fixed(5))
}

@Preview(showBackground = true, name = "Adaptive")
@Composable
fun Show() {
    Demo(GridCells.Adaptive(60.dp))
}

@Composable
fun Demo(style: GridCells) {
    val dataList = listOf("香菜", "酒鬼花生", "酱油酱油酱油", "海鲜酱海鲜酱", "陈醋陈醋", "小葱花", "大蒜泥", "辣椒", "老干妈", "耗油", "芝麻酱")
    val lazyGridState = rememberLazyGridState()
    LazyVerticalGrid(
        columns = style,    //描述单元格展现形式:fixed设置固定尺寸、Adaptive设置最小尺寸后内容自适应网格大小
        modifier = Modifier.fillMaxWidth(),
        state = lazyGridState,
        contentPadding = PaddingValues(0.dp),   //边距
        reverseLayout = false,  //是否反转显示
        verticalArrangement = Arrangement.Top,
        horizontalArrangement = Arrangement.Start,
        flingBehavior = ScrollableDefaults.flingBehavior(),
        userScrollEnabled = true    //是否允许滑动
    ){
        itemsIndexed(dataList){index: Int, item: String ->
            Text(text = "${index}:$item", maxLines = 1, modifier = Modifier
                .padding(1.dp)
                .background(Color.Red))
        }
    }
}

三、 LazyVerticalStaggeredGrid、LazyHorizontalStaggeredGrid

@Preview(showBackground = true, name = "Fixed")
@Composable
fun Show1() {
    Demo(StaggeredGridCells.Fixed(5))
}

@Preview(showBackground = true, name = "Adaptive")
@Composable
fun Show() {
    Demo(StaggeredGridCells.Adaptive(60.dp))
}

@Composable
fun Demo(style: StaggeredGridCells) {
    val dataList = listOf("香菜", "酒鬼花生", "酱油酱油酱油", "海鲜酱海鲜酱", "陈醋陈醋", "小葱花", "大蒜泥", "辣椒", "老干妈", "耗油", "芝麻酱")
    val state = rememberLazyStaggeredGridState()
    LazyVerticalStaggeredGrid(
        columns = style,//描述单元格展现形式:fixed设置固定尺寸、Adaptive设置最小尺寸后内容自适应网格大小
        modifier = Modifier.fillMaxWidth(),
        state = state,
        contentPadding = PaddingValues(0.dp),   //边距
        reverseLayout = false,  //是否反转显示
        verticalItemSpacing = 0.dp, //行间距
        horizontalArrangement = Arrangement.Start,
        flingBehavior = ScrollableDefaults.flingBehavior(),
        userScrollEnabled = true    //是否允许滑动
    ) {
        itemsIndexed(dataList){index: Int, item: String ->
            Text(text = "${index}:$item", maxLines = 1, modifier = Modifier
                .padding(1.dp)
                .background(Color.Red))
        }
    }
}

4. Diálogo emergente

var showDialog by remember { mutableStateOf(false) }
Column {
    Button(onClick = { showDialog = !showDialog }) {
        Text(text = "点击弹窗")
    }
    if (showDialog) {
        Dialog(
            onDismissRequest = { showDialog = false }, //消失回调
            properties = DialogProperties(
                dismissOnBackPress = true,  //消失响应返回键
                dismissOnClickOutside = true,   //消失响应外围点击
                securePolicy = SecureFlagPolicy.Inherit,    //是否可以被截屏(Inherit跟随父元素、SecureOn禁止、SecureOff允许)
            )
        ) {
            Box(modifier = Modifier.size(100.dp).background(Color.Red))
        }
    }
}

5. Diálogo de alertaDiálogo

var showDialog by remember { mutableStateOf(false) }
Column {
    Button(onClick = { showDialog = !showDialog }) {
        Text(text = "点击弹窗")
    }
    if (showDialog) {
        AlertDialog(
            modifier = Modifier,
            //确认按钮
            confirmButton = { TextButton(onClick = { showDialog = false }) { Text(text = "确认") } },
            //取消按钮
            dismissButton = { TextButton(onClick = { showDialog = false }) { Text(text = "取消") } },
            //图标
            icon = { Icon(imageVector = Icons.Default.Home, contentDescription = null) },
            iconContentColor = Color.Magenta,
            //标题
            title = { Text(text = "标题") },
            titleContentColor = Color.Red,
            //内容
            text = { Text(text = "内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容") },
            textContentColor = Color.Blue,
            //背景
            shape = RoundedCornerShape(5.dp),
            containerColor = Color.Yellow,
            tonalElevation = 10.dp,
            //配置
            properties = DialogProperties(
                dismissOnBackPress = true,  //消失响应返回键
                dismissOnClickOutside = true,   //消失响应外围点击
                securePolicy = SecureFlagPolicy.Inherit,//是否可以被截屏(Inherit跟随父元素、SecureOn禁止、SecureOff允许)
            ),
            //消失回调
            onDismissRequest = {
                showDialog = false
            }
        )
    }
}

6. Interfaz deslizante HorizontalPager, VerticalPager

Al igual que ViewPager, la biblioteca que lo acompaña se requiere antes de la versión 1.4, y la capa inferior se basa en LazyColumn y LazyRow, que son básicamente los mismos en uso.

fun HorizontalPager(
    pageCount: Int,    //页面数量
    modifier: Modifier = Modifier,
    state: PagerState = rememberPagerState(),    //控制监听页面状态的对象
    contentPadding: PaddingValues = PaddingValues(0.dp),    //内容内边距
    pageSize: PageSize = PageSize.Fill,
    beyondBoundsPageCount: Int = 0,
    pageSpacing: Dp = 0.dp,
    verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
    flingBehavior: SnapFlingBehavior = PagerDefaults.flingBehavior(state = state),    //描述滑动行为的逻辑
    userScrollEnabled: Boolean = true,    //是否允许通过用户手势或辅助功能进行滚动
    reverseLayout: Boolean = false,    //反转页面顺序
    key: ((index: Int) -> Any)? = null,    //滚动的位置将根据键保持
    pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
        Orientation.Horizontal
    ),
    pageContent: @Composable (page: Int) -> Unit
) 

 6.1 Fácil de usar

HorizontalPager(
    pageCount = 10,
    modifier = Modifier.size(100.dp)
) { page ->
    // 每一页的内容,比如显示个文本
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxSize()
    )
}

6.2 Agregar indicador Indicador

 

7. Pestaña PestañaFila

var selectedIndex by remember { mutableStateOf(0) }
TabRow(
    modifier = Modifier,
    selectedTabIndex = selectedIndex,    //选中的索引
    containerColor = Color.Green, //背景色
    contentColor = Color.Yellow, //内容色(子Tab设置未选中颜色会覆盖这个)
    indicator = {},  //设置指示器
    divider = {}    //设置分割线
) {
    //图片和文字是横向的
    LeadingIconTab(
        modifier = Modifier,
        selected = selectedIndex == 0,  //是否选中
        onClick = { selectedIndex = 0 },    //点击监听
        text = { Text(text = "选项0") },  //选项文字
        icon = { Icon( imageVector = Icons.Default.AccountBox, contentDescription = null ) },   //选项图片
        enabled = true, //是否启用
        selectedContentColor = Color.Red,    //选中颜色
        unselectedContentColor = Color.Blue, //未选中颜色
    )
    //图片和文字是纵向的
    Tab(
        modifier = Modifier,
        selected = selectedIndex == 1,  //是否选中
        onClick = { selectedIndex = 1 },    //点击监听
        text = { Text(text = "选项1") },    //选项文字
        icon = { Icon( imageVector = Icons.Default.AccountBox, contentDescription = null ) },   //选项图片
        enabled = true, //是否启用
        selectedContentColor = Color.Red,    //选中颜色
        unselectedContentColor = Color.Blue, //未选中颜色
    )
}

8. Fila de pestañas desplazable

val dataList = listOf("热点", "世界杯", "数码科技", "英雄联盟", "视频", "在线直播", "娱乐圈")
var selectedIndex by remember { mutableStateOf(0) }
ScrollableTabRow(
    modifier = Modifier,
    selectedTabIndex = selectedIndex,    //选中的索引
    containerColor = Color.Green, //背景色
    contentColor = Color.Yellow, //内容色(子Tab设置未选中颜色会覆盖这个)
    indicator = {},  //设置指示器
    divider = {}    //设置分割线
) {
    dataList.onEachIndexed { index, str ->
        Tab(
            selected = selectedIndex == index,  //是否选中
            text = { Text(text = dataList[index]) },    //选项文字
            onClick = { selectedIndex = index },    //点击监听
            selectedContentColor = Color.Red,    //选中颜色
            unselectedContentColor = Color.Blue, //未选中颜色
        )
    }
}

9. Tarjeta Tarjeta

Card(
    modifier = Modifier,
    shape = CircleShape,
    colors = CardDefaults.cardColors(),
    elevation = CardDefaults.cardElevation(),
    border = BorderStroke(width = 1.dp, color = Color.Red),
) {
    //子元素
}

10. Menú desplegable Menú desplegable

No ocupa espacio en el diseño en sí y se muestra en una ventana separada, encima del resto del contenido.

 

var expandedState by remember { mutableStateOf(false) }
Column {
    Button(onClick = { expandedState = !expandedState }) {
        Text(text = "点击打开")
    }
    DropdownMenu(
        modifier = Modifier,
        expanded = expandedState,  //是否展开
        offset = DpOffset(10.dp, 10.dp),    //展开菜单的偏移量
        properties = PopupProperties(
            focusable = true,   //是否聚焦
            dismissOnBackPress = true,  //消失响应返回键
            dismissOnClickOutside = true,   //消失响应外围点击
            securePolicy = SecureFlagPolicy.SecureOn    //是否可以被截屏(Inherit跟随父元素、SecureOn禁止、SecureOff允许)
        ),
        onDismissRequest = {    //消失回调
            expandedState = false
        }
    ) {
        DropdownMenuItem(
            modifier = Modifier,
            text = { Text(text = "苹果") },
            leadingIcon = { Icon(imageVector = Icons.Default.Home, contentDescription = null) },   //左侧图标
            trailingIcon = { Icon(imageVector = Icons.Default.Email, contentDescription = null) },  //右侧图标
            enabled = true, //是否启用
            colors = MenuDefaults.itemColors(),
            contentPadding = PaddingValues(5.dp),
            onClick = { /*TODO*/ }  //点击事件
        )
        DropdownMenuItem(text = { Text(text = "桔子") }, onClick = {  })
        DropdownMenuItem(text = { Text(text = "香蕉") }, onClick = {  })
    }
}

11. Superficie plana

Surface(
    modifier = Modifier.size(50.dp).padding(5.dp),
    shape = RectangleShape, //形状(RectangleShape矩形、CircleShape圆形、RoundedCornerShape圆角、CutCornerShape切角)
    color = Color.Red,  //背景色(默认是主题中的surface颜色)
    contentColor = Color.Blue,  //内容主色
    tonalElevation = 0.dp,  //当color=ColorScheme.surface时,值越大,浅色主题越深,深色主题越浅
    shadowElevation = 0.dp, //阴影大小
    border = BorderStroke(width = 1.dp, color = Color.Black),   //边框粗细和颜色
) {
    //子元素
}

11. Diseño de flujo FlowRow, FlowColumn

Cuando una fila (o columna) no puede caber en el contenido que contiene, se ajustará automáticamente. Estos diseños fluidos también permiten un cambio de tamaño dinámico utilizando pesos para distribuir artículos en contenedores.

 

@Composable
fun Filters() {
    val filters = listOf("Washer/Dryer", "Ramp access", "Garden", "Cats OK", "Dogs OK", "Smoke-free")
    FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
        filters.forEach { title ->
            var selected by remember { mutableStateOf(false) }
            val leadingIcon: @Composable () -> Unit = { Icon(Icons.Default.Check, null) }
            FilterChip(
                selected = selected,
                onClick = { selected = !selected },
                label = { Text(title) },
                leadingIcon = if (selected) leadingIcon else null
            )
        }
    }
}

Supongo que te gusta

Origin blog.csdn.net/HugMua/article/details/132525212
Recomendado
Clasificación