Android ソースコード解析スクローラー

ご存知のとおり、スクローラーはスライドできますが、その使用プロセスは一般的なクラスとは異なります。より深く理解するには、Scroller のソース コードを調べる必要があります。スクローラーの使い方をまだ覚えていますか? 要約しましょう:

システムは、ビューを描画するときに、draw() メソッドの computeScroller メソッドを呼び出します。これを行うには、viewGroup の view.getParent のscrollTo メソッドを呼び出します。現在のスライディング値を取得します。しかし、現在のスライド指を取得するにはどうすればよいでしょうか? スクローラー変数を作成します。次に、scroller.startScroller メソッドを呼び出します。それを実現するために。コードを思い出してみましょう。

  private val mScroller = Scroller(context)

     fun smoothScrollTo(destX:Int, destY:Int){
        val scrollX = scrollX
        val delta  = destX - scrollX
        mScroller.startScroll(scrollX,0,delta,2,2000)
        invalidate()
    }

    override fun computeScroll() {
        super.computeScroll()
        if (mScroller.computeScrollOffset()) {
            (parent as View).scrollTo(mScroller.currX,mScroller.currY)
            invalidate()
        }
    }

これはカスタム ビューのメソッドであり、smoothScrollerTo は外部に提供されるメソッドです。

まず、スクローラー (コンテキスト) 構築メソッドを見てみましょう。

 public Scroller(Context context) {
        this(context, null);
    }

    /**
     * Create a Scroller with the specified interpolator. If the interpolator is
     * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
     * be in effect for apps targeting Honeycomb or newer.
     */
    public Scroller(Context context, Interpolator interpolator) {
        this(context, interpolator,
                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
    }

    /**
     * Create a Scroller with the specified interpolator. If the interpolator is
     * null, the default (viscous) interpolator will be used. Specify whether or
     * not to support progressive "flywheel" behavior in flinging.
     */
    public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
        mFinished = true;
        if (interpolator == null) {
            mInterpolator = new ViscousFluidInterpolator();
        } else {
            mInterpolator = interpolator;
        }
        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
        mFlywheel = flywheel;

        mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
    }

最後に、3 番目の構築メソッドと後の 2 つのパラメータを呼び出します。デフォルトで渡すパラメータの 1 つは null で、もう 1 つは false です。

次に、scroller.startScroll メソッドを見ていきます。コードは以下のように表示されます。


    /**
     * Start scrolling by providing a starting point, the distance to travel,
     * and the duration of the scroll.
     * 
     * @param startX Starting horizontal scroll offset in pixels. Positive
     *        numbers will scroll the content to the left.
     * @param startY Starting vertical scroll offset in pixels. Positive numbers
     *        will scroll the content up.
     * @param dx Horizontal distance to travel. Positive numbers will scroll the
     *        content to the left.
     * @param dy Vertical distance to travel. Positive numbers will scroll the
     *        content up.
     * @param duration Duration of the scroll in milliseconds.
     */
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartX = startX;
        mStartY = startY;
        mFinalX = startX + dx;
        mFinalY = startY + dy;
        mDeltaX = dx;
        mDeltaY = dy;
        mDurationReciprocal = 1.0f / (float) mDuration;
    }

ご覧のとおり、このメソッドにはスライドを開始するメソッドはありませんが、渡されたさまざまなパラメーターは保存されます。したがって、startScroller メソッドは事前準備にのみ使用されます。ビューのスライドは作成されません。外部に提供したsmoothScrollメソッドを振り返ると、重要なのは、startScrollを呼び出した後、invalidateメソッドを呼び出し、これによりビューが再描画されることです。ビューの再描画ではビューのdrawメソッドが呼び出され、drawメソッドはビューのcomputeScrollメソッドを呼び出します。スクローラーの現在位置のscrollxとscrollYを取得するにはどうすればよいですか? これは、scrollTo メソッドを呼び出すときに、スクローラーの computeScrollOffset メソッドを呼び出すことになるためです。

  public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }

        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                mCurrX = mStartX + Math.round(x * mDeltaX);
                mCurrY = mStartY + Math.round(x * mDeltaY);
                break;
            case FLING_MODE:
                final float t = (float) timePassed / mDuration;
                final int index = (int) (NB_SAMPLES * t);
                float distanceCoef = 1.f;
                float velocityCoef = 0.f;
                if (index < NB_SAMPLES) {
                    final float t_inf = (float) index / NB_SAMPLES;
                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
                    final float d_inf = SPLINE_POSITION[index];
                    final float d_sup = SPLINE_POSITION[index + 1];
                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
                }

                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
                
                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
                // Pin to mMinX <= mCurrX <= mMaxX
                mCurrX = Math.min(mCurrX, mMaxX);
                mCurrX = Math.max(mCurrX, mMinX);
                
                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
                // Pin to mMinY <= mCurrY <= mMaxY
                mCurrY = Math.min(mCurrY, mMaxY);
                mCurrY = Math.max(mCurrY, mMinY);

                if (mCurrX == mFinalX && mCurrY == mFinalY) {
                    mFinished = true;
                }

                break;
            }
        }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }

このメソッドを分析してみましょう。最初にアニメーション期間 timePassed を計算します。アニメーション期間が設定したスライド期間 mDuration より短い場合は、switch ステートメントを実行します。startScroll メソッドの mMode 値は SCROLL_MODE であるため、分岐ステートメント SCROLL_MODE が実行され、期間内に移動した距離が補間器に従って計算されます。mCurX と mCurrY に値を割り当てて、Scroller を通じて現在の srollX とscrollY を取得できるようにします。また、computeScrollOffsetの戻り値がtrueの場合は、スライディングが終了していないことを意味します。false の場合、スライドが終了したことを意味します。したがって、スライドが終了していない場合は、引き続きscrollToメソッドとinvalidateメソッドを呼び出して、Viewをスライドさせる必要があります。

それでは、スコーラーの原理を要約しましょう。

Scroller は View のスライドを直接実現することはできず、View の computeScroll メソッドと連携する必要があります。computeScroll メソッドでは、ビューが継続的に再描画され、再描画のたびにスライドの継続時間が計算され、この継続時間に応じて View のスライド位置を計算できます。各スワイプの位置に応じてスワイプするために、scrollTo メソッドを呼び出します。これにより、最後のプロセスを継続的に繰り返すことで、伸縮性のあるスワイプが実現されます。

おすすめ

転載: blog.csdn.net/howlaa/article/details/128475038