Androidネストスライド機構(NestedScrolling)Androidネストスライド機構(NestedScrolling)Androidネストスライド機構(NestedScrolling)

12566 ワード

Androidネストスライド機構(NestedScrolling)NestedScrollingの特性はどこに現れますか?例えば、Toolbarを使用して、次のScrollView、上にスクロールしてToolbarを隠し、下にスクロールしてToolbarを表示します.ここでは論理的にNestedScrollingです.Toolbarを含むViewをスクロールする過程で、 が中のScrollViewをスクロールしたからです.
効果は上図のようです【私を嫌ってはいけません】
これまで、AndroidTouchイベントの配布には独自のメカニズムがあることを知っていました.主に3つの関数があります.dispatchTouchEventonInterceptTouchEventonTouchEventです.
この配布メカニズムには、次のような脆弱性があります.
子viewがtouchイベントを処理する機会を得た場合、親viewは次の指が押されるまでこのtouchイベントを処理する機会がなくなります.
すなわち,我々がサブViewをスライドさせる場合,サブViewがこのスライドイベントを処理したくない場合,親viewに渡さずにこのtouchイベントを捨てるしかない.
しかし、Googleの新しいNestedScrollingメカニズムはこの問題をよく解決した.このNestedScrollingをどのように実現するかを見てみましょう.まず、いくつかのクラス(インタフェース)に注目する必要があります.
NestedScrollingChild NestedScrollingParent NestedScrollingChildHelper NestedScrollingParentHelper
以上の4つのクラスはsupport-v4パッケージで提供されており、LollipopのViewデフォルトではいくつかの方法が実装されています.インタフェースの実装は簡単ですが、ここではNestedScrollingChildシリーズの方法を一時的に使用しました(Parentはsupport-designが提供するCoordinatorLayoutなので)
    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        super.setNestedScrollingEnabled(enabled);
        mChildHelper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return mChildHelper.isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return mChildHelper.startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        mChildHelper.stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return mChildHelper.hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }

はい、簡単ならそうすればいいです.
これらのインタフェースは、私たちが必要とするときに自分で呼び出したものです.childHelperは何をしましたか?startNestedScrollの方法を見てみましょう
    /** * Start a new nested scroll for this view. * * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass * method/{@link NestedScrollingChild} interface method with the same signature to implement * the standard policy.</p> * * @param axes Supported nested scroll axes. * See {@link NestedScrollingChild#startNestedScroll(int)}. * @return true if a cooperating parent view was found and nested scrolling started successfully */
    public boolean startNestedScroll(int axes) {
        if (hasNestedScrollingParent()) {
            // Already in progress
            return true;
        }
        if (isNestedScrollingEnabled()) {
            ViewParent p = mView.getParent();
            View child = mView;
            while (p != null) {
                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
                    mNestedScrollingParent = p;
                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
                    return true;
                }
                if (p instanceof View) {
                    child = (View) p;
                }
                p = p.getParent();
            }
        }
        return false;
    }

ここではNestedScrollingParentとのインタラクションを実現するためのいくつかの方法を見ることができます.ViewParentCompatは、親viewとインタラクティブな互換クラスであり、api versionを判断し、Lollipop以上であればviewが持参した方法を用い、そうでなければNestedScrollingParentインタフェースが実現されたかどうかを判断し、インタフェースを呼び出す方法である.
では、具体的にはどのようにこのメカニズムを使用しますか?例えば、子Viewの場合、親viewにネストされたtouchイベントを共有する必要があることを通知する必要があります.では、scrollインタラクションのみを含むワークフロー全体について説明します.
一、startNestedScroll
まずサブビューはプロセス全体を開く必要があります(内部は主にnestedScrollを受け入れるのに適したparentを見つけます)、親Viewに通知します.私はあなたと協力してTouchEventを処理します.
二、dispatchNestedPreScroll
子ViewのonInterceptTouchEventまたはonTouch(一般的にMontionEvent.ACTION_MOVEイベント内)では、このメソッドを呼び出して親Viewがスライドする距離を通知する.この方法の3番目の4番目のパラメータは、親viewが消費したscroll長さとサブViewのフォームオフセット量を返します.このscrollが消費されていない場合、サブviewは残りの距離を処理し、フォームが移動したため、指の最後の位置を記録した場合、4番目のパラメータoffsetInWindowに基づいてオフセット量を計算する必要があり、次のtouchイベントの計算が正しいことを保証することができます.親viewがスクロールパラメータを受け入れて一部消費した場合、この関数はtrueを返します.そうでない場合falseです.この関数は一般にサブviewでscrollを処理する前に呼び出されます.
三、dispatchNestedScroll
親viewにスクロール状況を報告し、サブviewが消費している部分と、サブviewが消費していない部分を含む.親viewがスクロールパラメータを受け入れて一部消費した場合、この関数はtrueを返します.そうでない場合falseです.この関数は一般的にサブviewでscrollを処理した後に呼び出されます.
四、stopNestedScroll
プロセス全体を終了します.
対応プロセス全体がこうです
サブビュー
親view
startNestedScroll
onStartNestedScroll、onNestedScrollAccepted
dispatchNestedPreScroll
onNestedPreScroll
dispatchNestedScroll
onNestedScroll
stopNestedScroll
onStopNestedScroll
一般に、サブviewが呼び出しを開始し、親viewがコールバックを受信します.
最も注目すべきはdispatchNestedPreScrollconsumedパラメータである.
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) ;

これはint型の配列で、長さは2で、最初の要素は親viewが消費するx方向の転がり距離です.2番目の要素は、親viewが消費するy方向のスクロール距離であり、この2つの値が0でない場合、サブviewはスクロール量をいくつか修正する必要がある.このパラメータがあるからこそ、スクロールイベントを処理するとき、考え方がより明確になり、以前のようにスクロールパラメータの山に混同されることはありません.
NestedScrollの紹介はここまでですが、次回はCoordinatorLayoutの使用(わかりにくいBehaviorオブジェクト)とSegmentFault Android での実践についてお話しします.応援ありがとうございます.