In most of our applications we tend to need to display a series of images. Images make our applications look more modern and interactive and they are a great way to pass information visually.

Sometimes you may even need to show your item lists horizontally. One way is to use a recyclerview with horizontal orientation. However in this case the items won’t be auto-flipped or faded.

Thus we will first look at how to create a custom carousel based on recyclerview, the we will curate some of the best libraries and examples already written by other developers and available for re-use. You could always customize them since they are all open source.

Most of them are actually based on recyclerview and some do need a custom adapter. This gives them flexibility and power.

In this article you will learn:

  1. How to create a custom image carousel in android based on recyclerview.
  2. How to use third party libraries to implement a carousel.

We use the following languages:

  1. Kotlin
  2. Java

NB/= This article is broken down into several pages. Each page teaches a unique concept. Here are the taught concepts:
Page 1: Implement a custom carousel
Page 2: Use libraries to implement a carousel

Concept 1: Creating a Custom Carousel

Example 1: Kotlin android Horizontal Carousel

How to implement a custom horizontal image carousel using recycelrview in Kotlin.

Step 1: Create Project

Start by creating an empty Android Studio Kotlin project.

Step 2: Dependencies

No third party libraries are needed for this project.

Step 3: Create a Custom Carousel View

We will create a custom carousel view by extending the recyclerview:

HorizontalCarouselRecyclerView.kt

package supahsoftware.androidexamplecarousel

import android.animation.ArgbEvaluator
import android.content.Context
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class HorizontalCarouselRecyclerView(
    context: Context,
    attrs: AttributeSet
) : RecyclerView(context, attrs) {

    private val activeColor by lazy { ContextCompat.getColor(context, R.color.blue) }
    private val inactiveColor by lazy { ContextCompat.getColor(context, R.color.gray) }
    private var viewsToChangeColor: List<Int> = listOf()

    fun <T : ViewHolder> initialize(newAdapter: Adapter<T>) {
        layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
        newAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
            override fun onChanged() {
                post {
                    val sidePadding = (width / 2) - (getChildAt(0).width / 2)
                    setPadding(sidePadding, 0, sidePadding, 0)
                    scrollToPosition(0)
                    addOnScrollListener(object : OnScrollListener() {
                        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                            super.onScrolled(recyclerView, dx, dy)
                            onScrollChanged()
                        }
                    })
                }
            }
        })
        adapter = newAdapter
    }

    fun setViewsToChangeColor(viewIds: List<Int>) {
        viewsToChangeColor = viewIds
    }

    private fun onScrollChanged() {
        post {
            (0 until childCount).forEach { position ->
                val child = getChildAt(position)
                val childCenterX = (child.left + child.right) / 2
                val scaleValue = getGaussianScale(childCenterX, 1f, 1f, 150.toDouble())
                child.scaleX = scaleValue
                child.scaleY = scaleValue
                colorView(child, scaleValue)
            }
        }
    }

    private fun colorView(child: View, scaleValue: Float) {
        val saturationPercent = (scaleValue - 1) / 1f
        val alphaPercent = scaleValue / 2f
        val matrix = ColorMatrix()
        matrix.setSaturation(saturationPercent)

        viewsToChangeColor.forEach { viewId ->
            val viewToChangeColor = child.findViewById<View>(viewId)
            when (viewToChangeColor) {
                is ImageView -> {
                    viewToChangeColor.colorFilter = ColorMatrixColorFilter(matrix)
                    viewToChangeColor.imageAlpha = (255 * alphaPercent).toInt()
                }
                is TextView -> {
                    val textColor = ArgbEvaluator().evaluate(saturationPercent, inactiveColor, activeColor) as Int
                    viewToChangeColor.setTextColor(textColor)
                }
            }
        }
    }

    private fun getGaussianScale(
        childCenterX: Int,
        minScaleOffest: Float,
        scaleFactor: Float,
        spreadFactor: Double
    ): Float {
        val recyclerCenterX = (left + right) / 2
        return (Math.pow(
            Math.E,
            -Math.pow(childCenterX - recyclerCenterX.toDouble(), 2.toDouble()) / (2 * Math.pow(
                spreadFactor,
                2.toDouble()
            ))
        ) * scaleFactor + minScaleOffest).toFloat()
    }

}

Step 4: Design Layouts

We will need two layouts for this project:

(a). list_item.xml

A layout to represent a single carousel item:

<?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"
    android:layout_margin="24dp"
    android:paddingStart="24dp"
    android:paddingTop="64dp"
    android:paddingEnd="24dp"
    android:paddingBottom="64dp">

    <ImageView
        android:id="@+id/list_item_background"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:src="@drawable/icon_background_blue" />

    <ImageView
        android:id="@+id/list_item_icon"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_centerInParent="true" />

    <TextView
        android:id="@+id/list_item_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/list_item_background"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="32dp"
        android:textColor="@color/blue"
        android:textSize="22sp" />

</RelativeLayout>

(b). activity_main.xml

Will contain our custom Horizontal Carousel recyclerview:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff">

    <supahsoftware.androidexamplecarousel.HorizontalCarouselRecyclerView
        android:id="@+id/item_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:clipToPadding="false"
        android:overScrollMode="never" />

</LinearLayout>

Step 5: Create an Adapter

Create our carousel adapter:

ItemAdapter.kt


class ItemAdapter(val itemClick: (position:Int,item:Item) -> Unit) : RecyclerView.Adapter<ItemViewHolder>() {

    private var items: List<Item> = listOf()

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

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        holder.bind(items[position])
        holder.itemView.setOnClickListener {
            itemClick(position,items[position])
        }
    }

    override fun getItemCount() = items.size

    fun setItems(newItems: List<Item>) {
        items = newItems
        notifyDataSetChanged()
    }
}

class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
    fun bind(item: Item) {
        view.list_item_text.text = "${item.title}"
        view.list_item_icon.setImageResource(item.icon)
    }
}

Step 6: Create MainActivity

Here is the code for MainActivity:

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private val itemAdapter by lazy {
        ItemAdapter { position: Int, item: Item ->
            Toast.makeText(this@MainActivity, "Pos ${position}", Toast.LENGTH_LONG).show()
            item_list.smoothScrollToPosition(position)
        } }
    private val possibleItems = listOf(
        Item("Airplanes", R.drawable.ic_airplane),
        Item("Cars", R.drawable.ic_car),
        Item("Food", R.drawable.ic_food),
        Item("Gas", R.drawable.ic_gas),
        Item("Home", R.drawable.ic_home)
    )

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

        item_list.initialize(itemAdapter)
        item_list.setViewsToChangeColor(listOf(R.id.list_item_background, R.id.list_item_text))
        itemAdapter.setItems(getLargeListOfItems())
    }

    private fun getLargeListOfItems(): List<Item> {
        val items = mutableListOf<Item>()
        (0..40).map { items.add(possibleItems.random()) }
        return items
    }
}

data class Item(
    val title: String,
    @DrawableRes val icon: Int
)

Run

Copy the code or download it in the link below, build and run.

Reference

Here are the reference links:

Number Link
1. Download Example
2. Follow code author
3. Code: Apache 2.0 License

Concepts 2: Best Android Image Carousel Libraries – 2021

Proceed to the next page to view the best android image carousel libraries this year.