第三章:View的事件體系

   3.1 View的基礎知識android

      主要有:View的位置參數,MotionEvent和TouchSlop對象,VelocityTracker,GestureDetector和Scroller對象ide

    3.1.1 View佈局

      view是android中全部控件的基類,是一種界面層的控件的一種抽象.post

      ViewGroup(控件組),內部包含了不少個控件,即一組view動畫

    3.1.2 View的位置參數this

      View的位置主要由它的四個頂點來決定:top(左上角縱座標),left(左上角橫座標),right(右下角橫座標),bottom(右下角縱座標)spa

      left = getLeft();code

      right = getRight();對象

      top = getTop();blog

      bottom = getBottom();

      從android3.0開始,增長了:x,y,translationX和translationY,x和y是View的左上角的左邊,而translationX和translationY是View左上角相對於父容器的偏移量. 

      x = left + translationX;

      y = right + translationY;

    3.1.3 MotionEvent和TouchSlop

      1. MotionEvent

        手指接觸屏幕後產生一些列的事件,典型的事件以下

        • ACTION_DOWN:手指剛接觸屏幕
        • ACTION_MOVE:手指在屏幕上移動
        • ACTION_UP:手指從屏幕上鬆開的一瞬間

      2. TouchSlop

        是系統所能識別出被認爲是滑動的最小距離.若是兩次滑動之間的距離小過這個常量,系統就不認爲你是在進行滑動操做

        經過ViewConfiguration.get(getContext()).getScaledTouchSlop()得到這個常量.

    3.1.4 VelocityTracker,GestureDetector和Scroller

      1.VelocityTracker

        速度追蹤:用來追蹤手指在滑動過程當中的速度

        首先在View的onTouchEvent方法中追蹤當前點擊事件的速度

        VelocityTracker velocityTracker = VelocityTracker.obtain();

        velocityTracker.addMovement(event);

        注意兩點:第一,獲取速度以前必須先計算速度,即在getXVelocity()和getYVelocity()方法以前要先調用computeCurrentVelocity方法,第二點,這裏的速度是指一段時間內手指所劃過的像素數

        velocityTracker.computeCurrentVelocity(1000);//時間間隔爲1000ms;

        int xVelocity = (int) velocityTracker.getXVelocity();

        int yVelocity = (int) velocityTracker.getYVelocity();

      2. GestureDetector

        手勢檢測

        首先須要建立一個GestureDetector對象並實現OnGestureListener接口

        GestureDetector mGestureDetector = new GestureDetector(this);

        //解決長按屏幕後沒法拖動的現象

        mGestureDetector.setIsLongpressEnabled(false);

        接着,接管目標View的onTouchEvent方法,在待監聽View的onTouchEvent方法中實現

        boolean consume = mGestureDetector.onTouchEvent(event);

        retrun consume;

      3. Scroller

        彈性滑動對象,用於實現View的彈性滑動,使用scrollTo/scrollBy方法來進行滑動時,其過程是瞬間完成的.

 

    3.2 View的滑動

      3.2.1 使用scrollTo/scrollBy

       scrollBy實際上也是調用了scrollTo方法,scrollBy它實現基於當前位置的相對滑動,scrollTo是基於所傳遞參數的絕對滑動

        View內部的兩個屬性mScrollX(等於View左邊緣和View內容左邊緣在水平方向的距離)和mScrollY(等於View的上邊緣和View內容上邊緣在豎直方向的距離)的改變規則,經過getScrollX和getScrollY得到

        從左往右滑動,mScrollX爲負值,反之爲正值;若是從上往下滑動,那麼mScrollY爲負值,反之爲正值.

      3.2.2 使用動畫

        使用View動畫,可是會形成View的位置沒有改變,動畫完成後,View仍是會停在原來的位置,因此要用屬性動畫

       3.2.3 改變佈局參數

        佈局參數:LayoutParams

        

MarginLayoutParams params = (MarginLayoutParams) mButton1.getLayoutParams();
params.width += 100;
params.leftHeight += 100;
mButton1.requestLayout();
//或者mButton1.setLayoutParams(params);

 

        

    3.3 彈性滑動

      3.3.1 使用scroller

       

Scroller Scroller = new Scroller(mContext);
//緩慢滾動到指定位置
private void smoothScrollTo(int destX, int destY){
    int scrollX = getScrollX();
    int deltaX = destX - scrollX;
    //1000ms內滑向的destX,效果就是慢慢滑動
    mScroller.startScroller(scrollX, 0, deltaX, 0, 1000);
    invalidate();
}

