ViewPager2とBottomNavigationViewのPage位置を同期する
ViewPager2とBottomNavigationViewを組み合わせて使う際、必要なのが位置の同期。
具体的には下記の2つの流れで、位置を同期する必要があります。
①BottomNavigationView→ViewPager2
Positionを取得する。
ViewPagerの位置を設定する。
@Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { int id = menuItem.getItemId(); switch (menuItemId) { case R.id.bnav_home: position = 0; break; ... } viewPager.setCurrentItem(position, true); }
動作ログ
D/Logger: BottomNavigationView onNavigationItemSelected position=2 D/Logger: ViewPager onPageScrollStateChanged state=Settling D/Logger: ViewPager onPageSelected position=2 D/Logger: ViewPager onPageScrollStateChanged state=Idle
②ViewPager2→BottomNavigationView
positionを取得する。
BottomNavigationViewの位置を設定する。
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() @Override public void onPageSelected(int position) { bottomNavigationView.getMenu().getItem(position).setChecked(true); } ... }
動作ログ
D/Logger: ViewPager onPageScrollStateChanged state=Dragging D/Logger: ViewPager onPageScrollStateChanged state=Settling D/Logger: ViewPager onPageSelected position=2 D/Logger: ViewPager onPageScrollStateChanged state=Idle
onPageSelectedの契機が①と②どちらかを区別する
それぞれの動作ログを見ると分かるのですが、どちらの場合でもonPageSelectedが呼ばれます。
どちらでも呼ばれるからと言って、動作がおかしくなるとかでは全くないのですが、区別したいケースがあったりすると思います。
ここでは呼ばれる契機がどちらかを区別するため、ViewPager2.OnPageChangeCallbackをextendsして改造してみます。
ちなみにViewPager2の実装は下記にあるのですが、その中でOnPageChangeCallbackがabstract classで定義されてます。これをいい感じにします。
上の2つの動作ログのViewPagerに関するもので違いが出てくるのが、state=Draggingの行です。
よって、state=Draggingのタイミングがあったかどうかを保持していけばよさそうです。
実装した結果は下記の通りです。
import androidx.annotation.Px; import androidx.viewpager2.widget.ViewPager2; public abstract class OnPageChangeCallback extends ViewPager2.OnPageChangeCallback { private boolean isDragging = false; public abstract void onPageSelected(int position, boolean isDragged); @Override public void onPageSelected(int position) { onPageSelected(position, isDragging); } @Override public void onPageScrollStateChanged(@ViewPager2.ScrollState int state) { switch (state) { case ViewPager2.SCROLL_STATE_DRAGGING: isDragging = true; break; case ViewPager2.SCROLL_STATE_IDLE: isDragging = false; break; case ViewPager2.SCROLL_STATE_SETTLING: default: // NOP break; } } }
使う側ではこんな感じです。
viewPager.registerOnPageChangeCallback(new OnPageChangeCallback() { @Override public void onPageSelected(int position, boolean isDragged) { if (isDragged) { // ViewPagerのスクロール契機だったら、BottomNavigationViewに伝える bottomNavigationView.getMenu().getItem(position).setChecked(true); } } ... });
こうすれば、どっち契機のonPageSelectedかが判別でき、どっちかだけアニメーションしたい、とかいうのも実現できそうです。