Android開發藝術探索學習筆記(三),android藝術探索

來源:互聯網
上載者:User

Android開發藝術探索學習筆記(三),android藝術探索

第三章  View的事件體系

  3.1 View基礎知識

  3.1.1 什麼是view

    View 是Android中所有控制項的基類,是一種介面層的控制項的一種抽象,它代表了一個控制項。

  3.1.2 View的位置參數

    View的位置主要由它的四個頂點來決定,分別對應於View的四個屬性:top,left,right,bottom;需要注意的是這些座標都是相對於View的父容器來說的;(在Android中X軸和Y軸的正方向分別為右和下)。

    3.0開始新增的屬性

    x-  view左上方橫座標;

    y-  view左上方縱座標;

    translationX-  view左上方相對於父容器的水平位移量;

    translationY-  view左上方相對於父容器的垂直位移量;

    View在平移過程中,top和left表示的是原始左上方的位置資訊,其值並不會發生變化,此時發生變化的是x,y,translationX,translationY這四個參數。

  3.1.3 MotionEvent和TouchSlop

    1 MotionEvent

    getX和getY方法返回的是當前View左上方的x和y座標,而getRawC和getRawY方法返回的是相對於手機螢幕左上方的x和y座標。

    2 TouchSlop

    TouchSlop是系統所能識別出的被認為是滑動的最小距離,是一個常量,通過如下方式可以獲得這個常量:ViewConfiguration.get(getContext()).getScaledTouchSlop();

  3.1.4 VelocityTracker,GestureDetector和Scroller

    1 VelocityTracker

    VelocityTracker—速度追蹤,追蹤手指在滑動過程中的速度(水平垂直兩個方向),注意速度可以為負值,當手指從右向左滑動時,水平方向速度即為負值。

    2 GestureDetector

    GestureDetector—手勢檢測,用於輔助檢測使用者的單擊,滑動,長,雙擊等行為。

    建議:如果只是監聽滑動相關的,在onTouchEvent中實現,如果要監聽雙擊行為就用GestureDetector。

    3 Scroller

    Scroller—彈性滑動,用於實現View的彈性滑動(有過渡效果的滑動),需要配合View的computeScroll方法使用。

  3.2 View的滑動

    實現View滑動的三種方式:

    1 通過View本身的scrollTo/scrollBy方法實現滑動;

    2 通過動畫給View施加平移效果實現滑動;

    3 通過改變View的LayoutParams使得View重新布局實現滑動;

  3.2.1 使用scrollTo/scrollBy

    scrollTo/scrollBy的源碼如下:

public void scrollTo(int x ,int y){if(mScrollX!=x||mScrollY!=y){    int oldX=mScrollX;    int oldY=mScrollY;    mScrollX=x;    mScrollY=y;    invalidateParentCaches();    onScrollChanged(mScrollX,mScrollY,oldX,oldY);    if(!awakenScrollBars()){    postInvalidateOnAnimation();    }  }}public void scrollBy(int x,int y){    scrollTo(mScrollX+x,mScrollY+y);}

    其中mScrollX的值總是等於View左邊緣和View內容左邊緣在水平方向的距離,mScrollY的值總是等於View的上邊緣和View內容上邊緣在垂直方向上的距離;

     scrollTo/scrollBy只能改變View內容的位置而不能改變自身在布局中的位置。

     也就是說使用scrollTo/scrollBy來實現View的滑動只能將View的內容進行移動,並不能將View本身進行移動。

  3.2.2 使用動畫

    使用動畫來移動View 主要是操作View的translationX和translationY屬性。(傳統的View動畫和屬性動畫)

     View動畫是對View的影像操作,它並不能真正改變View的位置參數,包括寬/高。

  3.2.3 改變布局參數

    主要是通過改變View的LayoutParams來實現;下面的代碼展示如何給一個mButton1重新設定LayoutParams:

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

  3.2.4各種滑動方式的對比

    scrollTo/scrollBy:操作簡單,適合對View內容的滑動;

     動畫:操作簡單,主要適用於沒有互動的View和實現複雜的動畫效果;

     改變布局參數:操作稍微複雜,適用於有互動的View。

  3.3彈性滑動

  3.3.1使用Scroller

     注意Scroller產生的滑動也是對View內容的滑動而非View本身位置的改變。

    Scroller的典型使用方式如下:

