RecyclerViewで使えるページコントロールを作る

# ページコントロールとは
iOSでよくあるドット型のページネーションのことです。
Androidでは標準で用意されていないので、作るかライブラリを使う必要があります。
![page_control.gif](https://qiita-image-store.s3.amazonaws.com/0/33071/0e65ef3f-86b2-a415-2598-6efb8bfd7b60.gif)
ここではiOSにならってページコントロールと呼んでいますが、AndroidではIndicatorという名前のものが多く、有名なライブラリに<a href"https://github.com/JakeWharton/ViewPagerIndicator">ViewPageIndicator</a>や、<a href+"https://github.com/ongakuer/CircleIndicator">CircleIndicator</a>などがあります。
が、これらのライブラリはViewPagerでしか使えなかったため、自前でカスタムビューを作ってみました。

# RecyclerViewをViewPagerのように使う
SupportLibrary25.1.0以上で使える<a href="https://developer.android.com/reference/android/support/v7/widget/PagerSnapHelper.html">PagerSnapHelper</a>を使うことで、RecyclerViewでViewPagerのような挙動を実現できます。
ViewPager in ViewPagerを避けたいときなどに便利です。

```java:MainActivity.java
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);

PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
```
バージョンの関係などでPagerSnapHelperを使えない場合は以下の記事でスナップの作り方が紹介されています。
http://qiita.com/hotpepsi/items/1167e5263f048a005e05

# ページコントロールを作る
## ドットを作る
デフォルトと選択時の２種類を用意します。

```xml:res/drawable/shape_page_control_default.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#FFFFFF" />
</shape>
```
```xml:res/drawable/shape_page_control_selected.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#B7B7B7" />
</shape>
```
## カスタムビューを作る
以下の2点を行うカスタムビューを作ります。

- RecyclerViewが表示する要素の数だけドットを作る。
- スナップするとドットも連動して切り替わる。
 
スナップに連動してドットを切り替える際、現在スナップ表示されている要素のポジションが必要になるため、LinerLayoutManagerの```findLastVisibleItemPosition()```を使って取得しています。

```java:PageControlView.java
public class PageControlView extends LinearLayout {
    private ImageView[] imageViews;

    public PageControlView(Context context) {
        this(context, null);
    }

    public PageControlView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PageControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setRecyclerView(RecyclerView recyclerView, final LinearLayoutManager layoutManager) {
        int itemCount = layoutManager.getItemCount();
        if (itemCount < 1) {
            return;
        }
        createPageControl(itemCount);

        // RecyclerViewのスクロールに合わせてドットを切り替え
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                int position = layoutManager.findLastVisibleItemPosition();
                int itemCount = layoutManager.getItemCount();

                for (int i = 0; i < itemCount; i++) {
                    imageViews[i].setImageResource(R.drawable.shape_page_control_default);
                }
                imageViews[position].setImageResource(R.drawable.shape_page_control_selected);
            }
        });
    }

　　// RecyclerViewのitem数だけドットを作成
    private void createPageControl (int itemCount) {
        imageViews = new ImageView[itemCount];

        for (int i = 0; i < imageViews.length; i++) {
            imageViews[i] = new ImageView(getContext());
            imageViews[i].setImageResource(R.drawable.shape_page_control_default);

　　　　　　 // ドットのサイズと間隔を調整(上部のgif画像は10dp)
            int dotSize = getResources().getDimensionPixelSize(R.dimen.page_control_dot_size);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dotSize, dotSize);
            params.setMargins(dotSize / 2, 0, dotSize / 2, 0);
            imageViews[i].setLayoutParams(params);

            addView(imageViews[i]);
        }
        imageViews[0].setImageResource(R.drawable.shape_page_control_selected);
    }
}
```

## カスタムビューを使う
通常のビューと同様に配置します。

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
    tools:context="com.hogehoge.fugafuga.MainActivity">
    
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <com.hogehoge.fugafuga.PageControlView
        android:id="@+id/page_control_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:layout_gravity="center"/>
</LinearLayout>
```
```java:MainActivity.java
PageControlView pageControlView = (PageControlView) findViewById(R.id.page_control_view);
pageControlView.setRecyclerView(recyclerView, layoutManager);
```

# おまけ
setRecyclerViewメソッドを以下のように書き変えるとViewPagerでも使えます。
リスナーがポジションを持っているので、こちらの方が楽。


```java:PageControlView.java
    public void setViewPager (final ViewPager viewPager) {
        int itemCount = viewPager.getAdapter().getCount();

        if (itemCount < 1) {
            return;
        }
        createPageControl(itemCount);

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

            }

            @Override
            public void onPageSelected(int position) {
                int itemCount = viewPager.getAdapter().getCount();

                for (int i = 0; i < itemCount; i++) {
                    imageViews[i].setImageResource(R.drawable.shape_page_control_default);
                }
                imageViews[position].setImageResource(R.drawable.shape_page_control_selected);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }
```