1. 程式人生 > >第三章 view的事件體系 ----- view滑動/拖動

第三章 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開發藝術探索