Scroller scroller=new Scroller(context);//緩慢滾動到指定位置private void smoothScrollTo(int destX,int destY){    int scrollx=getScrollX();    int deltaX=destX-scrollX;    //1000ms內滑向destX,效果就是慢慢滑動    scroller.startScroll(scrollX,0,deltaX,0,1000);    invalidate();}@Overridepublic void computeScroll(){    if(scroller.computeScrollOffset()){    scrollTo(scroller.getCurrX(),scroller.getCurrY());    postInvalidate();    }}

    Scroller的工作機制:Scroller本身並不能實現View的滑動,它需要配合View的computeScroll方法才能完成彈性滑動的效果,它不斷的讓View重繪,而每一次重繪距滑動起始時間會有一個時間間隔,通過這個時間間隔Scroller就可以得出View當前的滑動位置,知道了滑動位置就可以通過scrollTo方法來View的滑動。就這樣,View的每一次重繪都會導致View進行小幅度的滑動,而多次的小幅度滑動就組成了彈性滑動。

  3.4 View的時間分發機制

  3.4.1 點擊事件的路由規則

    點擊事件的分發過程主要由三個方法來完成。

    dispatchTouchEvent() :用來進行事件的分發,如果事件能夠傳遞給當前View,此方法一定會被調用,返回結果受當前View的onTouchEvent和下級View的dispatchTouchEvent方法影響,表示是否消耗當前事件。

    onInterceptTouchEvent() :在dispatchTouchEvent方法內部調用,判斷是否攔截某個事件,如果當前View攔截了某個事件,那麼在同一個事件序列中,此方法不會在被調用,返回結果表示是否攔截當前事件。

    onTouchEvent() :在dispatchTouchEvent方法中調用,用來處理點擊事件,返回結果表示是否消耗當前事件,如果不消耗,則在同一個事件序列中,當前View無法再次接收到其他事件。

    事件序列是指從手指接觸螢幕的那一刻起,到手指離開螢幕的那一刻結束,在這個過程中所產生的一系列事件,這個事件序列以down事件開始,中間含有數量不固定的move事件,最終以up事件結束。

    理解了這三個方法的工作過程也就理解了事件的分發機制。虛擬碼表示三個方法的關係如下:

