ページコントロールとは
iOSでよくあるドット型のページネーションのことです。
Androidでは標準で用意されていないので、作るかライブラリを使う必要があります。
ここではiOSにならってページコントロールと呼んでいますが、AndroidではIndicatorという名前のものが多く、有名なライブラリにViewPageIndicatorや、CircleIndicatorなどがあります。
が、これらのライブラリはViewPagerでしか使えなかったため、自前でカスタムビューを作ってみました。
RecyclerViewをViewPagerのように使う
SupportLibrary25.1.0以上で使えるPagerSnapHelperを使うことで、RecyclerViewでViewPagerのような挙動を実現できます。
ViewPager in ViewPagerを避けたいときなどに便利です。
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
ページコントロールを作る
ドットを作る
デフォルトと選択時の2種類を用意します。
<?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 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()
を使って取得しています。
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 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>
PageControlView pageControlView = (PageControlView) findViewById(R.id.page_control_view);
pageControlView.setRecyclerView(recyclerView, layoutManager);
おまけ
setRecyclerViewメソッドを以下のように書き変えるとViewPagerでも使えます。
リスナーがポジションを持っているので、こちらの方が楽。
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) {
}
});
}