今天頭條的面試官問起了實現View滑動的方式,其實前幾天剛剛看過,但還是沒能答上來,這裏再來總結一遍。

1、使用scrollTo/scrollBy

為了實現View滑動,Android專門提供了這兩個方法讓我們使用。這兩個函數的區別是scrollBy提供的是基於當前位置的相對滑動,而scrollTo提供的是基於起始位置的絕對滑動。

需要注意的是實際的滑動方向與我們想當然的方向不同,這個問題與View的內部變量mScrollX和mScrollY的含義有關,scrollTo函數與scrollBy函數實際上就是對這兩個變量做出修改。

mScrollX:View的左邊緣座標減去View內容的左邊緣座標。

mScrollY:View的上邊緣座標減去View內容的上邊緣座標。

另外一個需要注意的地方是超出View邊緣範圍的內容是不會顯示的。

相對內部滾動的容器固定定位_相對內部滾動的容器固定定位

2、使用動畫

動畫的方式主要是操作View的translationX與translationY屬性。可以使用XML文件自定義動畫效果也可以使用屬性動畫實現。

2.1、自定義動畫
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <translate
        android:duration="100"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100"
        android:toYDelta="0"
        android:interpolator="@android:anim/linear_interpolator"/>
</set>
View view = findViewById(R.id.target_view);
Animation animation = AnimationUtils.loadAnimation(context, R.anim.target_animation);
view.startAnimation(animation);

注意在XML文件中有一個fillAfter屬性,如果設置為false的話當動畫結束時View會恢復到原來的位置。

2.2、屬性動畫
View view = findViewById(R.id.target_view);
ObjectAnimator.ofFloat(view, "translationX", 0, 100)
    .setDuration(100)
    .start();

3、改變佈局參數

第三種方法是改變View的LayoutParams,與之前的方法相比,這種方法顯得不是很正統,但是也可以實現我們的需求。舉個例子,假如我們想把一個View右移100dp,那麼最簡單的方式就是把它LayoutParams裏的marginLeft參數的值增加100dp。但這種方法要根據View所在的父佈局靈活調整,在一些情況下改變margin值並不能改變View的位置。

View view = findViewById(R.id.target_view);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view .getLayoutParams();
params.leftMargin += 10;
mTargetTextView.setLayoutParams(params);

還有一個相似的方法,但可用性要好一些,那就是調用View的layout方法,直接修改View相對於父佈局的位置。

int offsetX = 10;
View view = findViewById(R.id.target_view);
view.layout(mTargetTextView.getLeft() + offsetX,
            view.getTop(),
            view.getRight() + offsetX,
            view.getBottom());

4、使用Scroller實現漸進式滑動

上邊提到的滑動方式有一個共同的缺點那是他們都不是漸進式的滑動。實現漸進式滑動的共同思想就是將一個滑動行為分解為若干個,在一段時間內按順序完成。

這裏介紹使用Scroller類的實現方式。

public class ScrollerTextView extends TextView {

    private Scroller mScroller;

    public ScrollerTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }

    public void smoothScrollBy(int dx, int dy) {
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        mScroller.startScroll(scrollX, scrollY, dx, dy);
        invalidate();
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
}