1

I have set up view model, live data, recycler view, adapters, and view holders to render the folllowing JSON response. The payload has other attributes, but they are not that important as compared to widthUnit.

{
    "canvasUnit": 16,
    "specials": [{
            "display_name": "Noodle Dish with Roasted Black Bean Sauce",
            "heightUnit": 8,
            "widthUnit": 16
        },
        {
            "display_name": "Onion Flavored Rings",
            "heightUnit": 8,
            "widthUnit": 8
        },
        {
            "display_name": "Kikkoman Less Sodium Soy Sauce",
            "heightUnit": 8,
            "widthUnit": 8
        },
        {
            "display_name": "Organic Romaine Hearts",
            "heightUnit": 4,
            "widthUnit": 14
        },
        {
            "display_name": "Navel Oranges 4LBS",
            "heightUnit": 6,
            "widthUnit": 4
        },
    {
      "display_name": "Doritos Ranch", 
      "heightUnit": 6, 
      "widthUnit": 4
    }, 
    {
      "display_name": "Lindt Hello Cookies & Cream 1.4 oz", 
      "heightUnit": 6, 
      "widthUnit": 4
    }
    ]
}

I need to have variable number of view on the same row depending on the ratio of widthUnit to canvasUnit. In this example, the first row has a ratio of 16 / 16 = 1. So the 1st row should show the entry associated with the first row

The next two JSON objects have a value of widthUnit as 8. So their ratio to the maxUnit is 0.5, which means that I need to display the next two items on the 2nd row.

Similarly, since the next 4 entries have a cumulative width of 16, then 4 items should be shown on the 3rd row.

So the final layout should be something like this

---------------------------------
|                                |
| -----------------------------  |
| |                            | |
| |          entry # 1         | |
| |                            | |
| ------------------------------ |
| 
|  ------------    ------------  |
| | entry # 2  |   | entry # 3 | |
|  -------------    -----------  |
|  ----   ----     ----  -----   |
| | #4 | | #5 |  | #6 | | #7  |  |
|  ----    ---    ----   -----   |
 ---------------------------------   

How do I accomplish something like this? I have tried using FlexLayoutManager like this

In my activity's onCreate function,

I have initialised the adapter, as follows

val recyclerAdapter = RecyclerAdapter(this)  // Passing the context to the adapter.
val recyclerView = findViewById(R.id.recycler_view)
with(recyclerView) {
            val flexboxLayoutManager = FlexboxLayoutManager(this@MyActivity).apply {
                flexDirection = FlexDirection.ROW
                justifyContent = JustifyContent.FLEX_START
                flexWrap = FlexWrap.WRAP
            }
            layoutManager = flexboxLayoutManager
            adapter = recyclerAdapter
            setHasFixedSize(true)
        }

Adapter code

class RecyclerAdapter(
    private val context: Context
): RecyclerView.Adapter<ViewHolder>() {

    var item: Item? = null;

    override fun getItemCount(): Int = item?.specials?.size ?: 0

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val itemBinding: ItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.item, parent, false)
        return ViewHolder(context, itemBinding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindViewHolder(item?.specials?.get(position), item?.canvasUnit)
    }
}

ViewHolder

class ViewHolder(
    private val context: Context,
    private val itemBinding: ItemBinding,
): RecyclerView.ViewHolder(
    itemBinding.root
) {

    fun bindViewHolder(special: Specials?, canvasUnit: Int?) {
        special ?: return
        canvasUnit ?: return
        itemBinding.special = special
        itemBinding.executePendingBindings()
    }
}

Layout

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="android.view.View"/>
        
        <variable
            name="special"
            type="com.myproject.mobile.model.Specials" />
    </data>

    <androidx.cardview.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/standardMargin2x"
        android:padding="@dimen/standardMargin2x"
        style="@style/CardView"
        android:visibility="@{special != null ?  View.VISIBLE : View.GONE}">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/standardMargin"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/product_image_url"
                android:layout_width="@dimen/product_image_width"
                android:layout_height="@dimen/product_image_height"
                android:importantForAccessibility="no"
                android:layout_alignParentStart="true"
                android:scaleType="fitCenter"
                android:contentDescription="@null"
                android:layout_marginEnd="@dimen/standardMargin2x"
                app:imageUrl="@{special.imageUrl}" />

            <LinearLayout
                android:id="@+id/product_price_container"
                android:layout_marginStart="@dimen/standardMargin2x"
                android:layout_marginBottom="@dimen/standardMargin2x"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toEndOf="@+id/product_image_url"
                android:layout_alignParentEnd="true"
                android:layout_alignWithParentIfMissing="true"
                android:orientation="vertical"
                android:layout_marginEnd="@dimen/standardMargin2x"
                android:gravity="end">

                <TextView
                    android:id="@+id/product_original_price"
                    android:layout_marginBottom="@dimen/standardMargin"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/strikethrough"
                    android:textAppearance="@style/TextAppearance"
                    app:inUSD="@{special.originalPrice}"
                    tools:text="$5.00"/>

                <TextView
                    android:id="@+id/product_price"
                    android:layout_marginBottom="@dimen/standardMargin"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textAppearance="@style/TextAppearance.Secondary"
                    app:inUSD="@{special.price}"

                    tools:text="$5.00"/>
            </LinearLayout>
            <TextView
                android:id="@+id/product_display_name"
                android:layout_width="@dimen/text_display_width"
                android:layout_height="wrap_content"
                android:gravity="center|center_vertical"
                android:layout_centerHorizontal="true"
                android:layout_below="@id/product_price_container"
                android:maxLines="2"
                android:ellipsize="end"
                android:text="@{special.displayName}"
                android:textAppearance="@style/TextAppearance.Bold"
                tools:text="Onion flavoured rings"
                />
        </RelativeLayout>

    </androidx.cardview.widget.CardView>
</layout>

The data is being rendered in a card view as follows

But I would like the entries to be rendered as per my requirements. How can I implement something like this? I have tried going through the following posts

  1. RecyclerView with StaggeredGridLayoutManager : variable number of columns and vertically scrollable
Kartik
  • 2,541
  • 2
  • 37
  • 59

1 Answers1

0

I think you need to use grid layout and set the number of items dynamically by calculating the ratio.

Like first calculate the screen size of the user's device and because you know that how many items should be shown in a row, suppose 8+8 = 16 then you need to show 2 items.

So, set the width of each item layout = total_screen_size/ num_of_items_to_be_shown {Here you can consider some margin as per your need}

and then set the grid item count accordingly.

Kamal Nayan
  • 1,635
  • 1
  • 5
  • 19
  • Can you paste a code snippet of some kind? I don't quite understand what you are suggesting. – Kartik May 04 '21 at 20:14