読者です 読者をやめる 読者になる 読者になる

道端の鳩

突然の鳩のフン

ViewPagerメモ

私はViewPagerが嫌いだ。

正確に言うとPageの要素にListFragmentを置いてるViewPagerが嫌いだ。

リストをスクロールしたいのにちょっとした指の動きでそれがスワイプに変わるからだ。

しかしスワイプが便利であることは事実なので、妥協案として端っこからのスワイプのみ受け付けるViewPagerを作ろうと思い立った。

 

まず、ViewPagerのスワイプを単に無効化するのはググればすぐに出てくるが、ViewPagerをextendしたカスタムViewPagerを作り、オーバーライドしたonTouchEvent(MotionEvent event)とonInterceptTouchEvent(MotionEvent event)の返り値にfalseを与えればいい。

まったくスワイプしない前提なら、他にボタンなどを作って、onClick時にsetCurrentItem()などしてページを指定して送ればいいだろう。

では端からのスワイプをこれに受付させるにはどうしたらいいか?ということで自分はまず2通りの方法を考えた。

1. 端に細長いボタンなどを設置して、タッチイベントを受け取ってACTION_DOWN及びMOVEの時のみスワイプをEnableにする。ACTION_UPで再びDisable。

2. 画面のサイズを検知し、ViewPagerにおいてどの範囲がタッチされた際にスワイプを可能にするのか分ける。あとは1と同じ。

 

結論からいうと今のところどちらもまだうまくいってない。

自分はまず1を試した。が、試してすぐに分かったのは確かにボタンをタッチした際にスワイプをEnableにはできるがそのMotionEventはボタンのものであってViewPagerには及ばないため、結局ViewPagerはピクリともしない。なるほど、と思い2の試行へ。

 

2は表面的にはうまくいったように見えた。少なくとも空のFragmentを何枚か配置したViewPagerにおいては正常に機能した。だがListFragmentを配置してみた途端に問題が起きた。スワイプができない。

なぜか調べた結果、原因はViewPagerのonInterceptTouchEventをfalseにしていたことにあって、このメソッドはtrueにすることでタッチイベントが子要素に起きててもそれをViewPagerにも同様に適用できるというものだ。

つまり、これをfalseにしたままだといくらタッチしてもリスト要素を選択するのみで、ViewPagerに対してイベントが起きないということだ。

この回避策としては、このメソッドを常時trueにするか、あるいはリスト要素がタッチされた時にのみtrueにするという方法が考えられる。

後者はうまくいきそうだが、このカスタムViewPagerに対応させたカスタムListViewを使わなければいけなくなるという点において後々めんどくさそうである。

そう思って前者の常時trueの方向でコーディングしたら、これまたぱっと見うまくいったようだった。が、困ったことにスワイプ可能範囲域外をタッチしてスワイプしてもちょっとだけページが横に動くという事態が……。

どうやらonInterceptTouchEventがonTouchEventより先に呼ばれることが起因している模様。またViewPagerのスクロール状態としてIDLE, DRAGGING, SETTLINGの3つがあるのだが、少なくともonTouchEventより先に状態がDRAGGINGに変化している。

むう、それ以外はまともに動くのに……

 

そんなわけで、やっぱりListViewに仕掛けるか、あるいはViewPagerの前面に置いたウィジェットをタッチした際にViewPagerにFakeDragというのを仕掛けるのが考えられる解決策かなあ、と今のところは考えている次第。

出来たところで公開するかは不明。