How do you create an Android View Pager with a dots indicator?

122,813

Solution 1

All we need are: ViewPager, TabLayout and 2 drawables for selected and default dots.

Firstly, we have to add TabLayout to our screen layout, and connect it with ViewPager. We can do this in two ways:


Nested TabLayout in ViewPager

<androidx.viewpager.widget.ViewPager
    android:id="@+id/photos_viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.tabs.TabLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</androidx.viewpager.widget.ViewPager>

In this case TabLayout will be automatically connected with ViewPager, but TabLayout will be next to ViewPager, not over it.


Separate TabLayout

<androidx.viewpager.widget.ViewPager
    android:id="@+id/photos_viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

<com.google.android.material.tabs.TabLayout
    android:id="@+id/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

In this case, we can put TabLayout anywhere, but we have to connect TabLayout with ViewPager programmatically

ViewPager pager = (ViewPager) view.findViewById(R.id.photos_viewpager);
PagerAdapter adapter = new PhotosAdapter(getChildFragmentManager(), photosUrl);
pager.setAdapter(adapter);

TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(pager, true);

Once we created our layout, we have to prepare our dots. So we create three files: selected_dot.xml, default_dot.xml and tab_selector.xml.


selected_dot.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="8dp"
            android:useLevel="false">
            <solid android:color="@color/colorAccent"/>
        </shape>    
    </item>
</layer-list>

default_dot.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="8dp"
            android:useLevel="false">
            <solid android:color="@android:color/darker_gray"/>
        </shape>    
    </item>
</layer-list>

tab_selector.xml

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

    <item android:drawable="@drawable/selected_dot"
          android:state_selected="true"/>

    <item android:drawable="@drawable/default_dot"/>
</selector>

Now we need to add only 3 lines of code to TabLayout in our XML layout.

app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"

Solution 2

First Create a layout, in that give one LinerLayout for Dots which show over your View Pager

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

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>


    <LinearLayout
        android:id="@+id/pager_dots"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="10dp"
        android:background="@android:color/transparent"
        android:gravity="center_horizontal"
        android:orientation="horizontal">
    </LinearLayout>

</RelativeLayout>

After that create 2 drawables

1. Unselected Drawable

<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/transparent"/>
    <size android:width="12dp" android:height="12dp"/>

    <stroke android:width="1dp" android:color="#ffffff"/>
</shape>

2. Selected Drawable

<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/transparent"/>
    <size android:width="12dp" android:height="12dp"/>

    <stroke android:width="1dp" android:color="#000000"/>
</shape>

After that set adapter

private LinearLayout llPagerDots;
private ViewPager viewPager;
private ArrayList<String> eventImagesUrl;
private HomeViewPagerAdapter homeViewPagerAdapter;
private ImageView[] ivArrayDotsPager;

public void setUpViewPager() {
    viewPager = (ViewPager) findViewById(R.id.view_pager);
    llPagerDots = (LinearLayout) findViewById(R.id.pager_dots);

    homeViewPagerAdapter = new HomeViewPagerAdapter(mContext, eventImagesUrl);

    viewPager.setAdapter(homeViewPagerAdapter);

    setupPagerIndidcatorDots();

    ivArrayDotsPager[0].setImageResource(R.drawable.page_indicator_selected);

    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            for (int i = 0; i < ivArrayDotsPager.length; i++) {
                ivArrayDotsPager[i].setImageResource(R.drawable.page_indicator_unselected);
            }
            ivArrayDotsPager[position].setImageResource(R.drawable.page_indicator_selected);
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });
}

Create a method setupPagerIndidcatorDots() :

private void setupPagerIndidcatorDots() {
    ivArrayDotsPager = new ImageView[eventImagesUrl.size()];
    for (int i = 0; i < ivArrayDotsPager.length; i++) {
        ivArrayDotsPager[i] = new ImageView(getActivity());
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.setMargins(5, 0, 5, 0);
        ivArrayDotsPager[i].setLayoutParams(params);
        ivArrayDotsPager[i].setImageResource(R.drawable.page_indicator_unselected);
        //ivArrayDotsPager[i].setAlpha(0.4f);
        ivArrayDotsPager[i].setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                view.setAlpha(1);
            }
        });
        llPagerDots.addView(ivArrayDotsPager[i]);
        llPagerDots.bringToFront();
    }

Solution 3

You can check out my library to handle your request : https://github.com/tommybuonomo/dotsindicator

In your XML layout

  <com.tbuonomo.viewpagerdotsindicator.DotsIndicator
      android:id="@+id/dots_indicator"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true"
      app:dotsColor="@color/colorPrimary"
      app:dotsSize="16dp"
      app:dotsWidthFactor="3"
      />

