Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Jetpack Compose RecyclerView with Sticky Header and network images using Coil
package eu.anifantakis.composeapp
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter
import eu.anifantakis.composeapp.ui.theme.ComposeAppTheme
// =========================[ NOTE ]=========================
// add Coil for Jetpack Compose at your app dependencies
//
// implementation("io.coil-kt:coil-compose:1.3.2")
// ==========================================================
// define a simple data class to hold Person information
data class Person(
val id: Int,
val section: Int,
val name: String,
val imageUrl: String,
val landingPage: String
)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val persons = arrayListOf<Person>() // create an array list of persons
var section = 1
for (i in 1..200){ // create 200 Person instances in that list
if (i%15 == 0){
section++
}
persons.add(
Person(
id = i,
section = section, // all persons are assigned section
name = "Ioannis Anifantakis",
imageUrl = "https://anifantakis.eu/wp-content/uploads/2021/05/ioannis-anifantakis-firebase-small.jpg",
landingPage = "https://anifantakis.eu"
)
)
}
setContent {
ComposeAppTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
// [Demo1]: A Scrollable ListView equivalent
// ScrollableColumnDemo()
// [Demo2]: A RecyclerView equivalent
// LazyColumnDemo()
// [Demo3]: A RecyclerView equivalent with click listeners on its items
// LazyColumnClickableDemo{
// Toast.makeText(this, "Person $it", Toast.LENGTH_SHORT).show()
// }
// [Demo 4]: A RecyclerView equivalent with rich UI populating it's items from a dataset
// LazyColumnClickableAdvDemo(persons){
// Toast.makeText(this, "${it.name} ${it.id}", Toast.LENGTH_SHORT).show()
// }
// [Demo 5]: A RecyclerView equivalent with rich UI populating it's items from a dataset with a sticky header
LazyColumnClickableAdvStickyDemo(persons){
Toast.makeText(this, "${it.name} ${it.id}", Toast.LENGTH_SHORT).show()
}
}
}
}
}
}
/**
* Demo1: Column
* A Scrollable ListView equivalent
*/
@Composable
fun ScrollableColumnDemo(){
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.verticalScroll(scrollState)
.fillMaxSize()
) {
for (i in 1..200){
Text(
text = "Person $i",
fontSize = 36.sp,
modifier = Modifier.padding(8.dp)
)
Divider(color = Color.Gray, thickness = 1.dp)
}
}
}
/**
* Demo2: LazyColumn
* A RecyclerView equivalent
*/
@Composable
fun LazyColumnDemo(){
LazyColumn(){
items(100){
Text(
text ="Person ${it+1}",
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(8.dp)
)
Divider(color = Color.Gray, thickness = 1.dp)
}
}
}
/**
* Demo3: LazyColumn with clickable items
* A RecyclerView equivalent with click listeners on its items
* @selectedPerson: a lambda function to return back to the caller the number of the current item iteration on click
*/
@Composable
fun LazyColumnClickableDemo(selectedPerson: (Int) -> Unit){
LazyColumn(){
items(200){
Surface(modifier = Modifier.clickable { selectedPerson(it+1) }) {
Text(
text = "Person ${it+1}",
fontSize = 36.sp,
modifier = Modifier.padding(8.dp)
)
Divider(color = Color.Gray, thickness = 1.dp)
}
}
}
}
/**
* Used in Demo4 & Demo5
* Composable function to use Coil to download and display an image
* @imageUrl: The URL of the image
*/
@Composable
fun ImageLoader(imageUrl: String){
Image(
painter = rememberImagePainter(imageUrl),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.size(120.dp)
)
}
/**
* Used in Demo4 & Demo5
* Composable function to represent a list item
* @person: The Person instance, who's information will be displayed in the list item
* @selectedPerson: a lambda function to return back to the caller the Person instance on click
*/
@Composable
fun ListItem(person: Person, selectedPerson: (Person)->Unit){
Card(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
.clickable { selectedPerson(person) },
elevation = 8.dp,
) {
Row{
ImageLoader(person.imageUrl)
Spacer(modifier = Modifier.width(8.dp))
Text(
person.name+' '+person.id,
style = MaterialTheme.typography.h5,
modifier = Modifier.padding(8.dp)
)
}
}
}
/**
* Demo 4: LazyColumn displaying a List<Person> with clickable items
* A RecyclerView equivalent with rich UI populating it's items from a dataset
* @persons: a List<Person> passed as dataset
* @selectedPerson: a lambda function to return back to the caller the number of the current item iteration on click
*/
@Composable
fun LazyColumnClickableAdvDemo(persons: List<Person>, selectedPerson: (Person)->Unit){
LazyColumn(){
items(
items = persons,
itemContent = {
ListItem(person = it, selectedPerson = selectedPerson)
}
)
}
}
/**
* Demo 5: LazyColumn displaying a List<Person> with clickable items, utilizing a StickyHeader
* A RecyclerView equivalent with rich UI populating it's items from a dataset with a sticky header
* @persons: a List<Person> passed as dataset
* @selectedPerson: a lambda function to return back to the caller the number of the current item iteration on click
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyColumnClickableAdvStickyDemo(persons: List<Person>, selectedPerson: (Person)->Unit){
val grouped = persons.groupBy{it.section}
LazyColumn(){
grouped.forEach { (section, sectionPersons) ->
stickyHeader {
Text(
text = "SECTION: $section",
color = Color.White,
modifier = Modifier
.background(color = Color.Black)
.padding(8.dp)
.fillMaxWidth()
)
}
items(
items = sectionPersons,
itemContent = {
ListItem(person = it, selectedPerson = selectedPerson)
}
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment