AndroidのNestedScrrolling機構はあなたを連れてかん合スライドを遊びます。


一、概要
Androidはsupport.v 4のカバンの中でみんなに二つの非常に不思議な種類を提供しました。
  • NestedScrrongPart
  • NestedScrrongChild
  • この二つの種類は聞いたことがないなら大丈夫です。ゆっくり紹介してください。この二つは何に使うか分かります。みんなが見たり使ったりしたことがあると思いますが、これを通じてとても便利に助けてくれます。例えば以下のような華やかな効果があります。

    このような効果はNestedScrrolling機構を使って完成するのに非常に適しています。また、Coordinaso Layoutの背後には実はこのメカニズムが利用されています。So、このメカニズムが何に使えるかが分かりました。
    しかし、まだ問題があると信じています。
  • このメカニズムは従来のカスタムView Groupイベントに比べて何か優越したところがありますか?
  • はい、簡単に分析してみます。
    上の図によると、
    私達が伝統的なイベントによって配布して理解すると仮定して、まず私達がスライドするのは下のコンテンツエリアで、移動は外部のView Groupが移動しています。だから伝統的な方式によって、外部のPartentが内部のChildのイベントをブロックしたに違いないです。しかし、上記の効果図は、Partentがある程度スライドすると、Childがまたスライドし始めました。中間プロセス全体は途切れることがありません。正常なイベントの配布(配布イベントを手動で呼び出さない、手動でイベントを出さない)という観点からすることは不可能です。Partブロック後は、イベントをChildに渡すことはできません。
    しかし、NestedScrrollingメカニズムはこのことを処理するのにとても便利です。だから、このメカニズムを深く勉強します。一つはネストスライドを作成する時に特殊な効果があります。二番目は、私がCoordinasolayoutを分析するための敷物です。
    ps:具体的にどのv 4バージョンに追加されましたか?深く追求しないでください。もしあなたのv 4に上記の二つの種類がないなら、あなたのv 4バージョンをアップグレードします。NestedScrrollingメカニズムという言葉は、個人的な呼び方です。公式の呼び方がよく分かりません。深く追究しないでください。
    二、期待効果
    もちろんこの二つの種類を説明するには、必ずケースのサポートが必要です。さもなくばあまりにも空洞です。幸い、ここではとてもいい例があります。
    昔、こんな文章を書きました。
    Androidは、ユーザー定義のコントロールにより、360のソフトウェア詳細ページの効果を実現します。
    完全に伝統的な方法で書かれています。そして連続的にスライドするために、DOWNイベントの種類を手動で配布するなど、非常に特殊な処理をしています。
    効果図はこうです。

    今日はこの効果を利用して、NestedSrollのメカニズムの実例として、最後にまた簡単にソースコードを分析します。ソースはやはり比較的簡単です。
    ps:Coordinano Layoutはこの効果を実現しやすく、後続の文章もCoordinate Layoutに対していくつかの分析を行います。
    三、実現する
    上記の効果図は、3つの部分に分けられます。トップレイアウトです。中間のビューPagerインジケータ;及び底のRecyclerView;
    RecyclerViewは、NestedSrollingChildの実装クラスですので、本例の主要な役割はNestedScrrongPartentを実現することです。
    (1)レイアウトファイル
    まずレイアウトファイルをプレビューします。頭の中に大体のレイアウトがあります。
    
    <com.zhy.view.StickyNavLayout xmlns:tools="http://schemas.android.com/tools"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >
     <RelativeLayout
     android:id="@id/id_stickynavlayout_topview"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="#4400ff00" >
     <TextView
      android:layout_width="match_parent"
      android:layout_height="256dp"
      android:gravity="center"
      android:text="    "
      android:textSize="30sp"
      android:textStyle="bold" />
     </RelativeLayout>
    
     <com.zhy.view.SimpleViewPagerIndicator
     android:id="@id/id_stickynavlayout_indicator"
     android:layout_width="match_parent"
     android:layout_height="50dp"
     android:background="#ffffffff" >
     </com.zhy.view.SimpleViewPagerIndicator>
    
     <android.support.v4.view.ViewPager
     android:id="@id/id_stickynavlayout_viewpager"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
      >
     </android.support.v4.view.ViewPager>
    
    </com.zhy.view.StickyNavLayout>
    StickyNavLayoutは直接Lineear Layoutから継承され、orentation=「vertical」に設定されているので、直観的にはコントロールが縦方向に並べられています。測定にはいくつかの特殊な処理が必要です。本文の重点ではないので、自分でソースを確認したり、上で述べた文章を見ることができます。
    (2)Nested ScrrollingPartの実現
    NestedScrrollingPartはインターフェースであり、これを実現するには次のような方法が必要である。
    
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
    
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
    
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
      int dxUnconsumed, int dyUnconsumed);
    
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
    
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
    
    public boolean onNestedPreFling(View target, float velocityX, float velocityY);
    
    public int getNestedScrollAxes();
    具体的な実現を書く前に、必要な上記の方法を簡単に紹介します。
  • onStartNestedScrrollこの方法は、必ず自分の需要に応じてtrueに戻さなければならない。この方法は、現在のコントロールがその内部View(直接サブViewではない)を受信できるかどうかを決定する。縦スライドのみに関わると、ここではnetedScreaxesというパラメータに基づいて縦方向の判断ができます。
  • onNestedPrescrollこの方法は内部Viewに移動するdx,dyに入ってきます。もしあなたが一定のdx,dyを消費する必要があるなら、最後のパラメータconsumedによって指定します。例えば半分のdyを消費したいなら、consumed[1]=dy/2
  • を書くことができます。
  • onNestedFling内部Viewのflingイベントをキャプチャすることができますが、return trueは内部Viewのイベントをブロックするという意味です。
  • 主に注目しているのはこの3つの方法です。
    ここの内部Viewは必ずしも直接的なものではないという意味です。内部Viewであれば大丈夫です。
    私達の具体的な実現を見てみます。
    
    public class StickyNavLayout extends LinearLayout implements NestedScrollingParent
    {
     @Override
     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)
     {
     return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
     }
     @Override
     public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)
     {
     boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
     boolean showTop = dy < 0 && getScrollY() > 0 && !ViewCompat.canScrollVertically(target, -1);
    
     if (hiddenTop || showTop)
     {
      scrollBy(0, dy);
      consumed[1] = dy;
     }
     }
     @Override
     public boolean onNestedPreFling(View target, float velocityX, float velocityY)
     {
     if (getScrollY() >= mTopViewHeight) return false;
     fling((int) velocityY);
     return true;
     }
    }
  • オンスStartNestedScrrollにおいて、縦にtrueに戻るなら、これは一般的に内部のViewが入ってくる必要があると判断しました。もし確実でないなら、または内部のViewが編纂されていないかを心配して、直接return trueができます。
  • onNestedPrescrollにおいて、上滑りかつ上部コントロールが完全に隠蔽されていないとdyが消費されると判断しました。すなわちconsumed[1]=dyです。スライドして内部Viewがダウンし続けられなくなったら、dyは消費されます。すなわちconsumed[1]=dyは自分でscrollByを実行するという意味です。実は私達のStick NavLayoutスライドです。
  • さらに、ここでflingも処理しました。NestedPreFling方法によって、これは自分の需要に応じて決められます。トップコントロールが表示されるときに、flingはトップコントロールを隠したり、表示させたりできます。
  • 以上のコードで次の効果が得られます。

    fling方法については、OverScrrollのflingを利用して、境界検出に対して、scrollToを書き換える方法である。
    
    public void fling(int velocityY)
    {
     mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
     invalidate();
    }
    
    @Override
    public void scrollTo(int x, int y)
    {
     if (y < 0)
     {
      y = 0;
     }
     if (y > mTopViewHeight)
     {
      y = mTopViewHeight;
     }
     if (y != getScrollY())
     {
      super.scrollTo(x, y);
     }
    }
    詳しい説明は上記の文章を見てもいいです。ここでは繰り返しません。
    ここに来てください。NestedScrrollingの仕組みが見えます。説明はとても簡単です。
    NestedScrrollingParts内部のViewです。スライドする時、まずdx、dyをNestedc ScrrollingPartentに伝えます。Nestedc ScrrollingPartsはそれを消費するかどうかを決定できます。一般的には必要に応じて部分または全部を消費します。しかし、ここでは実際の制約がないので、どれぐらいの消費ができますか?内部Viewに影響があります。
    白話で原本のイベント配布機と比較するとこうなります。
  • イベントの配布は、子Viewはまずイベントの処理権を得て、処理中、父Viewはそれをブロックすることができますが、ブロックしたら子供Viewに返すことができません。
  • NestedScrrollingメカニズムは、内部Viewがスクロールしている間、まずdx、dyをNestedc ScrrollingPartentに渡し、Nestedc ScrrongPartはそれを部分的に消費し、残りの部分は内部Viewに返すことができる。
  • 具体的なソースはこのブログより複雑になります。内部のViewエリアではない一部のインタラクションに触れますので、このブログのポイントではなく、ソースコードを参照することができます。
    四、原理
    原理とは、内部ViewがいつNestedScrrollingPartにフィードバックされるかを見ることです。直接内部ViewのonTouchEventに位置します。
    
    @Override
    public boolean onTouchEvent(MotionEvent e) {
     switch (action) {
      case MotionEvent.ACTION_DOWN: {
       int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
       if (canScrollHorizontally) {
        nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
       }
       if (canScrollVertically) {
        nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
       }
       startNestedScroll(nestedScrollAxis);
      } break;
      case MotionEvent.ACTION_MOVE: {
       if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
        dx -= mScrollConsumed[0];
        dy -= mScrollConsumed[1];
        vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
       }
      } break;
      case MotionEvent.ACTION_UP: {
       fling((int) xvel, (int) yvel);
       resetTouch();
      } break;
    
      case MotionEvent.ACTION_CANCEL: {
       cancelTouch();
      } break;
     }
     return true;
    }
    見られます
    ACTION_DOWNはstartNestedScrrollを呼び出しました。ACTION_MOVEではdispatch NestedPrescrollを呼び出しました。ACTION_UPは、reetTouchを起動するためにflingをトリガすることがあります。
    startNestedScrroll内部の実際:
    
    #NestedScrollingChildHelper
    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;
    }
    NestedScrrollingPartsを探しに行きます。その後、オンストップとone NestedScrrollAccteptedを返します。
    dispatch Nestediscrollでは、OnestedPrescroll方法をフィードバックします。内部のscrollByInternalでは、onNestedScrroll方法をリコンバージョンします。
    flingでは、onNestedPreFlingとonNestedFling方法をフィードバックします。
    resttouchではワンストップNestedScrrollに戻ります。
    コードには貼られていません。直接にontouchEventを見つけたらすぐに見られます。呼出の方法名はすべてdispatch Nesteddexメソッドです。実際の内部はすべてNestedc ScrrongChild Helperによって実現されます。
    だから、Nestedc ScrrollingPartとの連携が必要な内部Viewを実現して、Nestedc ScrrollingChildを実現して、内部はNestedc Scrrolling Chilperというサブクラスによって、コアの方法は全部包装しました。適切な実際にパラメータコール方法に入るだけでいいです。
    ok,このような一つの機構はぜひ試してみてください。多くのスライド関連の効果はこのように実現できます。
    ソースの住所:
    githubアドレス:https://github.com/hongyangAndroid/Android-StickyNavLayout
    ローカルダウンロード:http://xiazai.jb51.net/201705/yuanma/Android-StickyNavLayout(jb 51.net)rar
    締め括りをつける
    以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に一定の助けをもたらすことを望んでいます。もし疑問があれば、メッセージを残して交流してください。ありがとうございます。