In your Java code

dotsIndicator = (DotsIndicator) findViewById(R.id.dots_indicator);
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new ViewPagerAdapter();
viewPager.setAdapter(adapter);
dotsIndicator.setViewPager(viewPager);

Solution 4

When you want something similar to this with the latest ViewPager2 and Kotlin

Everything is self-explaining, no need to explain!

enter image description here

1. Your Activity or Fragment

val imageList = listOf(
        ImageModel(R.drawable.offer1),
        ImageModel(R.drawable.splash),
        ImageModel(R.drawable.offer1),
        ImageModel(R.drawable.splash2)
    )

    val adapter = HomeOffersAdapter()
    adapter.setItem(imageList)
    photos_viewpager.adapter = adapter

    TabLayoutMediator(tab_layout, photos_viewpager) { tab, position ->
    }.attach()
}

2. Layout

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_200">

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/photos_viewpager"
    android:layout_width="match_parent"
    android:layout_height="@dimen/dp_200" />

<com.google.android.material.tabs.TabLayout
    android:id="@+id/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_gravity="bottom|center"
    app:tabBackground="@drawable/tab_selector"
    app:tabGravity="center"
    app:tabIndicatorHeight="0dp"
    app:tabSelectedTextColor="@android:color/transparent"
    app:tabTextColor="@android:color/transparent" />

3. Drawable: tab_selector.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/dot_selected" android:state_selected="true" />
<item android:drawable="@drawable/dot_default" />

4. Drawable: dot_selected.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thickness="@dimen/dp_8"
android:useLevel="false">

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

<stroke
    android:width="@dimen/dp_1"
    android:color="@android:color/white" />

5. Drawable: dot_default.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thickness="@dimen/dp_8"
android:useLevel="false">

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

<stroke
    android:width="@dimen/dp_1"
    android:color="@android:color/white" />

6. Adapter

class HomeOffersAdapter : RecyclerView.Adapter<HomeOffersAdapter.HomeOffersViewHolder>() {

private var list: List<ImageModel> = listOf()


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOffersViewHolder {
    return HomeOffersViewHolder(parent)
}


override fun onBindViewHolder(holder: HomeOffersViewHolder, position: Int) {
    holder.bind(list[position])
}

fun setItem(list: List<ImageModel>) {
    this.list = list
    notifyDataSetChanged()
}

override fun getItemCount(): Int = list.size

class HomeOffersViewHolder constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {

    constructor(parent: ViewGroup) : this(
        LayoutInflater.from(parent.context).inflate(
            R.layout.pager_item,
            parent, false
        )
    )

    fun bind(imageModel: ImageModel) {
        itemView.offerImage.setImageResource(imageModel.image)
    }
}

}

7. Layout: pager_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">

<androidx.appcompat.widget.AppCompatImageView
    android:id="@+id/offerImage"
    android:layout_width="match_parent"
    android:layout_height="@dimen/dp_200"
    android:adjustViewBounds="true"
    android:scaleType="fitXY"
    tools:src="@drawable/offer1" />

Solution 5

2021, how to actually do it. ViewPager2 only.

Refer to this excellent short article, which has a couple of problems:

https://medium.com/@adrian.kuta93/android-viewpager-with-dots-indicator-a34c91e59e3a

Starting with a normal Android Studio default project as of 2021, with a reasonably new minimum (24 currently)...

General concept:

Make a standard TabLayout, but replace each "tab unit" with "a little dot" rather than the usual text.

In TabLayout, you can indeed replace each "tab unit" using "tabBackground":

        app:tabBackground="@drawable/tab_selector"

So add the following to the XML of your screen, which has a ViewPager2:

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center"
        android:background="#00FFFFFF"
        app:tabBackground="@drawable/tab_selector"
        app:tabIndicatorGravity="center"
        app:tabIndicatorHeight="0dp"/>

Notice carefully we are replacing each and every one of the "tab units" in the TabLayout, with our own "tab_selector".

To be completely clear, "tabBackground" means the individual little "tab units", not the whole tab bar system.

(Aside, note that the two tricks tabIndicatorGravity and tabIndicatorHeight will indeed get rid of the "boxes" that are the usual "tab units".)

Next create three drawables in the obvious way, tab_selector and the two different dots. See the above article or 100s of examples on this page.

The magic code:

In your onCreate have the expected code ...

viewPager = findViewById(R.id.simple_slide_pager);
tab_layout = findViewById(R.id.tab_layout);
viewPager.setAdapter(new ScreenSlidePagerAdapter(this));

and then here at last is the magic code fragment to make it work. Follow the above by:

Up to date for 2021:

