第三章 view的事件體系 ----- view滑動/拖動

Android view的滑動

先看下view完整代碼如下:

public class DemoView extends View {

    private int lastX;
    private int lastY;
    private Scroller mScroller;
    public DemoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    public DemoView(Context context) {
        super(context);
        mScroller = new Scroller(context);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
                //獲取到手指處的橫座標和縱座標
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                //計算移動的距離
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                //調用layout方法來重新放置它的位置
//                layout(getLeft() + offsetX, getTop() + offsetY,
//                        getRight() + offsetX, getBottom() + offsetY);
                //通過translation來實現
//                int translationX = (int) getTranslationX() + offsetX;
//                int translationY = (int) getTranslationY() + offsetY;
//                setTranslationX(translationX);
//                setTranslationY(translationY);
                //通過layout param 實現 需要注意:如果view在佈局文件中設置了layout_above、center等類似屬性,則會出現問題
//                RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
//                layoutParams.leftMargin = getLeft() + offsetX;
//                layoutParams.topMargin = getTop() + offsetY;
//                setLayoutParams(layoutParams);
                //通過MarginLayoutParams
//                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
//               layoutParams.leftMargin = getLeft() + offsetX;    //這樣也可以
//               layoutParams.topMargin = getTop() + offsetY;
//                layoutParams.leftMargin += offsetX;
//                layoutParams.topMargin += offsetY;
//                setLayoutParams(layoutParams);
                /*通過scrollBy操作,getParent是因爲scrollBy是對view的內容進行移動,而不是對view本身移動
                所以,當前view的移動通過獲取父佈局,對父佈局進行scrollBy操作,當前view就是父佈局的內容
                note:scrollBy會對view的所有內容進行移動,如果父佈局中還有其他的view(button啊 TextView啊)
                則在拖動該view時其他view也會一起移動*/
                ((View) getParent()).scrollBy(-offsetX,- offsetY);
                break;
        }
        return true;
    }
    public void smoothScrollTo(int destX,int destY){
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int deltaX = destX - scrollX;
        int deltaY = destY - scrollY;
        //1000秒內滑向destX destY
        Log.d("xj", "smoothScrollTo: ");
        mScroller.startScroll(scrollX,scrollY,deltaX,deltaY,1000);
        postInvalidate();// invalidate();
    }
}

一、通過layout方法實現

  • case MotionEvent.ACTION_MOVE: 下的代碼如下:
    //調用layout方法來重新放置它的位置
    layout(getLeft() + offsetX, getTop() + offsetY,
          getRight() + offsetX, getBottom() + offsetY);

二、offsetLeftAndRight()offsetTopAndBottom()

  • case MotionEvent.ACTION_MOVE: 下的代碼如下:
    //調用offset方法來重新放置它的位置,
    offsetLeftAndRight(offX);
    offsetTopAndBottom(offY);

三、通過translation(動畫的方式)來實現

  • case MotionEvent.ACTION_MOVE: 下的代碼如下:
    //通過translation來實現
    int translationX = (int) getTranslationX() + offsetX;
    int translationY = (int) getTranslationY() + offsetY;
    setTranslationX(translationX);
    setTranslationY(translationY);
  • 不隨手指移動,僅實現view的滑動,使用屬性動畫(若要兼容3.0以下版本,需要使用開源動畫庫nineoldandroids)動畫開源庫
    ObjectAnimator.ofFloat(demoview,"translationX",0,300).setDuration(1000).start();
    -使用view動畫滑動,R.anim.translate爲動畫資源文件:
 demoview.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));

四、通過LayoutParams來實現

如果view在佈局文件中設置了layout_above、center(因文中使用了相對佈局)等類似屬性,則會出現問題,不會正常拖動

  • case MotionEvent.ACTION_MOVE: 下的代碼如下:
/*通過LayoutParams來實現,這裏父佈局使用的是RelativeLayout,因此使用RelativeLayout.LayoutParams,若父佈局是LinearLayout,則使用RelativeLayout.LayoutParams*/
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams); 

還可以使用通過MarginLayoutParams實現,代碼替換爲:

ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
//下面兩種寫法:使用一種即可
//方法一(註釋中)
//layoutParams.leftMargin = getLeft() + offsetX;
//layoutParams.topMargin = getTop() + offsetY; 
//方法二
layoutParams.leftMargin += offsetX;
layoutParams.topMargin += offsetY;
setLayoutParams(layoutParams);

五、通過scollTo與scollBy(動畫的方式)來實現

sceollTo(x,y) 絕對滑動,傳入的是移動的終點座標
scrollBy(dx,dy) 相對滑動,傳入的是移動的增量,
通過scrollBy傳入的值應該是你需要的那個增量的相反數!
通過scrollBy操作,getParent是因爲scrollBy是對view的內容進行移動,而不是對view本身移動,所以,當前view的移動通過獲取父佈局,對父佈局進行scrollBy操作,當前view就是父佈局的內容


note:scrollBy會對view的所有內容進行移動,如果父佈局中還有其他的view(button啊 TextView啊,則在拖動該view時其他view也會一起移動

  • case MotionEvent.ACTION_MOVE: 下的代碼如下:
    //通過scrollBy來實現
    ((View) getParent()).scrollBy(-offsetX,- offsetY); 

六、通過Scroller(彈性滑動)來實現

這個是滑動效果,不是隨着手指拖動效果
- 初始化Scroller

public DemoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }
  • 重寫computeScroll()方法,
@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()){
            ((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
           // postInvalidate();//這個也可以,效果上沒啥區別
            invalidate();
        }
    }
  • 調用Scroller.startScroll()方法。寫一個smoothScrollTo()方法來調用Scroller.startScroll()方法,實現滑動
public void smoothScrollTo(int destX,int destY){
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int deltaX = destX - scrollX;
        int deltaY = destY - scrollY;
        //2000秒內滑動到指定點
        mScroller.startScroll(scrollX,scrollY,deltaX,deltaY,2000
       //postInvalidate();//這個也可以,效果上沒啥區別
        invalidate();
    }
  • 在activity中調用該方法,注意:該方法移動的是demoView所在父佈局中的所用內容
demoView.smoothScrollTo(-500,-300);
  • computeScrollOffset方法判斷是否完成了整個滑動,返回true,則沒有完成,否則完成滑動。
  • 必須要用invalidate方法刷新,因爲computeScroll方法不會自動調用,它是在view的draw方法中被調用的,所以必須使用invalidate刷新。
  • 在startScroll中,偏移量跟使用scrollBy方法中的偏移量用法是一樣的,即也必須填寫你實際想要移動距離的相反數。也就是你實際想讓它偏移一個正值,這裏就填寫它相應的負值,如果想偏移一個負值,這裏就填寫相應的正值!
  • 通過Scroller實現彈性滑動的代碼基本是固定的,實現原理:
    startScroll方法下面的invalidate方法會讓view進行重繪,view進行重繪時draw方法又會去調用computeScroll方法,該方法在view中是一個空實現,需要我們自己實現。computeScroll方法會向Scroller獲取當前的scrollX和scrollY,然後通過scrollTo方法滑動;接着又調用了invalidate方法重繪,重複之前的動作,直到整個滑動結束。

總結

  • 其中一二三四五都是view隨手指可以移動
  • scrollTo 和scrollBy 操作簡單,適合對view內容滑動
  • 動畫:適用於沒有交互的view和實現複雜的動畫效果
  • 改變佈局參數:操作稍微複雜,適用於有交互的view
  • 參考文章,書籍,Android開發藝術探索
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章