365連休

にわかのandroidとかの開発メモ。

Android Studio 3.4でListViewを斜めスクロールする方法(今更感) Diagonal Scroll!!

ListViewは縦スクロールしかできません。

layoutWidthをwrap_contentや固定長dpとして横に長いListViewを作っても、画面から見切れるだけで横スクロールはできません。

 

そこで、先人はHorizontalScrollViewをコンテナに使うことで、横スクロールを実現しました。

詳細はググってください。

 

しかし、この方法では横スクロールと縦スクロールしかできません。

斜めに動かすことができません。

 

不便なので斜めスクロールを実現する方法を編み出しました。

 

方法検討

  • × ListViewを継承⇒そんなめんどくさいの無理
  • △ 斜めスクロール可能なレイアウトコンテナを作る⇒ListView自体の縦スクロールはリソース削減のメリットがあるため、もったいない
  • ○ 縦スクロールと横スクロールを同時に動かせば、それは斜めスクロールに違いない!⇒うん、やってみるよ!

 

HorizontalScrollView方式で、なぜ、斜めスクロールができないか?

  • HorizontalScrollViewは横スクロールを検知すると、子へタッチイベントを伝搬しない
  • ListView(GridView)は縦スクロールを検知すると、親へタッチイベントを伝搬しない
  • ListView(GridView)は縦スクロールを検知すると、親へIntercept拒否リクエストを行う

タッチイベントの伝搬を管理しているのはdispatchTouchEvent()というメソッドなので、これをオーバーライドすれば解決できそうです。

blog.lciel.jp

しかし、こいつの中身が意味不明・・・

手っ取り早く解決するために、イベントの伝搬を阻害している処理を全部回避していきます。

 

レイアウト構造

  • 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; //常に子へ伝搬
    }

}

 

たったこれだけです。

 

たったこれだけで、斜めにスクロールできます。

 

ただし、力技で無理やり動かしているので、状況によって正常に動作しない可能性が高いです。

 

 

この駄文が何かのお役に立てば幸いです。