public boolean dispatchTouchEvent(MotionEvent ev){    boolean consume=false;    if(onInterceptTouchEvent(ev)){         consume=onTouchEvent(ev);          }else{         consume=child.dispatchTouchEvent(ev);        }     return consume;     }

    具體路由規則:對於一個根ViewGroup來說,點擊事件產生以後,首先會傳遞給它,這時它的dispatchTouchEvent就會被調用,如果這個ViewGroup的onInterceptTouchEvent方法返回true就表示它要攔截當前事件,接著事件就會交給這個ViewGroup處理,即它的onTouchEvent方法就會被調用,如果這個ViewGroup的onInterceptTouchEvent返回false就表示它不攔截當前事件,這時當前事件就會繼續傳遞給它的子項目,接著子項目的dispatchTouchEvent方法就會被調用,如此反覆直到事件被最終處理。

     當一個點擊事件產生後,它的傳遞過程遵循如下順序:Activity->Window->View,即事件總是先傳遞給Activity,Activity在傳遞給Window,最後Window在傳遞給頂級View。頂級View接收到事件後,就會按照事件分發機制去分發事件。考慮一種情況,如果一個View的onTouchEvent返回false,那麼它的父容器的onTouchEvent將會被調用,依此類推。如果所用的元素都不處理這個事件,那麼這個事件將會最終傳遞給Activity處理,即Activity的onTouchEvent方法被調用。
    幾個重要結論:

    正常情況下,一個事件序列只能被一個VIew攔截且消耗,因為一旦一個元素攔截了某個事件,那麼同一個事件序列內的所有事件都會直接交給它處理,因此同一個事件序列中的事件不能分別有兩個View同時處理,但是通過特殊手段可以做到,比如一個View將本該自己處理的事件通過onTouchEvent強行傳遞給其他View處理。

    某個View一旦決定攔截,那麼這一個事件序列都只能由它處理,並且它的onInterceptTouchEvent不會再被調用。

    某個View一旦開始處理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那麼同一事件序列中的其他事件都不會再交給它處理,並且事件將重新交由它的父元素去處理,即父元素的onTouchEvent會被調用。意思就是事件一旦交給一個View處理,那麼它就必須消耗掉,否則同一事件序列中剩下的事件就不再交給它來處理了。

    如果View不消耗出ACTION_DOWN以外的其他事件,那麼這個點擊事件會消失,最終這些消失的點擊事件會傳遞給Activity處理。

    ViewGroup預設不攔截任何事件。

    View沒有onInterceptTouchEvent方法,一旦有事件傳遞給它,那麼它的onTouchEvent方法就會被調用。

    View的OnTouchEvent方法預設都會消耗事件(返回true)。

    View的enable屬性不影響onTouchEvent的預設傳回值。

    onClick會發生的前提是當前View是可點擊的,並且它收到了down和up的事件。

    事件的傳遞過程是由外向內的,即事件總是項傳遞給父元素,然後再由父元素分發給子View,通過requestDisallowInterceptTouchEvent方法可以在子項目中幹預父元素的事件分發過程,但是ACTION_DOWN事件除外。(requestDisallowInterceptTouchEvent方法主要設定父元素中的FLAG_DISALLOW_INTERCEPT標記位,一旦設定後,ViewGroup將無法攔截除了ACTION_DOWN以外的其他點擊事件)

  3.5 View的滑動衝突

     常見的滑動衝突情境

    

     解決滑動衝突的方式:外部攔截法和內部攔截法。

    1 外部攔截法

    點擊事件都先經過父容器的攔截處理,如果父容器需要此事件就攔截,如果不需要此事件就不攔截。

    實現方法主要是重寫父容器的onInterceptTouchEvent方法,代碼如下:

  @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        boolean intercepted=false;        int x=(int)event.getX();        int y=(int)event.getY();        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                intercepted=false;                break;            case MotionEvent.ACTION_MOVE:                if(父容器需要當前點擊事件){                   intercepted=true;                }else {                    intercepted=false;                }                break;            case MotionEvent.ACTION_UP:                intercepted=false;                break;            default:                break;        }        mLastXIntercept=x;        mLastYIntercept=y;        return intercepted;    }

    2 內部攔截法

    父容器不攔截任何事件,所用的事件都傳遞給子項目,如果子項目需要此事件就直接消耗掉,否則就交由父容器進行處理。需要配合requestDisallowInterceptTouchEvent方法才能正常工作,同時重寫子項目的dispatchTouchEvent方法,代碼如下:

    子項目:

   @Override    public boolean dispatchTouchEvent(MotionEvent event) {        int x=(int)event.getX();        int y=(int)event.getY();        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                getParent().requestDisallowInterceptTouchEvent(true);                break;            case MotionEvent.ACTION_MOVE:                int deltaX=x-mLastX;                int deltaY=y-mLastY;                if(父容器需要當前點擊事件){                    getParent().requestDisallowInterceptTouchEvent(false);                }                break;            case MotionEvent.ACTION_UP:                break;            default:                break;        }        mLastX=x;        mLastY=y;        return super.dispatchTouchEvent(event);    }

    父元素:

    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        int action=event.getAction();        if (action==MotionEvent.ACTION_DOWN){            return false;        }else{            return true;        }    }

 

    以上就是滑動處理滑動衝突的典型代碼,當面對不同的滑動策略時(情境1,2,3)只需要修改裡面的條件即可。

            

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.