Probably many of you (as me), have problem with creating ViewPager
with bottom dots, like this:
How do you create such an Android ViewPager?
Probably many of you (as me), have problem with creating ViewPager
with bottom dots, like this:
How do you create such an Android ViewPager?
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:
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 withViewPager
, butTabLayout
will be next toViewPager
, not over it.
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 connectTabLayout
withViewPager
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
.
<?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>
<?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>
<?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"
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();
}
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);
When you want something similar to this with the latest ViewPager2 and Kotlin
Everything is self-explaining, no need to explain!
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" />
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)...
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.
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.)
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vpImage"
android:layout_width="match_parent"
android:layout_height="@dimen/_150sdp" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tlImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabBackground="@drawable/selector_product_image"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabMaxWidth="12dp"
app:tabRippleColor="@null" />
</androidx.appcompat.widget.LinearLayoutCompat>
ImageAdapter imageAdapter = new ImageAdapter(getActivity(), arrayListSlider);
binding.vpImage.setOffscreenPageLimit(1);
binding.vpImage.setAdapter(imageAdapter);
binding.tlImage.setupWithViewPager(binding.vpImage);
selector_product_image.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/image_selected" android:state_selected="true" />
<item android:drawable="@drawable/image_unselected" />
</selector>
image_selected.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="@color/colorAccent" />
</shape>
</item>
</layer-list>
image_unselected.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="@color/colorPrimary" />
</shape>
</item>
</layer-list>
ImageAdapter.java
class ImageAdapter extends PagerAdapter {
private Context context;
private ArrayList<ImageModel> arrayList;
private LayoutInflater layoutInflater;
public ImageAdapter(Context context, ArrayList<ImageModel> arrayList) {
this.context = context;
this.arrayList = arrayList;
this.layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return arrayList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == ((View) o);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = layoutInflater.inflate(R.layout.row_slider_image, container, false);
AppCompatImageView ivProductImage = view.findViewById(R.id.ivProductImage);
if (!TextUtils.isEmpty(arrayList.get(position).getImage())) {
Glide.with(context)
.load(arrayList.get(position).getImage())
.apply(new RequestOptions().placeholder(R.drawable.no_image).error(R.drawable.no_image))
.into(ivProductImage);
}
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
row_slider_image.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivProductImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="@drawable/no_image" />
</androidx.appcompat.widget.LinearLayoutCompat>
For Viewpager2, follow the same steps which suggested by @RediOne1 and in the activity or fragment use below code to attach tablayout with viwpager2
val tabLayoutMediator = TabLayoutMediator(binding.tabLayout,binding.offersVp) { _, _ -> }
tabLayoutMediator.attach()
Your xml
<RelativeLayout
android:id="@+id/rl_speed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/ll_dashboard_buttons"
android:layout_below="@+id/ib_menu">
<com.smart.gps.speedometer.app.utils.SmartViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</com.smart.gps.speedometer.app.utils.SmartViewPager>
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabBackground="@drawable/tab_selector"
app:tabIndicatorHeight="0dp"
app:tabGravity="center"
/>
create a drawable. right click on drawable -> new -> Drawable file resource name that file
tab_selector.xml
<item android:drawable="@drawable/selected_tab"
android:state_selected="true"/>
<item android:drawable="@drawable/unselected_tab"/>
Now there is two more xml files. create two more xml files with respected name. these are the selecter indicator and unselected indicator
selected_tab.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="4dp"
android:useLevel="false">
<solid android:color="@color/highspeed"/>
</shape>
</item>
</layer-list>
unselected_tab.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="2dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray"/>
</shape>
</item>
</layer-list>
Place ViewFlipper and viewFlipper_linear_dot_lay(Linearlayout) on samebaseline and follow the below one
viewFlipper_linear_dot_lay= (LinearLayout) findViewById(R.id.dots_lay);
setupDotsOnViewPager(images_viewFlipper);
for (int i = 0; i < images_viewFlipper.size(); i++) {
//Add Images to ViewFlipper
}
private void setupDotsOnViewPager(ArrayList images_viewFlipper) {
images_linear = new ImageView[images_viewFlipper.size()];
for (int i = 0; i < images_linear.length; i++) {
images_linear[i] = new ImageView(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(5, 0, 5, 0);
params.gravity = Gravity.BOTTOM | Gravity.CENTER;
images_linear[i].setLayoutParams(params);
images_linear[i].setImageResource(R.drawable.unselected);
viewFlipper_linear_dot_lay.addView(images_linear[i]);
viewFlipper_linear_dot_lay.bringToFront();
}
}
And OnRight & OnLeft getsures place the below code
for (int i = 0; i < images_linear.length; i++) {
images_linear[i].setImageResource(R.drawable.unselected);
}
images_linear[viewFlipper.getDisplayedChild()].setImageResource(R.drawable.selected);
Add dependencies > Sync the Gradle
implementation 'com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2'
In your java code
dotsIndicator = (DotsIndicator) findViewById(R.id.dots_indicator3);
myViewPagerAdapter = new MyViewPagerAdapter();
viewPager.setAdapter(myViewPagerAdapter);
viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
dotsIndicator.setViewPager(viewPager);
In your layout
<com.tbuonomo.viewpagerdotsindicator.SpringDotsIndicator
android:id="@+id/spring_dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:dampingRatio="0.5"
app:dotsColor="@color/material_white"
app:dotsStrokeColor="@color/material_yellow"
app:dotsCornerRadius="2dp"
app:dotsSize="16dp"
app:dotsSpacing="6dp"
app:dotsStrokeWidth="2dp"
app:stiffness="300"
/>