ListViewは縦スクロールしかできません。
layoutWidthをwrap_contentや固定長dpとして横に長いListViewを作っても、画面から見切れるだけで横スクロールはできません。
そこで、先人はHorizontalScrollViewをコンテナに使うことで、横スクロールを実現しました。
しかし、この方法では横スクロールと縦スクロールしかできません。
斜めに動かすことができません。
不便なので斜めスクロールを実現する方法を編み出しました。
方法検討
- × ListViewを継承⇒そんなめんどくさいの無理
- △ 斜めスクロール可能なレイアウトコンテナを作る⇒ListView自体の縦スクロールはリソース削減のメリットがあるため、もったいない
- ○ 縦スクロールと横スクロールを同時に動かせば、それは斜めスクロールに違いない!⇒うん、やってみるよ!
HorizontalScrollView方式で、なぜ、斜めスクロールができないか?
- HorizontalScrollViewは横スクロールを検知すると、子へタッチイベントを伝搬しない
- ListView(GridView)は縦スクロールを検知すると、親へタッチイベントを伝搬しない
- ListView(GridView)は縦スクロールを検知すると、親へIntercept拒否リクエストを行う
タッチイベントの伝搬を管理しているのはdispatchTouchEvent()というメソッドなので、これをオーバーライドすれば解決できそうです。
しかし、こいつの中身が意味不明・・・
手っ取り早く解決するために、イベントの伝搬を阻害している処理を全部回避していきます。
レイアウト構造
- NanameScrollView extends HorizontalScrollView
┗ListView(GridView)
レイアウトとAdapterの実装は省略します。
横スクロール可能なListViewサンプルの、HorizontalScrollViewを次のカスタムビューで置き換えるだけです。
NanameScrollView extends HorizontalScrollView
import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.HorizontalScrollView; public class NanameScrollView4List extends HorizontalScrollView { public NanameScrollView4List(Context context) { super(context); } public NanameScrollView4List(Context context, AttributeSet attrs) { super(context, attrs); } /** Interceptして欲しくない時に子から呼び出される<br/> * HorizontalScrollViewのonInterceptTouchEventはこの値に影響されるため、断固無視する。 */ @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { //なんもしない } /** ScrollViewにおけるタッチイベントのコア<br/> * HorizontalScrollViewでは横スクロールを検知すると子へイベントを伝搬しなくなるため、<br/> * return falseによって強制的に子へ伝搬する。 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { super.onInterceptTouchEvent(ev); //スクロールとかしてもらう、戻り値は無視。 onTouchEvent(ev); //子(ListView)がtouchイベント伝搬を止めるため自身のonTouchEventを強制的に呼び出す。 return false; //常に子へ伝搬 } }
たったこれだけです。
たったこれだけで、斜めにスクロールできます。
ただし、力技で無理やり動かしているので、状況によって正常に動作しない可能性が高いです。
この駄文が何かのお役に立てば幸いです。