@Override
public void computeScroller(){
    if (mScroller.computeScrollOfset()) {
        mscrollTo(mScroller.getScrollX, mScroller.getCurrY());
        postInvalidate();
    }
}

 

      

      Scroller自己並不能實現view的滑動,他須要配合View的computeScroll方法才能完成彈性滑動的效果它不斷讓view重繪,每一次重繪距滑動起始時間會有一個時間間隔,經過這個時間間隔就能夠得出View當前的滑動位置,知道了滑動位置就能夠經過scrollTo方法完成View的滑動.

   3.3.2 經過動畫

     動畫ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(1000).start();

   3.3.3 使用延時策略

     使用Handler發送消息sendMessage(),在handleMessage()方法中進行view的繪製    

     

   3.4 View的事件分發機制

     3.4.1 點擊事件的傳遞規則

        有三個方法:

        public boolean dispatchTouchEvent(MotionEvent ev):用來進行事件的分發.若是事件可以傳遞給當前View,那麼次方法必定會被調用,返回結果受當前View的onTouchEvent和下級View的dispatchTouchEvent方法的影響,表示是否消耗當前事件.

        public boolean onInterceptTouchEvent(MotionEvent event):用來判斷是否攔截某個事件,若是當前view攔截了某個事件,那麼在同一個事件序列當中,此方法不會再次調用,返回結果表示是否攔截當前事件

        public boolean onTouchEvent(MotionEvent event):在dispatchTouchEvent方法中調用,用來處理點擊事件,返回結果表示是否消耗當前事件,若是不消耗,則在同一事件序列中,當前View沒法再次接收到事件.

      

        一個點擊事件產生後,它的傳遞過程遵循以下順序:Activity->Window->View.

        1. 統一個事件序列是從手指接觸屏幕的那一刻開始,到手指離開屏幕的那一刻結束(以down事件開始,中間含有數量不定的move事件,最終以up事件結束).
        2. 正常狀況下,一個時間序列只能被一個View攔截並消耗.
        3. 某個View一旦決定攔截,那麼這一個事件序列都只能由它欄處理,而且它的onInterceptTouchEvent不會再被調用.
        4. 某個View一旦開始處理事件,若是他不消耗ACTION_DOWN事件(onTouchEvent返回false),那麼同一事件序列中的其它時間都不會再交給它被調用.
        5. 若是View不消耗ACTION_DOWN之外的其它時間,那麼這個點擊事件會小時,此時父元素的onTouchEvent並不會被調用,而且當前View能夠持續受到後續的時間,最終這些消失的點擊事件會傳遞給Activity處理.
        6. ViewGroup默認不攔截任什麼時候間
        7. View沒有onInterceptTouchEvent方法,一旦有點擊事件傳遞給它,那麼它的onTouchEvent方法就會調用
        8. VIew的onTouchEvent默認都會消耗事件(返回true),除非它是不可點擊的(clickable和龍Clickable同時爲false).
        9. View的enable屬性不影響onTouchEvent的默認返回值.
        10. onClick會發生的前提是當前View是可點擊的,而且它收到down和up的事件
        11. 事件傳遞過程是由外向內的,即事件老是先傳遞給父元素,而後再由父元素分發給子View,經過requestDisallowInterceptTouchEvent方法能夠在子元素中勇於父元素的事件分發過程,可是ACTION_DOWN事件除外.

      3.4.2 事件分發

        1.Activity對點擊事件的分發過程

          點擊事件用MotionEvent來表示,事件最早傳遞給當前Activity,由Activity的dispatchTouchEvent來進行事件派發,具體的工做是有Activity內部的Window來完成的.

            Window會將事件傳遞給decor view ,decor view 通常就是當前界面的底層容器,經過Activity.getWindow.getDecorView()能夠得到.

            Window是一個抽象類,實現類是PhoneWindow,PhoneWindow將時間直接傳遞給DecorView

            經過((ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content)).getChildAt(0)這種方式獲取Activity所設置的View

            而咱們經過setContentView設置的View是它的一個子View.

         2. 頂級View對點擊事件的分發過程

           點擊事件到達頂級View(通常是一個ViewGroup)之後,會調用ViewGroup的dispatchTouchEvent方法

           若是頂級ViewGroup攔截事件,即onInterceptTouchEvent返回true,則事件由ViewGroup處理,這時若是ViewGroup的mOnTouchListener被設置,則onTouchEvent會被調用,不然onTouchEvent會被調用.

           onTouch會屏蔽掉onTouchEvent,

           在onTouchEvent中,若是設置了mOnClickListener,則onClick會被調用.

           若是頂級View不攔截,則事件會傳遞給它所在的點擊事件鏈上的子View.

         3. View對點擊事件的處理過程

           首先會判斷有沒有設置OnTouchListener,若是OnTouchListener中的onTouch方法返回true,那麼onTouchEvent就不會被調用,OnTouchListener的優先級高於onTouchEvent

           只要View的CLICKABLE和LONG_CLICKABLE有一個爲true,那麼他就會消耗這個事件,即onTouchEvent方法會返回true

      

      3.5 View的滑動衝突

          1. 外部滑動方向跟內部滑動方向不一致

          2.外部滑動方向和內部滑動方向一致

          3.上面兩種狀況的嵌套

      3.5.2 滑動衝突的處理規則

          根據滑動時水平滑動仍是豎直滑動來判斷到底由誰來攔截事件

          根據具體的業務尋找具體規則

      3.5.3 滑動衝突的解決方法

          1.外部攔截法 

            點擊事件都先通過父容器的攔截處理,若是父容器須要此事件被攔截,若是不須要此事件就不攔截

            外部攔截髮須要重寫父容器的onInterceptTouchEvent方法,在內部作相應的攔截便可

            須要攔截,intercepted返回true,不攔截返回false

          2.內部攔截法

            指父容器不攔截任何事件,全部的事件都傳遞給子元素,若是子元素須要此事件就直接消耗掉,不然就交由父容器進行處理,須要配合requestDisallowInterptTouchEvent方法

            須要重寫子元素的dispatchTouchEvent方法.

相關文章
相關標籤/搜索