Rohit Kumar Sehrawat
Rohit Kumar Sehrawat

Reputation: 243

Image Slider with horizontal Progress Bar indicator in android

I want to create an image slider with a horizontal progress bar. Here is the final output I am looking at.

enter image description here

I am using an example that is currently listed below. I want to replace the dot slider with a horizontal progress bar with a screenshot of the current slider

At the moment, I have a dot indicator

Default indicatior.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>

    <shape android:innerRadius="0dp"
        android:shape="ring"
        android:thickness="3dp"
        android:useLevel="false">

        <solid android:color="@android:color/darker_gray" />

    </shape>

</item>

selected.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">


<item>

    <shape android:innerRadius="0dp"
        android:shape="ring"
        android:thickness="3dp"
        android:useLevel="false">

        <solid android:color="@color/teal_200" />

    </shape>

</item>

I am using view page to load this image slider

 <androidx.viewpager.widget.ViewPager
        android:id="@+id/slider_pager"
        android:layout_width="0dp"
        android:layout_height="220dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" >

    </androidx.viewpager.widget.ViewPager>

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="180dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/slider_pager"
        app:tabBackground="@drawable/indicator_selector"
        app:tabGravity="center"
        app:tabIndicatorHeight="0dp"
        android:background="@android:color/transparent">

    </com.google.android.material.tabs.TabLayout>

In Java

    private ViewPager sliderpager;

private TabLayout indicator;

//

indicator.setupWithViewPager(sliderpager,true);

enter image description here

Upvotes: 0

Views: 883

Answers (1)

Charles Neo
Charles Neo

Reputation: 1

I have an easy way to achieve what do you want, here is dumb way i do:

First update your layout like this:

            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/rvPager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_constraintTop_toBottomOf="@id/account_balance"
                app:layout_constraintBottom_toTopOf="@+id/rv_videos">

                <androidx.viewpager2.widget.ViewPager2
                    android:id="@+id/viewPager"
                    android:layout_width="match_parent"
                    android:layout_height="150dp"
                    tools:listitem="@layout/carousel_item_layout"
                    app:layout_constraintTop_toTopOf="parent"/>

                <LinearLayout
                    android:id="@+id/indicatorLayout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentBottom="true"
                    android:gravity="center"
                    android:orientation="horizontal"
                    android:paddingBottom="5dp"
                    app:layout_constraintBottom_toBottomOf="parent">
                </LinearLayout>

            </androidx.constraintlayout.widget.ConstraintLayout>

Then apply this code to implements progressbar as indicator with loading and movement animations

private lateinit var viewPager: ViewPager2
private lateinit var carouselAdapter: CarouselAdapter
private val handler = Handler(Looper.getMainLooper())
private val slideRunnable = Runnable { viewPager.currentItem = (viewPager.currentItem + 1) % carouselAdapter.itemCount }
private var progressBarTimer: CountDownTimer? = null
private val progressBars = mutableListOf<ProgressBar>()

Added this code in OnViewCreated

viewPager = binding.viewPager

    val carouselItems = listOf(
        CarouselItem("https://picsum.photos/id/237/200/300"),
        CarouselItem("https://picsum.photos/id/238/200/300"),
        CarouselItem("https://picsum.photos/id/239/200/300")
    )

        carouselAdapter = CarouselAdapter(
            carouselItems,
            viewPager,
            onPageChangeListener = { _, _, _ ->
                // Handle page change
            }
        )
        viewPager.adapter = carouselAdapter

        val indicatorLayout = view?.findViewById<LinearLayout>(R.id.indicatorLayout)

        indicatorLayout?.removeAllViews()

        for (i in carouselItems.indices) {
            val progressBar = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal).apply {
                layoutParams = LinearLayout.LayoutParams(6.dpToPx(), 6.dpToPx()).apply {
                    if (i > 0) {
                        marginStart = 5.dpToPx()
                    }
                }
                progressDrawable = ContextCompat.getDrawable(context, R.drawable.bar_carousel_round)
            }
            indicatorLayout?.addView(progressBar)
            progressBars.add(progressBar)
        }

        val params = progressBars[0].layoutParams
        params.width = 30.dpToPx()
        params.height = 6.dpToPx()
        progressBars[0].layoutParams = params

        viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                progressBars.forEach { it.progress = 0 }

                for (i in progressBars.indices) {
                    progressBars[i].animateSize(6.dpToPx(), 6.dpToPx())
                }

                progressBars[position].animateSize(30.dpToPx(), 6.dpToPx())

                progressBarTimer?.cancel()

                progressBarTimer = object : CountDownTimer(5000, 50) {
                    override fun onTick(millisUntilFinished: Long) {
                        val progress = ((5000 - millisUntilFinished) / 50).toInt()
                        progressBars[position].progress = progress
                    }

                    override fun onFinish() {
                        viewPager.currentItem = (viewPager.currentItem + 1) % carouselAdapter.itemCount
                    }
                }.start()
            }
        })
        handler.postDelayed(slideRunnable, 5000)
    }

and add new Extension

fun Int.dpToPx(): Int {
    val metrics = Resources.getSystem().displayMetrics
    return (this * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)).roundToInt()
}

fun ProgressBar.animateSize(targetWidth: Int, targetHeight: Int) {
    val initialWidth = this.width
    val initialHeight = this.height

    val widthAnimator = ValueAnimator.ofInt(initialWidth, targetWidth)
    val heightAnimator = ValueAnimator.ofInt(initialHeight, targetHeight)

    widthAnimator.addUpdateListener { animation ->
        val params = this.layoutParams
        params.width = animation.animatedValue as Int
        this.layoutParams = params
    }

    heightAnimator.addUpdateListener { animation ->
        val params = this.layoutParams
        params.height = animation.animatedValue as Int
        this.layoutParams = params
    }

    widthAnimator.start()
    heightAnimator.start()
}

Don't Forget This

override fun onDestroyView() {
    super.onDestroyView()
    handler.removeCallbacks(slideRunnable)
    progressBarTimer?.cancel()
}

And this CarouselItems Model

data class CarouselItem(val imageUrl: String)

It's will look like this Preview

I Consider make this as library may someone can help?

Upvotes: 0

Related Questions