How to show partial item in Horizontal RecyclerView at last to indicate scrolling?
Asked Answered
A

2

6

I have tried to search the answer like in this thread: RecyclerView LinearLayoutManager set item count

But it doesn't exactly solve my problem because it shows fixed items.

I want to make RecyclerView like the image below

enter image description here

as you can see in the picture, there are 3 items showing, but also it has incomplete item showing in the right side to give indication that this scroll view can be scrolled. I want to have that partial(3 + 1/4) item in the recyclerView for all screen size.

The xml of item layout I use is like this:

<?xml version="1.0" encoding="utf-8"?>

<androidx.cardview.widget.CardView
        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"
        android:layout_width="150dp"
        android:layout_height="250dp"
        app:cardCornerRadius="10dp"
        android:layout_marginRight="8dp"
        app:cardElevation="3dp">

    <androidx.constraintlayout.widget.ConstraintLayout
            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"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/background_light">

        <ImageView
                android:layout_width="0dp"
                android:layout_height="100dp"
                app:srcCompat="@drawable/logo_apps"
                android:id="@+id/productImageView_Item"
                android:layout_marginTop="8dp"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="8dp"
                app:layout_constraintDimensionRatio="w,1:1"/>

        <TextView
                android:text="@string/product_name"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:id="@+id/productName_textView_item"
                android:layout_marginTop="8dp"
                app:layout_constraintTop_toBottomOf="@+id/productImageView_Item"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:layout_marginStart="4dp"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="4dp"
                android:textAlignment="center"
                android:minLines="1"
                android:maxLines="2"
                app:autoSizeMinTextSize="8sp"
                app:autoSizeMaxTextSize="14sp"
                android:layout_marginBottom="8dp"/>


    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.cardview.widget.CardView>

and the recycler view xml in the activity is like this:

<ScrollView 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"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".Fragment.HomeFragment" android:background="@color/colorLightGrayBackground">

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">


        <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:layout_marginTop="8dp"
                app:layout_constraintTop_toBottomOf="@+id/textView"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="8dp"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp"
                android:layout_marginBottom="8dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintVertical_bias="1.0"
                android:id="@+id/recyclerView_1"/>


    </androidx.constraintlayout.widget.ConstraintLayout>


</ScrollView>

and here is the code in the java file:

 val layoutManager = LinearLayoutManager(mContext,LinearLayoutManager.HORIZONTAL,false)
recyclerView1.adapter = productAdapter
recyclerView1.layoutManager = layoutManager

here is my adapter:

class ProductListAdapter(val context: Context, val products: List<Product>) : RecyclerView.Adapter<ProductListAdapter.ViewHolderProductList>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderProductList {
        val layoutInflater = LayoutInflater.from(parent.context)
        val cellForRow = layoutInflater.inflate(R.layout.item_product_list,parent, false)
        return ViewHolderProductList(cellForRow)
    }

    override fun getItemCount(): Int {
        return products.size
    }

    override fun onBindViewHolder(holder: ViewHolderProductList, position: Int) {
        val product = products[position]


        holder.productNameTextView.text = product.name
        Glide
            .with(context)
            .load(product.getFormattedImagePath())
            .into(holder.productImageView)

    }

    inner class ViewHolderProductList(itemView: View) : RecyclerView.ViewHolder(itemView) {

        val productImageView = itemView.findViewById<ImageView>(R.id.productImageView_Item)
        val productNameTextView = itemView.findViewById<TextView>(R.id.productName_textView_item)


    }



}
Accustom answered 5/2, 2019 at 9:23 Comment(0)
D
11

I recently did this in one of my lists. You want to find out the device's screen width:

val displayMetrics = DisplayMetrics()
(context as NavigationActivity).windowManager.getDefaultDisplay().getMetrics(displayMetrics)
width = displayMetrics.widthPixels

And then divide the width of the device in order to have 3 and a 1/3 blocks cover it, so divide the screen width by around 3.33:

itemWidth = width / 3.33

Now on your onBind method in your list's adapter, you want to change the item's container layout params width to be the itemWidth:

val lp = itemView.container.layoutParams
lp.height = originalHeight
lp.width = itemWidth
itemView.container.layoutParams = lp

You might need to take into consideration padding between items, so instead of:

itemWidth = width / 3.33

Something like:

itemWidth = (width - totalPadding) / 3.33

