Здравствуйте, недавно мне понадобился ViewPager с индикатором. После продолжительных поисков была найдена очень функциональная библиотека — ViewPagerIndicator.
Автор библиотеки — довольно известный в кругах Android-разработчиков Jake Wharton. Также он занимается разработкой ActionBarSherlock, Picasso, Retrofit и многих других интересных библиотек. Вернемся к нашим баранам.
ViewPagerIndicator предлагает 6 разных типов индикаторов. Если вам не понравится стандартный стиль вы можете изменить его. Так как видов индикаторов много и нужно рассмотреть подробно каждый, то я разобью статью на несколько частей.
Предварительная настройка
Для того, чтобы начать пользоваться библиотекой нужно скачать ее с Github и подключить к проекту. Как это сделать подробно описано здесь.
1. CirclePageIndicator
Начнем, пожалуй, с самого простого типа индикаторов — кружков.
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    <com.viewpagerindicator.CirclePageIndicator
        android:id="@+id/indicator"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dip">
</LinearLayout>
CirclePageIndicator — это и есть наш индикатор. Ничего сложного нет. Инициализируем элементы в активити.
MainActivity.java:
package ru.javahelp.viewpagerindicator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import com.viewpagerindicator.CirclePageIndicator;
public class MainActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		chbDefaultCircleIndicatorSnap = (CheckBox) findViewById(R.id.chbDefaultCircleIndicatorSnap);
		chbDefaultCircleIndicatorSnap.setOnCheckedChangeListener(onCheckedChange);
		ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
		ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(this);
		viewPager.setAdapter(viewPagerAdapter);
		CirclePageIndicator circleIndicator = (CirclePageIndicator)findViewById(R.id.indicator);
		circleIndicator.setViewPager(viewPager);
	}
}
С помощью метода setViewPager(ViewPager) мы как бы «привязываем» индикатор к определенному ViewPager.
ViewPagerAdapter:
package ru.javahelp.viewpagerindicator;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class ViewPagerAdapter extends PagerAdapter {
	Context context;
	int count = 5;
	public ViewPagerAdapter(Context context) {
		this.context = context;
	}
	@Override
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		if(count<10){
			this.count = count;
		}
	}
	@Override
	public Object instantiateItem(ViewGroup container, int position) {
		TextView tv = new TextView(context);
		tv.setText("Позиция " + position);
		ViewPager viewPager = (ViewPager) container;
		viewPager.addView(tv, 0);
		return tv;
	}
	@Override
	public void destroyItem(ViewGroup container, int position, Object object) {
		((ViewPager) container).removeView((TextView) object);
	}
	@Override
	public boolean isViewFromObject(View arg0, Object arg1) {
		return arg0 == ((View) arg1);
	}
}
В переменной COUNT указываем количество страниц ViewPager’а. При каждом вызове метода getView создаем новый TextView и присваиваем ему текст с номер позиции.
Чтобы сделать переход индикатора резким (без плавного перелезания с одного кружка на другой) нужно вызвать метод setSnap(boolean) у индикатора и передать ему true.
circleIndicator.setSnap(true);
Добавляем элементы
Если в процессе выполнения программы во ViewPager добавляются или удаляются элементы, то необходимо вызвать метод notifyDataSetChanged() у индикатора, чтобы индикатор обновил свои данные.
circleIndicator.notifyDataSetChanged();
Стилизуем индикатор
Наверное не всем может понравиться стандартный стиль индикатора. Автор библиотеки предусмотрел это и добавил возможность кастомизации внешнего вида индикаторов. Изменить вид индикаторов можно как с помощью XML, так и программно.
Стилизуем с помощью XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    <com.viewpagerindicator.CirclePageIndicator
        android:id="@+id/indicator"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#FFCCCCCC"
        android:padding="10dip"
        app:fillColor="#FF888888"
        app:pageColor="#88FF0000"
        app:radius="10dp"
        app:strokeColor="#FF000000"
        app:strokeWidth="2dp">
</LinearLayout>
Разберем атрибуты CirclePageIndicator с префиксом app:
fillColor — цвет неактивных индикаторов
pageColor — цвет индикатора текущей страницы
radius — размер кружков
strokeColor — цвет обводки индикатора
strokeWidth — толщина обводки индикатора
Теперь сделаем то же самое, но программно:
float density = getResources().getDisplayMetrics().density; circleIndicator.setBackgroundColor(0xFFCCCCCC); circleIndicator.setRadius(10 * density); circleIndicator.setPageColor(0x880000FF); circleIndicator.setFillColor(0xFF888888); circleIndicator.setStrokeColor(0xFF000000); circleIndicator.setStrokeWidth(2 * density);
Я не буду писать, что делает каждый метод, а проведу аналогию с XML-атрибутами представленными выше:
setBackgroundColor(int) — fillColor.
setRadius(float) — radius.
setPageColor(int) — pageColor.
setFillColor(int) — fillColor.
setStrokeColor(int) — strokeColor.
setStrokeWidth(float) — strokeWidth.
2. LinePageIndicator
LinePageIndicator — индикатор в виде полосок.
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    <com.viewpagerindicator.LinePageIndicator
        android:id="@+id/indicator"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dip">
