Android compose custom layout composable

Compose promotes reusability of composables as small chunks that can be enough for some custom layouts by combining built-in composables such as Column, Row, or Box.

However, you might need to build something unique to your app that requires measuring and laying out children manually. For that, you can use the Layout composable. In fact all higher level layouts like Column and Row are built with this.

Principles of layouts in Compose
Some composable functions emit a piece of UI when invoked that is added to a UI tree that will get rendered on the screen. Each emission (or element) has one parent and potentially many children. Also, it has a location within its parent: an (x, y) position, and a size: a width and height.

Elements are asked to measure themselves with Constraints that should be satisfied. Constraints restrict the minimum and maximum width and height of an element. If an element has child elements it may measure each of them to help determine its own size. Once an element reports its own size, it has an opportunity to place its child elements relative to itself. This will be further explained when creating the custom layout.

Compose UI does not permit multi-pass measurement. This means that a layout element may not measure any of its children more than once in order to try different measurement configurations. Single-pass measurement is good for performance, allowing Compose to handle efficiently deep UI trees. If a layout element measured its child twice and that child measured one of its children twice and so on, a single attempt to lay out a whole UI would have to do a lot of work, making it hard to keep your app performing well. However, there are times when you really need additional information on top of what a single child measurement would tell you – for these cases there are ways of doing this, we will talk about them later.

Here is an example of a custom composable layout MyOwnColumn does the same thing as the provided Column.

import androidx.compose.foundation.layout.padding
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.codelab.layouts.ui.LayoutsCodelabTheme

@Composable
fun MyOwnColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->

        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each child
            measurable.measure(constraints)
        }

        // Track the y co-ord we have placed children up to
        var yPosition = 0

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

@Preview
@Composable
fun MyOwnColumnPreview() {
    LayoutsCodelabTheme {
        Surface(Modifier.padding(8.dp)) {
            MyOwnColumn {
                Text("MyOwnColumn")
                Text("places items")
                Text("vertically.")
                Text("We've done it by hand!")
            }
        }
    }
}

Reference:
https://github.com/googlecodelabs/android-compose-codelabs

Search within Codexpedia

Custom Search

Search the entire web

Custom Search