In this tutorial we are going to discuss few concept in the jetpack compose library which a upcoming ui toolkit for faster development of android UI with kotlin managed by google.
@ComposablefunStaggeredGrid(modifier:Modifier=Modifier,rows:Int=3,content:@Composable()->Unit){Layout(modifier=modifier,content=content){measurables,constraints->// Keep track of the width of each rowvalrowWidths=IntArray(rows){0}// Keep track of the max height of each rowvalrowHeights=IntArray(rows){0}// Don't constrain child views further, measure them with given constraints// List of measured childrenvalplaceables=measurables.mapIndexed{index,measurable->// Measure each childvalplaceable=measurable.measure(constraints)// Track the width and max height of each rowvalrow=index%rowsrowWidths[row]+=placeable.widthrowHeights[row]=max(rowHeights[row],placeable.height)placeable}// Grid's width is the widest rowvalwidth=rowWidths.maxOrNull()?.coerceIn(constraints.minWidth.rangeTo(constraints.maxWidth))?:constraints.minWidth// Grid's height is the sum of the tallest element of each row// coerced to the height constraintsvalheight=rowHeights.sumBy{it}.coerceIn(constraints.minHeight.rangeTo(constraints.maxHeight))// Y of each row, based on the height accumulation of previous rowsvalrowY=IntArray(rows){0}for(iin1untilrows){rowY[i]=rowY[i-1]+rowHeights[i-1]}// Set the size of the parent layoutlayout(width,height){// x co-ord we have placed up to, per rowvalrowX=IntArray(rows){0}placeables.forEachIndexed{index,placeable->valrow=index%rowsplaceable.placeRelative(x=rowX[row],y=rowY[row])rowX[row]+=placeable.width}}}}
In order to view the implementation of what we've just created, let's create a preview
This is the column We want to insert into the staggered Grid.
The image should be in your drawable folder.
/* Testing custom staggered Grid. */@ComposablefunGetImageColumn(text:String){valtypography=MaterialTheme.typographyMaterialTheme{Column(modifier=Modifier.padding(16.dp)){Card(elevation=10.dp){Image(painter=painterResource(R.drawable.header),contentDescription=null,modifier=Modifier.height(180.dp).fillMaxWidth().clip(shape=RoundedCornerShape(4.dp)),contentScale=ContentScale.Crop)}Spacer(Modifier.height(16.dp))Card(elevation=10.dp,modifier=Modifier.fillMaxWidth()){Column(modifier=Modifier.padding(16.dp)){Text(text=text,style=typography.h4)Text("Nairobi Kenya",style=typography.body2)Text("14 th Feb 2021",style=typography.body2)}}}}}
For testing our column, we can replace all the column composables that we used in creating the preview to our custom staggered view.
@Preview(showBackground=true)@ComposablefunPreview1(){Scaffold{/* New GetCustomColumn we created*/GetCustomColumn(modifier=Modifier.padding(16.dp).verticalScroll(rememberScrollState()),content={StaggeredGrid(rows=2){for(topicintopics){GetImageColumn(text=topic)}}})}}
@ComposablefunSimpleList(){vallistSize=100// We save the scrolling position with this statevalscrollState=rememberLazyListState()// We save the coroutine scope where our animated scroll will be executedvalcoroutineScope=rememberCoroutineScope()Row(modifier=Modifier.fillMaxWidth(),horizontalArrangement=Arrangement.SpaceEvenly){Button(onClick={coroutineScope.launch{scrollState.animateScrollToItem(0)}}){Text(text="Top")}Button(onClick={coroutineScope.launch{scrollState.animateScrollToItem(listSize-1)}}){Text(text="Bottom")}}LazyColumn(state=scrollState){items(listSize){PhotographerCard(numberCount=it)}}}@Preview(showBackground=true)@ComposablefunLayoutsCodeLabPreview(){Test2Theme{SimpleList()}}@ComposablefunPhotographerCard(modifier:Modifier=Modifier,numberCount:Number){Card(elevation=10.dp,){Row(modifier.padding(6.dp).clip(RoundedCornerShape(4.dp)).background(MaterialTheme.colors.surface).clickable(onClick={/*TODO Implement click */}).padding(16.dp).fillMaxWidth()){Surface(modifier=Modifier.size(50.dp),shape=CircleShape,color=MaterialTheme.colors.onSurface.copy(alpha=0.2f)){Image(painter=rememberCoilPainter(request="https://developer.android.com/images/brand/Android_Robot.png"),contentDescription="Android Logo",modifier=Modifier.size(50.dp).padding(8.dp))// Icon(Icons.Filled.PersonOutline, modifier = Modifier.padding(8.dp), contentDescription = null)}Column(modifier.padding(start=8.dp).align(Alignment.CenterVertically)){Text("List View. ",fontWeight=FontWeight.Bold)CompositionLocalProvider(LocalContentAlphaprovidesContentAlpha.medium){Text(text="$numberCount sec.",style=MaterialTheme.typography.body2)}}}}}
Preview
Lastly Let's combine everything to have some simple UI in action.
/* UI play */@Preview(showBackground=true)@ComposablefunLayoutsCodeLab(){Scaffold(topBar={TopAppBar(title={Text(text="Design With Composed",style=MaterialTheme.typography.h5)},actions={IconButton(onClick={/*TODO*/}){Icon(Icons.Filled.Favorite,contentDescription=null)}})}){innerPadding->BodyContent(Modifier.padding(innerPadding).padding(8.dp))}}@ComposablefunBodyContent(modifier:Modifier=Modifier){Spacer(Modifier.height(10.dp))GetCustomColumn(modifier=modifier){Row(modifier=modifier.fillMaxWidth().padding(16.dp).size(200.dp).horizontalScroll(rememberScrollState()),content={StaggeredGrid{for(topicintopics){Chip(modifier=Modifier.padding(8.dp),text=topic)}}})PreviewListView1()}}@ComposablefunPreviewListView1(){Scaffold{/* New GetCustomColumn we created*/// We save the scrolling position with this statevalscrollState=rememberLazyListState()LazyColumn(state=scrollState){for(topicintopics){items(topics.size){GetImageColumn(text=topic)}}}}}
Jetpack Compose is a modern toolkit for building native Android UI. It's based on the declarative programming model, so you can simply describe what your UI should look like, and Compose takes care of the rest—as app state changes, your UI automatically updates.