qnote Advent Calendar 18日目
今回は自分でViewPagerを使ってカルーセルを実装したことについて書いてみようかと思います。
#まずライブラリに頼りたい
自分で実装する前にいい感じのライブラリがあればそれで解決だよね〜
と探してみましたが良いものが無く、自分で実装することに
それっぽいものは結構あったのですが、最終的に目的に一致するものがありませんでした。。。
#ではどうしよう?
Androidでカルーセルっぽい動きをしているのはViewPagerやViewFlipperがありますが、ViewPagerを使うことにしました。
何故かと言うとViewPagerの方が使い慣れていたらから。
#実装
さて自分で実装するにあたりヒントが欲しくていろいろ調べていたら、やはりもう実装されている方がいて公開していただけていたので、それを参考にしました。
参考URL
https://github.com/alphamu/LoopingViewPagerDemo
https://groups.google.com/forum/#!topic/android-group-japan/uqpL94Q-MUQ
上のはIndicatorの参考に
下のは実際にviewをループさせるように見せる部分の参考にしています。
無限にループするのではなく、頑張ればいつか終点に着くようになっています。
自動で切り替えるのは、簡単に自分で実装しました。
タイマーを付けて時間が来たら次のページへ移動するというだけ
全部をここに載せるのは見くくなると思ったのでgithubにあげました。
良ければ御覧ください
https://github.com/Hatijan/AndroidCarousel
#解説
キーになるのはこの部分
スタート地点を全体のカウントの真ん中に持ってきていること
実際ただそれだけのViewPagerです
public void initialize(Context context, int pages, LoopedViewPagerListener listener) {
if (context == null || listener == null) {
throw new RuntimeException();
}
mPages = pages;
if (pages == 0) {
return;
}
if (pages == 1) {
mAdapterPages = 1;
} else {
mAdapterPages = ALL_PAGE_COUNT;
}
mListener = listener;
setAdapter(new MyPagerAdapter());
addOnPageChangeListener(new MyOnPageChangeListener());
int maxSets = ALL_PAGE_COUNT / mPages;
mFirstPos = (maxSets / 2) * mPages;
setCurrentItem(mFirstPos);
}
使い方はこんな感じ
public class MainActivity extends AppCompatActivity {
private List<CarouselContentView> frameLayouts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LayoutInflater inflater = LayoutInflater.from(this);
frameLayouts = new ArrayList<>();
CarouselContentView contentView = (CarouselContentView) inflater.inflate(R.layout.carousel_content, null);
contentView.setTitle("一枚目");
contentView.setBackgroundColor(Color.RED);
frameLayouts.add(contentView);
CarouselContentView contentView2 = (CarouselContentView) inflater.inflate(R.layout.carousel_content, null);
contentView2.setTitle("二枚目");
contentView2.setBackgroundColor(Color.BLUE);
frameLayouts.add(contentView2);
CarouselContentView contentView3 = (CarouselContentView) inflater.inflate(R.layout.carousel_content, null);
contentView3.setTitle("三枚目");
contentView3.setBackgroundColor(Color.GREEN);
frameLayouts.add(contentView3);
CarouselContentView contentView4 = (CarouselContentView) inflater.inflate(R.layout.carousel_content, null);
contentView4.setTitle("四枚目");
contentView4.setBackgroundColor(Color.YELLOW);
frameLayouts.add(contentView4);
LoopedViewPager carouselView = (LoopedViewPager) findViewById(R.id.pager);
carouselView.initialize(this, frameLayouts.size(), new LoopedViewPager.LoopedViewPagerListener() {
@Override
public View OnInstantiateItem(int page) {
return frameLayouts.get(page);
}
@Override
public void onPageScrollChanged(int page) {
}
});
LoopPageIndicator pageIndicator = new LoopPageIndicator(this, (LinearLayout) findViewById(R.id.pages_container), carouselView, R.drawable.indicator_circle);
pageIndicator.setPageCount(frameLayouts.size());
pageIndicator.show();
carouselView.startMoveTimer();
}
@Override
protected void onStart() {
super.onStart();
((LoopedViewPager) findViewById(R.id.pager)).startMoveTimer();
}
@Override
protected void onPause() {
super.onPause();
((LoopedViewPager) findViewById(R.id.pager)).stopMoveTimer();
}
}
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="exsample.yamada.mycarousel.MainActivity">
<FrameLayout
android:id="@+id/carousel_view"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerHorizontal="true">
<exsample.yamada.mycarousel.LoopedViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="300dp"/>
<LinearLayout
android:id="@+id/pages_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="8dp"
android:gravity="center_horizontal"
android:orientation="horizontal"/>
</FrameLayout>
</RelativeLayout>
レイアウトで直接LoopedViewPagerを置いています。
内部に表示するコンテンツを生成して、LoopedViewPagerの初期化そしてタイマーを起動すればカルーセルの完成
内部のコンテンツにタップアクションを付けたい場合はそのまま、CarouselContentViewにOnClickListenerをセットすればOK
LayoutInflater inflater = LayoutInflater.from(this);
frameLayouts = new ArrayList<>();
CarouselContentView contentView = (CarouselContentView) inflater.inflate(R.layout.carousel_content, null);
contentView.setTitle("一枚目");
contentView.setBackgroundColor(Color.RED);
contentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
frameLayouts.add(contentView);
#まとめ
実際にはCollapsingToolbarLayoutに入れてスクロールすると縮んでいくというUIにしたのですが、なぜかALL_PAGE_COUNTにInteger.MAX_VALUEを入れるとページが切り替わらない程度にフリックするとスタート地点に戻ろうとする動き(右にいくつかページを移して、軽くフリックして離すと一枚左に動く)が発生して、ものすごくハマりました。
ALL_PAGE_COUNTを8000000としたら正しく動くようになって、何だそりゃとなりました。
8000000でも十分大きいので問題ないですが、なんで発生するかは分からずじまい。