TabLayoutMediator tabLayoutMediator =
  new TabLayoutMediator(tab_layout, viewPager, true,
    new TabLayoutMediator.TabConfigurationStrategy() {
      @Override public void onConfigureTab(
        @NonNull TabLayout.Tab tab, int position) { }
    }
);
tabLayoutMediator.attach();

It's done.

(Inside onConfigureTab you can do any sound effects or whatever the heck might be needed. For shorter syntax see the key comment by @F1iX above.)

Share:
122,813

Related videos on Youtube

RediOne1
Author by

RediOne1

Android enthusiast and professional developer since 2014.

Updated on April 12, 2022

Comments

  • RediOne1
    RediOne1 about 2 years

    Probably many of you (as me), have problem with creating ViewPager with bottom dots, like this: enter image description here

    How do you create such an Android ViewPager?

    • Fattie
      Fattie about 3 years
      2021 If you are pure ViewPager2 and 24+, the critical code for TabLayoutMediator has changed. I put in the latest in an answer below. What a PITA !
  • Erfan GLMPR
    Erfan GLMPR over 7 years
    you better put this in the documentation section. it helped though.
  • RediOne1
    RediOne1 over 7 years
  • Amit Singh
    Amit Singh almost 7 years
    Hey, can you help me in reducing the gaps between the dots?
  • RediOne1
    RediOne1 almost 7 years
    @AmitSingh You can use app:tabMaxWidth in TabLayout
  • dawanse
    dawanse almost 7 years
    can't put those indicator in the bottom of layout overlapping ViewPager. When using LinearLayout it goes below the ViewPager and using RelativeLayout also same.
  • RediOne1
    RediOne1 almost 7 years
    @dawn TabLayout should be placed as separated widget (like in example Separate TabLayout. If you put TabLayout as a child of ViewPager it'll be autmoaticaly placed below ViewPager and not overlap it.
  • Seven
    Seven over 6 years
    I do not know if I did not follow the instructions correctly but I also had to add the following in order to show only the dots with a decent space between them: app:tabMaxWidth="30dp" app:tabTextColor="@color/transparent2" app:tabSelectedTextColor="@color/transparent2" app:tabIndicatorHeight="0dp" android:layout_gravity="bottom|center"
  • Fat Monk
    Fat Monk over 6 years
    I'm getting an error very like the one described in stackoverflow.com/questions/31712563/… when trying to use this. The solutions suggested for the linked question don;t resolve the problem either. The app compiles and runs OK ad there are no errors in the editor, but when the activity tries to inflate the app crashes.
  • Fat Monk
    Fat Monk over 6 years
    So simple to use and looks great especially with the animations... thanks! I did, however, have to add tools:replace="android:label" to the application tag in my AndroidManifest.xml to get rid of an error about that tag being defined twice and causing a gradle build fail.
  • Fat Monk
    Fat Monk over 6 years
    I am getting a gradle error, though Error:(39, 20) All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes). Found versions 27.0.0, 25.3.1. Examples include 'com.android.support:support-compat:27.0.0' and 'com.android.support:animated-vector-drawable:25.3.1' - how can i fix this?
  • dev_mg99
    dev_mg99 over 6 years
    For remove spacing between dots app:tabPaddingStart="@dimen/margin_7.5" app:tabPaddingEnd="@dimen/margin_7.5"
  • eC Droid
    eC Droid over 6 years
    I have only Viewpager, please suggest what to do.
  • Sam
    Sam almost 6 years
    I followed your answer and everything works really smoothly, thank you. I have one question though, in my app the TabLayout is always in the far top tried many things to put it in the bottom with no luck. I placed it nested in the ViewPager and also separately. still the same problem. Any ideas?
  • Sam
    Sam almost 6 years
    Thank you I followed this link to fix the TabLayout position on the screen
  • CoolMind
    CoolMind almost 6 years
    Probably SmartViewPager is your custom view.
  • CoolMind
    CoolMind almost 6 years
    After adding app:tabBackground="@drawable/tab_selector" and others to TabLayout an application stopped compiling wih error.
  • CoolMind
    CoolMind almost 6 years
    @RediOne1, 3 lines of exception like AGPBI: {"kind":"error","text":"error: attribute \u0027com.example:tabBackground\u0027 not found.","sources":[{"file":"C:\\Users\\user\\AndroidStudioPr‌​ojects\\sample\\app\‌​\src\\main\\res\\lay‌​out\\activity_start.‌​xml","position":{"st‌​artLine":53}}],"orig‌​inal":"","tool":"AAP‌​T"}. After removing app attributes it crushes with exception Caused by: android.view.InflateException: Binary XML file line #54: Error inflating class android.support.design.widget.TabLayout. I write in Kotlin.
  • CoolMind
    CoolMind almost 6 years
    And what is HomeViewPagerAdapter?
  • RediOne1
    RediOne1 almost 6 years
    @CoolMind Do you have xmlns:app="http://schemas.android.com/apk/res-auto" on top of your layout file? If you can, please paste whole layout file to pastebin.com and paste link here.
  • CoolMind
    CoolMind almost 6 years
    @RediOne1, yes, i have this attribute. I removed TabLayout, because it is not needed. But if you wish, I can create a sample layout for you.
  • RediOne1
    RediOne1 almost 6 years
    @CoolMind Ok, do it, please.
  • amity
    amity over 5 years
    Modify onPageSelect little to work perfectly as below override fun onPageSelected(position: Int) { for (i in 0 until ivArrayDotsPager!!.size) { if (i != position) ivArrayDotsPager!![i].setImageDrawable(ContextCompat.getDraw‌​able(applicationCont‌​ext, R.drawable.unselected_dot)) else ivArrayDotsPager!![i].setImageDrawable(ContextCompat.getDraw‌​able(applicationCont‌​ext, R.drawable.selected_dot)) } linearLayout!!.bringToFront()}
  • Touré Holder
    Touré Holder over 5 years
    @RediOne1 The xml could just be a <shape>, instead of a <shape> in an <item> in a <layer-list>, right?
  • RediOne1
    RediOne1 over 5 years
    @Touré Holder Yes
  • Shivam Kumar
    Shivam Kumar over 5 years
    how to scroll automatically DotsIndicator
  • Black_Zerg
    Black_Zerg about 5 years
    Thanks! Just need place TabLayout outside ViewPager to place below ViewPager
  • Maveňツ
    Maveňツ about 5 years
    unable to find tabIndicatorHeight
  • Jordan H
    Jordan H about 5 years
    I had to add implementation 'com.android.support:support-v4:x.x.x' into the dependencies, otherwise I got errors like Android resource linking failed attribute tabBackground (aka com.company.appname:tabBackground) not found.
  • Aniket
    Aniket over 4 years
    <com.google.android.material.tabs.TabLayout use this in androidx
  • Admin
    Admin over 4 years
    hey ..I implemented this..I used <com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator> but dots are getting displayed at extreme left ..how to align dots at centre?
  • IgorGanapolsky
    IgorGanapolsky about 4 years
    How to do this with ViewPager2 and TabLayoutMediator?
  • RediOne1
    RediOne1 about 4 years
    @IgorGanapolsky I prepared simple demo for ViewPager and ViewPager2: github.com/AdrianKuta/ViewPagerDotsIndicator I will also update Answer, thank you for this question ;-)
  • Hari Kiran
    Hari Kiran almost 4 years
    should a separate tablayout be created if I want to display dots below viewpager?
  • Sai
    Sai almost 4 years
    And finally, to disable tab touch events use this tab_layout.touchables?.forEach { it.isEnabled = false }
  • Sai
    Sai almost 4 years
    To play with the space/gap between tabs and to remove indication use this app:tabPaddingStart="8dp" app:tabPaddingEnd="8dp" app:tabIndicatorHeight="0dp"
  • meekash55
    meekash55 over 3 years
    nice ,but this uses AndroidX. I am in situation that i cannot migrate my project into AndroidX now.
  • F1iX
    F1iX over 3 years
    For ViewPager2 with Java, you need viewPager = findViewById(R.id.pager);pagerAdapter = new ScreenSlidePagerAdapter(this); viewPager.setAdapter(pagerAdapter); tabLayout = findViewById(R.id.tabs); new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {}).attach();
  • Fattie
    Fattie about 3 years
    bravo @F1iX exactly
  • Tri Mueri Sandes
    Tri Mueri Sandes about 3 years
    @meekash55 I prefer you migrate to android X at the beginning build App, cause there many cases that make you should move to Android X, this case is one of the samples.
  • Tri Mueri Sandes
    Tri Mueri Sandes about 3 years
    I think to migrate not took too long, but this is your decision. adjust to your situation and conditions
  • Bungles
    Bungles over 2 years
    This was a great help, thanks. When I set <android:thickness="16dp"> in the two DOT.XML files, I'd expect the dot bar to adjust size accordingly - but it does not. I need them bigger for our audience, so was hoping you could direct me where to make that change so they're larger on screen?
  • Fattie
    Fattie over 2 years
    hell that's a tough one, I'm rusty. i think you have to actually make the thing that holds it (the tab_selector ?) BIGGER. that determines the size? I am sorry ... it s a good question, ask a new question about it !