How to create expandable list view with static values in jetpack compose
Asked Answered
S

2

13

I have created Kotlin Code with static values:

I wanted to know how can I create the same with jetpack compose? I don't know

Code:

       class TestApp : AppCompatActivity() {
    
        var listAdapter: ExpandableListAdapter? = null
        var expListView: ExpandableListView? = null
        var listDataHeader: MutableList<String>? = null
        var listDataChild: HashMap<String, List<String>>? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            expListView = findViewById<View>(R.id.lvExp) as ExpandableListView
            prepareListData()
            listAdapter = ExpandableListAdapter(this, listDataHeader, listDataChild)
            expListView!!.setAdapter(listAdapter)
    
        }
        private fun prepareListData() {
        listDataHeader = ArrayList()
        listDataChild = HashMap()
        listDataHeader?.add(getString(R.string.q_1))
        val on_0: MutableList<String> = ArrayList()
        on_0.add(getString(R.string.faq_d_0))
        listDataChild!![listDataHeader!![0]] = on_0
}
    }
Stressful answered 31/8, 2021 at 3:58 Comment(1)
Even i want the same solution for same using recycler view with retrofit!Cam
H
31

You can do it using LazyColumn plus mutable state list to store collapsed state:

@Composable
fun CollapsableLazyColumn(
    sections: List<CollapsableSection>,
    modifier: Modifier = Modifier
) {
    val collapsedState = remember(sections) { sections.map { true }.toMutableStateList() }
    LazyColumn(modifier) {
        sections.forEachIndexed { i, dataItem ->
            val collapsed = collapsedState[i]
            item(key = "header_$i") {
                Row(
                    verticalAlignment = Alignment.CenterVertically,
                    modifier = Modifier
                        .clickable {
                            collapsedState[i] = !collapsed
                        }
                ) {
                    Icon(
                        Icons.Default.run {
                            if (collapsed)
                                KeyboardArrowDown
                            else
                                KeyboardArrowUp
                        },
                        contentDescription = "",
                        tint = Color.LightGray,
                    )
                    Text(
                        dataItem.title,
                        fontWeight = FontWeight.Bold,
                        modifier = Modifier
                            .padding(vertical = 10.dp)
                            .weight(1f)
                    )
                }
                Divider()
            }
            if (!collapsed) {
                items(dataItem.rows) { row ->
                    Row {
                        Spacer(modifier = Modifier.size(MaterialIconDimension.dp))
                        Text(
                            row,
                            modifier = Modifier
                                .padding(vertical = 10.dp)
                        )
                    }
                    Divider()
                }
            }
        }
    }
}

data class CollapsableSection(val title: String, val rows: List<String>)

const val MaterialIconDimension = 24f

Usage:

CollapsableLazyColumn(
    sections = listOf(
        CollapsableSection(
            title = "Fruits A",
            rows = listOf("Apple", "Apricots", "Avocado")
        ),
        CollapsableSection(
            title = "Fruits B",
            rows = listOf("Banana", "Blackberries", "Blueberries")
        ),
        CollapsableSection(
            title = "Fruits C",
            rows = listOf("Cherimoya", "Cantaloupe", "Cherries", "Clementine")
        ),
    ),
)

Result:

Hypogene answered 31/8, 2021 at 8:56 Comment(10)
Thanks for your comment sir, I have checked but the only thing I want to ask is why title length is only up to half screen?Stressful
@Stressful I forget to remove the Spacer, check out updated variant =)Hypogene
Great @Philip. yes, it worked. Thanks. Can you tell me how can I add an action bar title for this screen?Stressful
@PhilDukhov: How to animate the items in collapsibleSection in this code? I tried animateItemPlacement() with key for the item. Doesn't seem to work.Ammonify
@PhilDukhov I have used this with one change, that is i have a subheader for each row, so my data class has title and a Map<String, List<String>> with subheader as key and items in list.. but when i expand it does not scroll completely in some cases. should i create a seperate question for this ?Navarro
@PhilDukhov do we need to add anything extra to make the screen scrollable ? the list does not show all items when fully expandedNavarro
@SiddarthG it sounds like an issue with your layout, it should work fine by default. Create a separate question with a minimal reproducible example of your problemHypogene
@PhilDukhov i also tried copying your exact code, and adding more items to the lists, when there are items more than the screen height in Fruits B then you will see that it will cut off the remaining items.Navarro
@SiddarthG I didn't get it - multiple items in B works fine to me: youtu.be/78IxRZsQDFIHypogene
@PhilDukhov you are right, it was my constraint layout causing the issue. adding this to another plain surface worked.Navarro
S
1

Expanding on @Phil Dhukov's answer I made a more generic version that accepts composables allowing for more flexibility when customising the rows/headers;

@Composable
fun CollapsableList(
    sections: List<CollapsableListSection>,
    modifier: Modifier = Modifier,
    state: LazyListState = rememberLazyListState(),
    expandByDefault: Boolean = true
) {
    val expandState =
        remember(sections) { sections.map { expandByDefault }.toMutableStateList() }

    LazyColumn(modifier, state = state) {
        sections.forEachIndexed { i, section ->
            val expand = expandState[i]
            item(key = "header_$i") {
                CollapsableListRow(
                    { section.header() },
                    Modifier.clickable { expandState[i] = !expand }
                )
            }
            if (expand) {
                items(section.rows) { row ->
                    CollapsableListRow({ row() })
                }
            }
        }
    }
}

@Composable
private fun CollapsableListRow(content: @Composable () -> Unit, modifier: Modifier = Modifier) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .then(modifier)
    ) {
        content()
    }
}

data class CollapsableListSection(
    val header: @Composable () -> Unit,
    val rows: List<@Composable () -> Unit>
)
Scorn answered 26/1 at 13:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.