1. 程式人生 > >記錄Android中實現滑動的幾種方法

記錄Android中實現滑動的幾種方法

一、layou方法

程式碼示例如下,自定義一個view,在onTouchEvent()方法中計算手指滑動時的偏移量,呼叫view的layout()方法,在當前left、top、right、bottom上加上偏移量,實現view的滑動。

public class TestView extends View {
    private static final String TAG = "TestView";
    public TestView(Context context) {
        super(context);
    }

    public TestView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

    }

    public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    float startX;
    float startY;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //記錄初始觸控點座標
                startX = event.getRawX();
                startY = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                //手指滑動過程中經過的點座標
                float lastX = event.getRawX();
                float lastY = event.getRawY();
                //計算偏移量
                int offsetX = (int) (lastX - startX);
                int offsetY = (int) (lastY - startY);
                //在當前的left、top、right、bottom上加上偏移量
                layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
                Log.e(TAG, "onTouchEvent: layout: " + offsetX + "/" + offsetY);
                //重新設定初始座標 ,這一步很重要            
                startX = lastX;
                startY = lastY;
            break;
          }
        return true;
    }
 }

二、offsetLeftAndRight()與offsetTopAndBottom()方法

這兩個方法相當於系統提供的一個對左右、上下移動的API的封裝。使用方法同樣先在onTouchEvent()方法中計算出手指滑動的偏移量,然後將偏移量作為方法引數傳入。關鍵程式碼如下:

    float startX;
    float startY;
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //記錄初始觸控點座標
                startX = event.getRawX();
                startY = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                //手指滑動過程中經過的點座標
                float lastX = event.getRawX();
                float lastY = event.getRawY();
                //計算偏移量
                int offsetX = (int) (lastX - startX);
                int offsetY = (int) (lastY - startY);
                //呼叫方法,引數為計算出的偏移量
                offsetLeftAndRight(offsetX);
                offsetTopAndBottom(offsetY);
                Log.e(TAG, "onTouchEvent: offsetLeftAndRight_offsetTopAndBottom: " + offsetX + "/" + offsetY);
                //重新設定初始座標 ,這一步很重要   
                startX = lastX;
                startY = lastY;
                break;
              }
        return true;
    }

三、修改view的LayoutParams

LayoutParams儲存著一個View的佈局引數。可以通過改變LayoutParams來動態修改一個View的位置引數,以此達到改變View位置的效果。關鍵程式碼如下:

    float startX;
    float startY;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //記錄初始觸控點座標
                startX = event.getRawX();
                startY = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                //手指滑動過程中經過的點座標
                float lastX = event.getRawX();
                float lastY = event.getRawY();
                //計算偏移量
                int offsetX = (int) (lastX - startX);
                int offsetY = (int) (lastY - startY);
                //修改LayoutParams引數,這裡要改變位置主要是修改LayoutParams的margin屬性,因此這裡直接使用
                // ViewGroup.MarginLayoutParams以求方便,不必考慮view的父佈局型別。在一般應用中,如若有需要
                // 修改LayoutParams的其他屬性,要注意getLayoutParams()必須獲取的是父佈局型別的LayoutParams。
                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
                layoutParams.leftMargin = getLeft() + offsetX;
                layoutParams.topMargin = getTop() + offsetY;
                setLayoutParams(layoutParams);
                Log.e(TAG, "onTouchEvent: layoutParams: " + offsetX + "/" + offsetY);
                //重新設定初始座標,這一步很重要
                startX = lastX;
                startY = lastY;
                break;
               }
        return true;
    }

四、scrollTo與scrollBy

1.scrollTo(int x,int y):移動到一個座標點
2.scrollBy(int x,int y):按照x軸增量x,y軸增量y的方式移動
這兩個方法實質上移動的是呼叫方法的物件所包含的內容。即:如果對一個ViewGroup(如LinearLayout)呼叫這兩個方法,會發現其內部所有的子view會一起發生移動;而如果對一個view(如TextView)呼叫,則會發現看不到移動的效果。要對view使用這兩個方法,實際上是需要對該view 的parent使用。程式碼示例如下:
1.在ViewGroup中,可以直接呼叫

float startX;
float startY;
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startX = event.getRawX();
            startY = event.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            float lastX = event.getRawX();
            float lastY = event.getRawY();
            int offsetX = (int) (lastX - startX);
            int offsetY = (int) (lastY - startY);
            //注意這裡的引數要變成相反數。由於作用物件是parent,所以實際發生
            //移動的應該是parent,但我們的目標是子view。因此,以相對檢視關係
            //來看,要讓子view產生所期望的移動效果,parent應該要向反方向移動。
            scrollBy(-offsetX, -offsetY);
            startX = lastX;
            startY = lastY;
            break;
    }
    return true;
}

2.在view中,需要由view的parent來呼叫

 ((View) getParent()).scrollBy(-offsetX, -offsetY);

五、使用Scroller實現帶過度動畫的滑動

使用前面幾種方法實現view位置的改變,如果不是通過手指觸控事件,而是通過如按鈕點選事件呼叫,view的位置變化是瞬間完成的。如果要讓位置變化有一個過渡動畫的過程,可以使用Scroller來實現。Scroller的使用可以分為3步驟:
1.初始化Scroller: Scroller scroller=new Scroller(context);
2.重寫computeScroll()方法,實現模擬滑動

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

3.startScroll()方法開啟模擬過程。可以在自定義view內部合適的位置呼叫下列方法,也可以在自定義view內部宣告一個呼叫該方法的方法,然後在外部Activity等需要呼叫的位置呼叫。

scroller.startScroll(startX, startY, dx, dy, duration);

程式碼示例:

自定義View
public class TestView extends View {
private static final String TAG = "TestView";
private Scroller mScroller;

public TestView(Context context) {
    super(context);
}

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

public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

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

public void scrollAnimation(int startX, int startY, int dx, int dy, int duration) {
    mScroller.startScroll(startX, startY, dx, dy, duration);
    invalidate();
}
}

在Activity中呼叫:

 @Override
public void onClick(View v) {
    mTestView.scrollAnimation(0,0,-300,-400,2000);
}