EDIT

Add it in your code like this:

class ProductListAdapter(val context: Context, val products: List<Product>) : RecyclerView.Adapter<ProductListAdapter.ViewHolderProductList>() {

  // holds this device's screen width,
  private var screenWidth = 0

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderProductList {
        // get screen width
        val displayMetrics = DisplayMetrics()
        (context as YourActivity).windowManager.getDefaultDisplay().getMetrics(displayMetrics)
        screenWidth = displayMetrics.widthPixels

        val layoutInflater = LayoutInflater.from(parent.context)
        val cellForRow = layoutInflater.inflate(R.layout.item_product_list,parent, false)
        return ViewHolderProductList(cellForRow)
    }

    override fun getItemCount(): Int {
        return products.size
    }

    override fun onBindViewHolder(holder: ViewHolderProductList, position: Int) {
        val product = products[position]

        val itemWidth = screenWidth / 3.33

        val lp = holder.cardView.layoutParams
        lp.height = lp.height
        lp.width = itemWidth
        itemView.container.layoutParams = lp


        holder.productNameTextView.text = product.name
        Glide
            .with(context)
            .load(product.getFormattedImagePath())
            .into(holder.productImageView)

    }

    inner class ViewHolderProductList(itemView: View) : RecyclerView.ViewHolder(itemView) {

        val productImageView = itemView.findViewById<ImageView>(R.id.productImageView_Item)
        val productNameTextView = itemView.findViewById<TextView>(R.id.productName_textView_item)


    }
}
Dressingdown answered 5/2, 2019 at 10:26 Comment(13)
sir I am actually a beginner, and I little bit confused with your first block of code. really need your help. I copy and paste it in my adapter, but it seems I can't find NavigationActivity like in this picture: i.sstatic.net/D9uXT.png . I have added my adapter code in my question above. thank you very muchAccustom
Ah yes sorry that was my activity I forgot to change it. In your adapter you pass your context already, so you can cast the context as your activity, the one you create the adapter in.Dressingdown
sir could you please add your code in my adapter code above ? :( I have copied my adapter code in my question. I am really confused where to place the code in my adapter, it seems the itemView can't be accessed like in the image in here i.sstatic.net/juqoy.pngAccustom
Check out my new Edit.Dressingdown
Suppose my screen width is 450, I set the width of the item to be divided by 1.5, so one image is 300. and height is 500. Now the image source is 300x500. Now if you see, then there will be blank spaces around the image which is not filled by the source. How to handle that?Sewole
@AmanVerma Is the blank space in the ImageView or is it outside of the ImageView? You might either want to center your view by using Layout gravity or center crop your image.Dressingdown
Inside the ImageView. If you know Zomato, there are banners on the home screen of Android App. And the Amount of the image that show on the screen at a time is constant through all devices. I want to achieve that.Sewole
Look at the scaleType property in ImageView. Use centerCropDressingdown
I already tried that. I'll post a question on this with images. Okay? Maybe, you will understand better.Sewole
Here it is #59814059Sewole
@P Fuster, it works as expected but I fear setting width dynamically on onBindViewholder will cause memory issues because we are doing it for every item?Gyronny
@makkhaygurung You are right that you could perform the calculation in the ViewHolder constructor for example and just set it once as the screen width is fixed! I wouldn't worry about performance since we are using RecyclerView and it only renders a few items at a time. Also it doesn't really store it in memory, the calculation is performed and the UI is set. I'd be more worried about seeing jankiness when scrolling fast as the width is adjusted.Dressingdown
the above solution works unless the adapter is updated. On updating the adapter with a new list, it recyclerview auto scrolls to the topmost index which was not expected.Haematocryal
M
2

You can use this:

  1. Create kotlin extension to calculate percentage value of screen width:

    infix fun Int.percentOf(value: Int): Int { 
        return if (this == 0) 0 
        else ((this.toDouble() / 100) * value).toInt()
    }
    
  2. Override 'checkLayoutParams' function in LayoutManager

    layoutManager = object : LinearLayoutManager(context, HORIZONTAL, false) {
        override fun checkLayoutParams(lp: RecyclerView.LayoutParams): Boolean {
             lp.width = 85 percentOf width
             return true
         }
    }
    

Result:

Result

Maritamaritain answered 30/11, 2023 at 7:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.