Forked from ioannisa/Jetpack Compose - RecyclerView with Sticky Headers - MainActivity.kt
Created
September 6, 2021 11:17
-
-
Save Merkost/6ebb413e501bccd8a898e892f7a85081 to your computer and use it in GitHub Desktop.
Jetpack Compose RecyclerView with Sticky Header and network images using Coil
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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