Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the equivalent of [NestedScrollView + RecyclerView] or [Nested RecyclerView (Recycler inside another recycler) in Jetpack compose

I want to create the following layout in Jetpack compose. enter image description here

I've tried creating two lists inside a vertical scrollable Box but that's not possible as I got the this error: "java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like ScrollableContainer and LazyColumn is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items()."

I've tried creating two different lists inside a parent list by using the following code, but that doesn't work either.

@Composable
fun MainList() {
    LazyColumn() {
        
        item {
            /* LazyRow code here */
        }
        
        item {
            /* LazyColumn code here */
        }
    }
}

Now I'm clueless about what else could I try to achieve two lists (one vertical and one horizontal) on the same activity and keep the activity vertically scrollable too.

like image 563
Aiman Muzafar Avatar asked Apr 01 '21 17:04

Aiman Muzafar


People also ask

Does jetpack compose use RecyclerView?

RecyclerView 1.3. 0-alpha02 and Compose UI 1.2. 0-beta02 bring out-of-the-box performant usage of composables from RecyclerView — no extra code required!

How do you make a column scrollable in jetpack compose?

We can make the Column scrollable by using the verticalScroll() modifier.

What is a nested recyclerview?

A nested RecyclerView is an implementation of a RecyclerView within a RecyclerView. An example of such a layout can be seen in a variety of apps such as the Play store where the outer (parent) RecyclerView is of Vertical orientation whereas the inner (child) RecyclerViews are of horizontal orientations. A similar layout is developed here.

Which Java class is required for each recyclerview?

A Java class is required for each RecyclerView wherein the constructor initializes the parameters of the item and the getter and setter methods are declared. Thus, create the following Java classes:

Why does my recyclerview height is wrap_content?

when you are using vertical recycler view inside vertical NestedScrollView without using a constant height, RecyclerView height will be wrap_content even when you set the RecylcerView height to match_parent. so when you update your RecyclerView data, MainThread will be frozen because all the RecyclerView items onBind () method will be called.

How can I extend the size of a recyclerview?

In such situations you have either the option to give the RecyclerView a fixed size and the user can scroll the items within that box or you wrap it in a NestedScrollView. This component allows you to not only scroll through views that extend the size of the device screen but also use complex layouts inside like the RecyclerView.


3 Answers

I think the best option, would be if the LazyVerticalGrid allows some sort of expand logic on each item, but looks like it's not supported yet (beta-03).

So I'm leaving here my solution using one single LazyColumn for the entire list and LazyRow for "My Books" section.

LazyColumn(
    modifier = Modifier.fillMaxSize(),
) {
    // My Books section
    item {
        Column(modifier = Modifier.fillMaxWidth()) {
            Text("My Books")
            LazyRow {
                items(books) { item ->
                    // Each Item
                }
            }
        }

    }
    // Whishlisted Books title
    item {
        Text("Whishlisted Books", style = MaterialTheme.typography.h4)
    }
    // Turning the list in a list of lists of two elements each
    items(wishlisted.windowed(2, 2, true)) { item ->
        Row {
            // Draw item[0]
            // Draw item[1]
        }
    }
}

Here is my gist with the full solution and the result is listed below.

enter image description here

like image 84
nglauber Avatar answered Oct 05 '22 01:10

nglauber


You can do something like:

   Column(Modifier.fillMaxWidth()) {

        LazyRow() {
            items(itemsList){
                  //.....
                }
        }

       LazyColumn() {
           items(itemsList2){
               //..
           }
       }
    }

enter image description here

or:

   Column(Modifier.fillMaxWidth()) {

        LazyRow() {
            items(itemsList){
               //....
            }
        }
       
       LazyVerticalGrid(cells = GridCells.Fixed(2)) {
           items(itemsList2.size){
               //....
           }
       }

    }

enter image description here

like image 28
Gabriele Mariotti Avatar answered Oct 05 '22 02:10

Gabriele Mariotti


An alternative equivalent of nested RecyclerViews is nested LazyColumns, where the heights of the inner LazyColumns are specified or constant, and the inner LazyColumns are placed inside item {} blocks.

Unlike the accepted answer, this approach relies on the .height() modifier to avoid the "java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like ScrollableContainer and LazyColumn is not allowed... " error. Also, this approach addresses the scenario of nested scrolling in the same direction.

Here is an example code and output.

    @Composable
    fun NestedLists() {
        LazyColumn(Modifier.fillMaxSize().padding(12.dp),
            horizontalAlignment = Alignment.CenterHorizontally) {
            //Header for first inner list
            item {
                Text(text = "List of numbers:", style = MaterialTheme.typography.h5)
            }
            // First, scrollable, inner list
            item {
                // Note the important height modifier.
                LazyColumn(Modifier.height(100.dp)){
                    val numbersList = (0 .. 400 step 4).toList()
                    itemsIndexed(numbersList) { index, multipleOf4 ->
                        Text(text = "$multipleOf4", style = TextStyle(fontSize = 22.sp, color = Color.Blue))
                    }
                }
            }

        // Header for second inner list
        item {
            Text(text = "List of letters:", style = MaterialTheme.typography.h5)
        }
        // Second, scrollable, inner list
        item {
            // Note the important height modifier.
            LazyColumn(Modifier.height(200.dp)) {
                val lettersList = ('a' .. 'z').toList()
                itemsIndexed(lettersList) { index, letter ->
                    Text(text = "$letter", style = TextStyle(color = Color.Blue, fontSize = 22.sp))
                }
            }
        }
    }
}

enter image description here

like image 34
Cherif Diallo Avatar answered Oct 05 '22 01:10

Cherif Diallo