1. 程式人生 > >View 的滑動

View 的滑動

ica 補間動畫 替代 tca != -a use end sta

View 的滑動

學習自

《Android開發藝術探索》

滑動漫談

因為Android手機屏幕大小的原因,所以為了顯式更多的信息,我們必須采用滾動的方式來處理,因為滾動就涉及到了滑動,有的滑動十分生硬,而有的滑動卻是圓潤並且絢麗的,View的滑動就是我們本章要學習的內容。

使用scrollTo/scrollBy

註意,這種方式只是改變了View內容的位置,並沒有改變View的位置,以Button為例,那就是僅僅改變了Button中的文本的位置,而沒有改變Button的位置。

scrollTo 實現了指定位置的絕對滑動

/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * 設置View的滾動的位置,這一操作將會回到onScrollChange事件 * 這一操作將會使View失效 * @param x the x position to scroll to * x 滑動到的x坐標 * @param y the y position to scroll to * y 滑動到的y坐標 */ public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int
oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } }

scrollBy 實現了基於當前位置的相對滑動

/**
* Move the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the amount of pixels to scroll by horizontally * x 橫向滾動的像素的總和 * @param y the amount of pixels to scroll by vertically * y 縱向滾動的像素的總和 */ public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }

代碼分析,通過查看scrollBy的代碼,我麽發現scrollBy的實現也是基於scrollTo的,所以我們需要分析scrollTo的代碼,在scrollTo的代碼中,有 mScrollXmScrollY 這兩個值需要我們多註意一下,其中,mScrollX 值代表的是view的內容和View左邊緣的距離,mScrollY帶邊的view的內容與view上邊緣的值。

通過動畫

通過補間動畫來完成移動

這裏省略的補間動畫實現的介紹,因為補間動畫並不會真正的改變View的位置,而只是改變的View的影像。這裏就不說,因為已經有了屬相動畫來替代補間動畫。

通過屬性動畫倆完成移動

scrollBtn.setOnClickListener {
    ObjectAnimator
            .ofFloat(scrollBtn, "translationX", 0.0F, 100.0F)
            .setDuration(1000L)
            .start()
}

改變布局參數

通過改變ViewLayoutParams的值,我們也可以實現View的移動。方式如下

scrollBtn.setOnClickListener {
    var params = scrollBtn.layoutParams as ViewGroup.MarginLayoutParams
    params.leftMargin = 100
    scrollBtn.requestLayout()
}

總結

上面的三種方法都可以實現View的滑動。我們來總結一下

  • scrollTo/scrollBy,僅僅改變的是View內容的位置,而不嫩改變View自身的位置
  • 使用動畫,如果是Android3.0及以上的版本使用動畫實現滑動是一個非常好的方式,如果在3.0已下就需要處理View的移動的問題了,推薦使用在沒有交互的View上,如果使用的3.0以上則另當別論。
  • 是真正的改變了View的位置,推薦使用在有交互性的View上。

平滑的滑動

通過上面的集中方法我們已經可以實現View的滑動,但是上面的方法除了通過動畫的方式外,實現的效果都比較生硬,所以現在我們來學習一下,如何平滑地實現滑動。

Scroller

Scroller類並能幫助我們進行滑動操作,它僅僅是為我們完成滑動過程中的計算,實際的滑動的功能還需要我們自己實現,ViewPager中就中就采用了Scroller來實現,下面我們以一個Dome(將一個TextView向右滑動)的形式來學習Scroller。

技術分享圖片



class MyTextView(context: Context, attrs: AttributeSet) : TextView(context, attrs) {
    var mScroller = Scroller(context)

    fun smoothScroll(destX: Int, destY: Int) {
        var deltaX = destX - x
        L.e(deltaX.toString())
        this.mScroller.startScroll(x.toInt(), 0, deltaX.toInt(), 0, 1000)
        invalidate()
    }

    override fun computeScroll() {
        if (mScroller.computeScrollOffset()) {
            this.x = mScroller.currX.toFloat()
            postInvalidate()
        }
    }
}
//調用
scrollBtn.setOnClickListener {
    helloTV.smoothScroll(this.helloTV.x.toInt() + 100, 0)
}

Scroller原理淺析

首先我們調用的是Scroller的 startScroll 方法,下面是此方法的源碼,其中沒有任何和滑動相關的代碼,僅僅是將我麽傳遞的參數存儲了起來。參數解釋:

  • startX 開始X軸的坐標
  • startY 開始Y的坐標
  • dx X軸前進的距離
  • dy Y軸前進的距離

    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;
    }

startScroll 方法中我們發現,滾動並沒有在這個方法中,那滑動的效果是怎麽實現的,答案是通過利用View的重繪機制實現的。請註意我們調用了 invalidate() 方法,此方法會導致View重繪,View重繪時會調用View的 draw 的方法,此方法會調用我們 computeScroll 方法,因為我們已經重寫此方法,所以會調用我們重寫的方法,在我們重寫的 computeScroll方法中,我們移動了View後,調用 postInvalidate 方法再次引發重繪,如此循環直到結束。

上面我們提到了computeScroll,接下來看看此方法:

override fun computeScroll() {
    //判斷是否需要繼續滑動
    //true 表示滑動還未結束,需要繼續滑動
    //false 表示滑動已經完成
    if (mScroller.computeScrollOffset()) {
        //改變View的位置
        this.x = mScroller.currX.toFloat()
        //引發重繪
        postInvalidate()
    }
}

如果你查看了 computeScrollOffset 的源碼的話,就會發現其中是計算偏移的長度的,並且返回值表示的是,是否需要繼續滑動。到此我們已經對Scroller的工作方式有一個大概的了解了。

利用屬性動畫

scrollBtn.setOnClickListener {
    var animator = ValueAnimator.ofInt(0, 1).setDuration(1000)
    animator.addUpdateListener {
        var faction = it.animatedFraction
        scrollBtn.scrollTo(startX + (dalteX * faction).toInt(), 0)
    }
    animator.start()
}

使用延時策略

通過Handler或者View的Handler或者View的 postDelayed 方法或者利用線程睡眠的方式,我們也可以實現滑動的效果。

var end = 200
var distance = 0
var offset = 10

private fun scroll() {
    Handler().postDelayed({
        var temp = offset
        distance += offset
        if (distance > end) {
            temp = distance - end
        }
        if (distance < end) {
            scrollBtn.scrollBy(temp, 0)
            scroll()
        }
    }, 100)
}

View 的滑動