Horizontal circular RecyclerView in Kotlin?

Viewed 769

I am using a horizontal RecyclerView and showing images using the CircleImageView widget (See image below). I am really struggling on how to make the list of items loop around, so that if you are in the last item, you will move to the first item in the list?

I have tried quite a few similar examples but I can’t make them work as they are in Java, or are quite complex. following is my code

MainActivity.kt

class MainActivity : AppCompatActivity() {
    lateinit var adapter: ConcatAdapter
    lateinit var userVerticalAdapter: UsersAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setupDataInRecyclerView()
    }

    private fun setupDataInRecyclerView() {
        recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
        userVerticalAdapter = UsersAdapter(DataSource.getUser())
        val listOfAdapters = listOf(userVerticalAdapter)
        adapter = ConcatAdapter(listOfAdapters)
        recyclerView.adapter = adapter
   }
}

Adapter.kt

class UsersAdapter(
    private val users: ArrayList<User>
) : RecyclerView.Adapter<UsersAdapter.DataViewHolder>() {

    class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
       fun bind(user: User) {
           itemView.textViewUserName.text = user.name
           Glide.with(itemView.imageViewAvatar.context)
                .load(user.avatar)
                .into(itemView.imageViewAvatar)
       }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        DataViewHolder(LayoutInflater.from(parent.context)
                        .inflate(R.layout.item_layout, parent,false))

    override fun getItemCount(): Int = users.size

    override fun onBindViewHolder(holder: DataViewHolder, position: Int) =
        holder.bind(users[position])
    }
}

DataSource.kt

object DataSource {

fun getUser() = ArrayList<User>().apply {
     add(User(1, "Chillis", R.drawable.chilli))
     add(User(2, "Tomato", R.drawable.tomatoe))
     add(User(3, "Sweetcorn", R.drawable.sweeetcorn))
     add(User(4, "Potatoe", R.drawable.potatoe))
     add(User(5, "Aubergine", R.drawable.aubmain))
     add(User(12, "Onion", R.drawable.onion))
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:listitem="@layout/item_layout_banner"
    />
</androidx.constraintlayout.widget.ConstraintLayout>

item_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="1dp">

<androidx.cardview.widget.CardView
    android:layout_width="120dp"
    android:layout_height="120dp"
    app:cardCornerRadius="4dp"
    app:cardMaxElevation="2dp"
    app:cardElevation="1dp"
    android:layout_centerInParent="true">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <de.hdodenhof.circleimageview.CircleImageView
            android:layout_width="90dp"
            android:layout_height="90dp"
            android:scaleType="centerCrop"
            android:layout_centerHorizontal="true"
            app:civ_border_width= "3dp"
            app:civ_border_color= "@color/colorPrimary"
            android:id="@+id/imageViewAvatar"
            android:src="@drawable/ic_launcher_background"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="20dp"
            android:text="Veg"
            android:layout_below="@+id/imageViewAvatar"
            android:id="@+id/textViewUserName"
            android:gravity="center"
            android:layout_marginTop="5dp"
            android:layout_centerHorizontal="true"
            android:textColor="#000"
            android:layout_marginBottom="5dp"
            android:autoSizeTextType="uniform"
            android:autoSizeMinTextSize="8sp"
            android:autoSizeMaxTextSize="15sp"/>

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

This is my recycler view

1 Answers

To achieve this first you have to override the getItemCount function of UsersAdapter to return a very large value such as Int.MAX_VALUE

// This enables your adapter to scroll past the actual list size
override fun getItemCount(): Int = Int.MAX_VALUE

Now change your onBindViewHolder to calculate actual position

// Lets say your list had 100 items
override fun onBindViewHolder(holder: DataViewHolder, position: Int) {
    // position can be larger than 100 (ex 101), because our getItemCount doesn't return actual list size

    // so if we receive position as 101, which item should we display?
    // that should be item 1. (for 102 -> 2, 201 -> 1) and so on
    // this suggests use of modules 

    val pos = position % users.size  
    holder.bind(users[pos])        // bind the actual item
}

Now in your MainActivity after recyclerView.adapter = adapter line, add following line

// When RV is loaded scroll it to middle, so that user doesn't hit limit on both ends(easily)
rv.scrollToPosition(Int.MAX_VALUE/2)
Related