I want to add 3 bottom dots to my ViewPager, like this.
I use FragmentActivity and support library ViewPager.
I want to add 3 bottom dots to my ViewPager, like this.
I use FragmentActivity and support library ViewPager.
No need for that much code.
You can do all this stuff without coding so much by using only viewpager with tablayout.
Your main 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="wrap_content">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
<android.support.design.widget.TabLayout
android:id="@+id/tabDots"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"/>
</RelativeLayout>
Hook up your UI elements inactivity or fragment as follows:
Java Code:
mImageViewPager = (ViewPager) findViewById(R.id.pager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabDots);
tabLayout.setupWithViewPager(mImageViewPager, true);
That's it, you are good to go.
You will need to create the following xml resource file in the drawable folder.
tab_indicator_selected.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorAccent"/>
</shape>
tab_indicator_default.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="oval"
android:thickness="2dp"
android:useLevel="false">
<solid android:color="@android:color/darker_gray"/>
</shape>
tab_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/tab_indicator_selected"
android:state_selected="true"/>
<item android:drawable="@drawable/tab_indicator_default"/>
</selector>
Feeling as lazy as I am? Well, all the above code is converted into a library!
Usage
Add the following in your gradle:
implementation 'com.chabbal:slidingdotsplash:1.0.2'
Add the following to your Activity or Fragment layout.
<com.chabbal.slidingdotsplash.SlidingSplashView
android:id="@+id/splash"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:imageResources="@array/img_id_arr"/>
Create an integer array in strings.xml e.g.
<integer-array name="img_id_arr">
<item>@drawable/img1</item>
<item>@drawable/img2</item>
<item>@drawable/img3</item>
<item>@drawable/img4</item>
</integer-array>
Done!
Extra in order to listen page changes use addOnPageChangeListener(listener);
Github link.
My handmade solution:
In the layout:
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/dots"
/>
And in the Activity
private final static int NUM_PAGES = 5;
private ViewPager mViewPager;
private List<ImageView> dots;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
addDots();
}
public void addDots() {
dots = new ArrayList<>();
LinearLayout dotsLayout = (LinearLayout)findViewById(R.id.dots);
for(int i = 0; i < NUM_PAGES; i++) {
ImageView dot = new ImageView(this);
dot.setImageDrawable(getResources().getDrawable(R.drawable.pager_dot_not_selected));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
dotsLayout.addView(dot, params);
dots.add(dot);
}
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
selectDot(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
public void selectDot(int idx) {
Resources res = getResources();
for(int i = 0; i < NUM_PAGES; i++) {
int drawableId = (i==idx)?(R.drawable.pager_dot_selected):(R.drawable.pager_dot_not_selected);
Drawable drawable = res.getDrawable(drawableId);
dots.get(i).setImageDrawable(drawable);
}
}
viewPager.addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
switch (position) {
case 0:
img_page1.setImageResource(R.drawable.dot_selected);
img_page2.setImageResource(R.drawable.dot);
img_page3.setImageResource(R.drawable.dot);
img_page4.setImageResource(R.drawable.dot);
break;
case 1:
img_page1.setImageResource(R.drawable.dot);
img_page2.setImageResource(R.drawable.dot_selected);
img_page3.setImageResource(R.drawable.dot);
img_page4.setImageResource(R.drawable.dot);
break;
case 2:
img_page1.setImageResource(R.drawable.dot);
img_page2.setImageResource(R.drawable.dot);
img_page3.setImageResource(R.drawable.dot_selected);
img_page4.setImageResource(R.drawable.dot);
break;
case 3:
img_page1.setImageResource(R.drawable.dot);
img_page2.setImageResource(R.drawable.dot);
img_page3.setImageResource(R.drawable.dot);
img_page4.setImageResource(R.drawable.dot_selected);
break;
default:
break;
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
I created a library to address the need for a page indicator in a ViewPager. My library contains a View called DotIndicator. To use my library, add compile 'com.matthew-tamlin:sliding-intro-screen:3.2.0' to your gradle build file.
The View can be added to your layout by adding the following:
<com.matthewtamlin.sliding_intro_screen_library.indicators.DotIndicator
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:numberOfDots=YOUR_INT_HERE
app:selectedDotIndex=YOUR_INT_HERE/>
The above code perfectly replicates the functionality of the dots on the Google Launcher homescreen, however if you want to further customise it then the following attributes can be added:
app:unselectedDotDiameter and app:selectedDotDiameter to set the diameters of the dotsapp:unselectedDotColor and app:selectedDotColor to set the colors of the dotsapp:spacingBetweenDots to change the distance between the dotsapp:dotTransitionDuration to set the time for animating the change from small to big (and back)Additionally, the view can be created programatically using:
DotIndicator indicator = new DotIndicator(context);
Methods exist to modify the properties, similar to the attributes. To update the indicator to show a different page as selected, just call method indicator.setSelectedItem(int, true) from inside ViewPager.OnPageChangeListener.onPageSelected(int).
Here's an example of it in use:
If you're interested, the library was actually designed to make intro screens like the one shown in the above gif.
Github source available here: https://github.com/MatthewTamlin/SlidingIntroScreen
ViewPagerIndicator has not been updated since 2012 and got several bugs that were never fixed.
I finally found an alternative with this light library that displays nice dots for the viewpager, here is the link:
https://github.com/ongakuer/CircleIndicator
Easy to implement!
I thought of posting a simpler solution for the above problem and indicator numbers can be dynamically changed with only changing one variable value dotCounts=x what I did goes like this.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android:height="8dp" android:width="8dp"/>
<solid android:color="@color/image_item_selected_for_dots"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android:height="8dp" android:width="8dp"/>
<solid android:color="@color/image_item_unselected_for_dots"/>
</shape>
viewPager in your Layout XML file. <RelativeLayout
android:id="@+id/viewPagerIndicator"
android:layout_width="match_parent"
android:layout_below="@+id/banner_pager"
android:layout_height="wrap_content"
android:gravity="center">
<LinearLayout
android:id="@+id/viewPagerCountDots"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:gravity="center"
android:orientation="horizontal" />
</RelativeLayout>
private int dotsCount = 5; //No of tabs or images
private ImageView[] dots;
LinearLayout linearLayout;
private void drawPageSelectionIndicators(int mPosition){
if(linearLayout!=null) {
linearLayout.removeAllViews();
}
linearLayout=(LinearLayout)findViewById(R.id.viewPagerCountDots);
dots = new ImageView[dotsCount];
for (int i = 0; i < dotsCount; i++) {
dots[i] = new ImageView(context);
if(i==mPosition)
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.item_selected));
else
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.item_unselected));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(4, 0, 4, 0);
linearLayout.addView(dots[i], params);
}
}
drawPageSelectionIndicators(0);
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
drawPageSelectionIndicators(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
Following is my proposed solution.
A) Following is my activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="schneider.carouseladventure.MainActivity">
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewpager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RelativeLayout
android:id="@+id/viewPagerIndicator"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_alignParentBottom="true"
android:layout_marginTop="5dp"
android:gravity="center">
<LinearLayout
android:id="@+id/viewPagerCountDots"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:gravity="center"
android:orientation="horizontal" />
</RelativeLayout>
</RelativeLayout>
B) pager_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/imageView" />
</LinearLayout>
C) MainActivity.java
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener, View.OnClickListener {
int[] mResources = {R.drawable.nature1, R.drawable.nature2, R.drawable.nature3, R.drawable.nature4,
R.drawable.nature5, R.drawable.nature6
};
ViewPager mViewPager;
private CustomPagerAdapter mAdapter;
private LinearLayout pager_indicator;
private int dotsCount;
private ImageView[] dots;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
pager_indicator = (LinearLayout) findViewById(R.id.viewPagerCountDots);
mAdapter = new CustomPagerAdapter(this, mResources);
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(0);
mViewPager.setOnPageChangeListener(this);
setPageViewIndicator();
}
private void setPageViewIndicator() {
Log.d("###setPageViewIndicator", " : called");
dotsCount = mAdapter.getCount();
dots = new ImageView[dotsCount];
for (int i = 0; i < dotsCount; i++) {
dots[i] = new ImageView(this);
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(4, 0, 4, 0);
final int presentPosition = i;
dots[presentPosition].setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mViewPager.setCurrentItem(presentPosition);
return true;
}
});
pager_indicator.addView(dots[i], params);
}
dots[0].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
}
@Override
public void onClick(View v) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
Log.d("###onPageSelected, pos ", String.valueOf(position));
for (int i = 0; i < dotsCount; i++) {
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
}
dots[position].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
if (position + 1 == dotsCount) {
} else {
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
D) CustomPagerAdapter.java
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class CustomPagerAdapter extends PagerAdapter {
private Context mContext;
LayoutInflater mLayoutInflater;
private int[] mResources;
public CustomPagerAdapter(Context context, int[] resources) {
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mResources = resources;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.pager_item,container,false);
ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView);
imageView.setImageResource(mResources[position]);
/* LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(950, 950);
imageView.setLayoutParams(layoutParams);*/
container.addView(itemView);
return itemView;
}
@Override
public void destroyItem(ViewGroup collection, int position, Object view) {
collection.removeView((View) view);
}
@Override
public int getCount() {
return mResources.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
E) selecteditem_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android:height="12dip" android:width="12dip"/>
<solid android:color="#7e7e7e"/>
</shape>
F) nonselecteditem_dot.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android:height="12dip" android:width="12dip"/>
<solid android:color="#d3d3d3"/>
</shape>
The solution provided by @Junaid can be simplified.
Instead of creating a selector (tab_selector.xml) you can simply set the drawables on the TabLayout directly.
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:tabBackground="@drawable/tab_indicator_default"
app:tabIndicator="@drawable/tab_indicator_selected"
app:tabIndicatorColor="<youColor>"
app:tabIndicatorGravity="center"
app:tabIndicatorHeight="8dp" />
app:tabIndicatorHeight is set to the same value as in tab_indicator_selected.xml.
Following this approach you keep the default animation of the TabLayout.
Programmatically, you have to set the tab as selected:
object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
val tab = binding.tabLayout.getTabAt(position)
binding.tabLayout.selectTab(tab, true)
}
}
Please note that I used ViewPager2. There should be equivalent method using ViewPager.
If anyone wants to build a viewPager with thumbnails as indicators, using this library could be an option:
ThumbIndicator for viewPager that works also with image links as resources.
Here is how I did this, somewhat similar to the solutions above. Just make sure you call the loadDots() method after all images are downloaded.
private int dotsCount;
private TextView dotsTextView[];
private void setupAdapter() {
adapter = new SomeAdapter(getContext(), images);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(0);
viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
}
private final ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
for (int i = 0; i < dotsCount; i++)
dotsTextView[i].setTextColor(Color.GRAY);
dotsTextView[position].setTextColor(Color.WHITE);
}
@Override
public void onPageScrollStateChanged(int state) {}
};
protected void loadDots() {
dotsCount = adapter.getCount();
dotsTextView = new TextView[dotsCount];
for (int i = 0; i < dotsCount; i++) {
dotsTextView[i] = new TextView(getContext());
dotsTextView[i].setText(R.string.dot);
dotsTextView[i].setTextSize(45);
dotsTextView[i].setTypeface(null, Typeface.BOLD);
dotsTextView[i].setTextColor(android.graphics.Color.GRAY);
mDotsLayout.addView(dotsTextView[i]);
}
dotsTextView[0].setTextColor(Color.WHITE);
}
XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="180dp"
android:background="#00000000"/>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/introImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout
android:id="@+id/image_count"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00000000"
android:gravity="center|bottom"
android:orientation="horizontal"/>
</FrameLayout>