</LinearLayout>
MainActivity.java:
package ru.javahelp.viewpagerindicator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import com.viewpagerindicator.LinePageIndicator;
public class MainActivity extends Activity {
	ViewPagerAdapter viewPagerAdapter;
	ViewPager viewPager;
	LinePageIndicator linePageIndicator;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.lines_default);
		viewPagerAdapter = new ViewPagerAdapter(this);
		viewPager = (ViewPager)findViewById(R.id.pager);
		viewPager.setAdapter(viewPagerAdapter);
		linePageIndicator = (LinePageIndicator)findViewById(R.id.indicator);
		linePageIndicator.setViewPager(viewPager);
	}
}
Пока все точно та же как и у CirclePageIndicator, отличаются только имена классов.
Теперь, попробуем изменить внешний вид индикаторов. Делать мы это будем, опять же, через XML и программно.
Стилизуем с помощью XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    <com.viewpagerindicator.LinePageIndicator
        android:id="@+id/indicator"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dip"
        app:strokeWidth="4dp"
        app:lineWidth="30dp"
        app:unselectedColor="#FF888888"
        app:selectedColor="#FF880000">
</LinearLayout>
strokeWidth — толщина линии-индикатора
lineWidth — ширина линии-индикатора
unselectedColor — цвет неактивного индикатора
selectedColor — цвет индикатора текущей страницы
Стилизуем программно:
package ru.javahelp.viewpagerindicator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import com.viewpagerindicator.LinePageIndicator;
public class LinesThemedJava extends Activity {
	ViewPagerAdapter viewPagerAdapter;
	ViewPager viewPager;
	LinePageIndicator linePageIndicator;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.lines_themed_java);
		viewPagerAdapter = new ViewPagerAdapter(this);
		viewPager = (ViewPager) findViewById(R.id.pager);
		viewPager.setAdapter(viewPagerAdapter);
		linePageIndicator = (LinePageIndicator) findViewById(R.id.indicator);
		linePageIndicator.setViewPager(viewPager);
		final float density = getResources().getDisplayMetrics().density;
		linePageIndicator.setSelectedColor(0x88FF0000);
		linePageIndicator.setUnselectedColor(0xFF888888);
		linePageIndicator.setStrokeWidth(4 * density);
		linePageIndicator.setLineWidth(30 * density);
	}
}
Точно та же приведу аналоги методов в XML:
setSelectedColor(int) — selectedColor
setUnselectedColor(int) — unselectedColor
setStrokeWidth(float) — strokeWidth
setLineWidth(float) — lineWidth
3. UnderlinePageIndicator
Underline PageIndicator — индикатор в виде полоски, нахадящейся в самом низу экрана.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    <com.viewpagerindicator.UnderlinePageIndicator
        android:id="@+id/indicator"
        android:layout_width="fill_parent"
        android:layout_height="2dp">
</LinearLayout>
MainActivity.java:
package ru.javahelp.viewpagerindicator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import com.viewpagerindicator.UnderlinePageIndicator;
public class UnderlineDefault extends Activity {
	ViewPagerAdapter viewPagerAdapter;
	ViewPager viewPager;
	UnderlinePageIndicator underlinePageIndicator;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.underline_default);
			viewPagerAdapter = new ViewPagerAdapter(this);
			viewPager = (ViewPager)findViewById(R.id.pager);
			viewPager.setAdapter(viewPagerAdapter);
			underlinePageIndicator = (UnderlinePageIndicator)findViewById(R.id.indicator);
			underlinePageIndicator.setViewPager(viewPager);
	}
}
Стилизуем с помощью XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    <com.viewpagerindicator.UnderlinePageIndicator
        android:id="@+id/indicator"
        android:layout_height="2dp"
        android:layout_width="fill_parent"
        android:background="#FFCCCCCC"
        app:selectedColor="#FFCC0000"
        app:fadeDelay="1000"
        app:fadeLength="1000">
</LinearLayout>
selectedColor — цвет активного индикатора
fadeDelay — без понятия
fadeLength — без понятия
Стилизуем программно:
package ru.javahelp.viewpagerindicator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import com.viewpagerindicator.UnderlinePageIndicator;
public class UnderlineThemedJava extends Activity{
	ViewPagerAdapter viewPagerAdapter;
	ViewPager viewPager;
	UnderlinePageIndicator underlinePageIndicator;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.underline_themed_java);
			viewPagerAdapter = new ViewPagerAdapter(this);
			viewPager = (ViewPager)findViewById(R.id.underline_themed_java_pager);
			viewPager.setAdapter(viewPagerAdapter);
			underlinePageIndicator = (UnderlinePageIndicator)findViewById(R.id.underline_themed_java_indicator);
			underlinePageIndicator.setViewPager(viewPager);
			underlinePageIndicator.setSelectedColor(0xFFCC0000);
			underlinePageIndicator.setBackgroundColor(0xFFCCCCCC);
			underlinePageIndicator.setFadeDelay(1000);
			underlinePageIndicator.setFadeLength(1000);
	}
}
setSelectedColor(int) — selectedColor
setFadeDelay(int) — fadeDelay
setFadeLength(int) — fadeLength
Библиотека на GitHub: ViewPagerIndicator.
Исходники примера: ViewPagerIndicator.
