ViewPagerで右スクロールを禁止して、左スクロールだけにする
概要
ViewPager を使用して複数の Fragment を横スクロールで切り替える時、右スクロールは禁止して、左スクロール(戻る)のみを有効にする方法です。
確認環境
参考情報
onTouchEvent メソッドと onInterceptTouchEvent メソッドをオーバーライドすることで実現している。
At the end the solution was in the adapter. I changed the count of the PagerAdapter and this way blocks the user from passing the max page:
PagerAdapter の getCount メソッドで返す値を変えることにより実現している。
対処方法
両方とも試してみた。
- onTouchEvent, onInterceptTouchEvent メソッドをオーバーライドする方法
スワイプでは、右に全くスクロールできない。(左スクロールはスワイプ、右スクロールはボタンで行っている。)
- PagerAdapter の getCount メソッドで返す値を変える方法
スワイプで右にスクロールしようとすると、スクロールできるかに見えるが跳ね返る。(左スクロールはスワイプ、右スクロールはボタンで行っている。)
補足
onTouchEvent, onInterceptTouchEvent の違いがわからないので、ソースコードを読んでみた。 要約すると次のようになる。
- onInterceptTouchEvent が true を返したら自身の dispatchTouchEvent を行い、false を返したら子 View の dispatchTouchEvent を行う。
- ACTION_MOVE の時は、ACTION_DOWN の時にイベントを handle した子 View の dispatchTouchEvent を行う。
- この際、onInterceptTouchEvent が true ならば、ACTION_CANCEL として子 View の dispatchTouchEvent を行う。
以下、要点のみ疑似コードで記載。
ViewGroup::dispatchTouchEvent は下記のように振る舞いっていると思われる。
ACTION_DOWN の時: intercepted = onInterceptTouchEvent intercepted が false の時: 全ての childView に対して: ...(A) handled = childView.dispatchTouchEvent handled が true の時: TouchTarget の先頭に childView を追加して、(A) 終了 TouchTarget がない時: handled = View::dispatchTouchEvent handled を返して、終了 ACTION_MOVE の時: TouchTarget がない時: handled = View::dispatchTouchEvent TouchTarget がある時: intercepted = onInterceptTouchEvent intercepted が true の時: event.action を ACTION_CANCEL として、全ての TouchTarget(childView) に対して: handled = childView.dispatchTouchEvent childView を TouchTarget から取り除く intercepted が false の時: 全ての TouchTarget(childView) に対して: handled = childView.dispatchTouchEvent いずれかの handled が true ならば true を返して、終了 ACTION_HOVER_MOVE の時: TouchTarget がある時: intercepted = onInterceptTouchEvent TouchTarget がない時: intercepted = false intercepted が false の時: 全ての childView に対して: ...(A) TouchView に childView が含まれているなら、(A) 終了 handled = childView.dispatchTouchEvent handled が true の時: TouchTarget の先頭に childView を追加して、(A) 終了 TouchTarget がない時: handled = View::dispatchTouchEvent TouchTarget がある時: intercepted が true の時: event.action を ACTION_CANCEL として、全ての TouchTarget(childView) に対して: handled = childView.dispatchTouchEvent childView を TouchTarget から取り除く intercepted が false の時: 全ての TouchTarget(childView) に対して: handled = childView.dispatchTouchEvent いずれかの handled が true ならば true を返して、終了
View::dispatchTouchEvent は下記のように振る舞いっていると思われる。
OnTouchListener が登録されている時: handled = OnTouchListener::onTouch handled が false で、TouchDelegate が登録されている時: handled = TouchDelegate::onTouchEvent handled が false の時: handled = イベントを処理する handled を返して、終了