Android中View的事件體系(2)——View滑動與事件分發

1、View的滑動

1.1、使用ScrollTo和ScrollBy滑動

ScrollTo(x,y):滑動到某個位置,傳遞的是位置的座標
ScrollBy(x,y):滑動某個距離,傳遞的是移動過的距離
同時這兩個方法移動的是view中的內容,而不是view本身,比如作用於Layout,將會移動的是layout中button的位置。從理解上來說,可以理解爲畫布在移動。

操作簡單,適合於對View的內容滑動

1.2、使用動畫滑動

可以通過如下代碼調用ObjectAnimator來實現動畫滑動效果,這裏給出簡單的實例代碼,詳細代碼參見本人的另一篇博客

ObjectAnimator.ofFloat(targetView,"translationX",0,100).setDuration(100).start();
操作簡單,但是3.0以下不兼容,可以實現複雜的動畫效果

1.3、改變佈局參數實現滑動

一般我們可以改變一個view的佈局來對它進行移動,如果想把一個button向右移動100px,只需要將這個Button的LayoutParams裏的marginLeft參數的值增加100px即可。示例代碼如下:

MarginLayoutParams params = (MarginLayoutParams)mButton1.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
mButton1.requestLayout();
//或mButton1.setLayoutParams(params);
操作稍微複雜,適用於Android的所有api,同時可以保留交互事件

1.4、彈性滑動

使用Scroller
Scroller scroller = new Scroller(mContext);
//緩慢滑動到指定的位置
private void smothScrollTo(int destX,int destY){
    int scrollX = getScrollX();
    int delta = destX - scrollX;
    mScroller.startScroll(scrollX,0,delta,0,1000);
    invalidate();
}

@Override
public void computeScroll(){
    if(mScroller.computeScrollOffset()){
        scrollTo(mScroller.etCurrX(),mScroller.getCurrY());
        postInvalidate();
    }
}
利用動畫

利用動畫本身的間隔時間來完成

ObjectAnimator.ofFloat(TargetView,"translationX",0,100).setDuration(100).start();

利用監聽動畫更新的方法實現移動

ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000);
animator.addUpdateListeneer(new AnimatorUpdateListener(){
    @Override
    public void onAnimationUpdate(ValueAnimator animator){
        float fraction  = animator.getAnimatedFraction();
        mButton1.scrollTo(startX + (int) (deltaX*fraction),0);
    }
});
animator.start();
使用延時策略

實現原理是通過發送一系列的延時消息,從而達到漸變的效果。實際上就是通過while不斷髮消息和睡眠,從而達到動畫效果。

private static final int MESSAGE_SCROLL_TO  = 1;
private static final int FRAME_COUNT = 30;
private static final int DELAYED_TIME=33private int mCount = 0;

private Handler mHandler =  new Handler(){
    public void handleMEssage(Message msg){
        switch(msg.what){
        case MESSAGE_SCROLL_TO:{
            mCount++;
            if(mCount <= FRAME_COUNT{
                float fraction = mCount/(float)FRAME_COUNT;
                int scrollX = (int) (fraction*100);
                mButton1.scrollTo(scrollX,0);
                mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO,
                DELATED_TIME);
            }
            break;
        }
        default:
            break;
        };
    };
}

2、View的事件分發機制

事件分發機制就是其實就是對MotionEvent事件的分發過程。即當一個MotionEvent(點擊事件)產生後,系統需要把這個事件傳遞給一個具體的view進行處理,這個過程就是事件的分發過程。

2.1、點擊事件的傳遞規則

事件分發過程由三個很重要的方法來共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev)

事件進入當前view調用的第一個方法,用來進行事件分發。返回結果受到當前view的onTouchEvent方法和下級view的dispatchTouchEvent的方法的影響,表示是否消耗當前事件。

public boolean onInterceptTouchEvent(MotionEvent event)

在disPatchTouchEvent方法的內部調用,用來判斷是否攔截當前事件。如果攔截則執行onTouchEvent方法,否則執行下級view的dispatchTouchEvent方法。

public boolean onTouchEvent(MotionEvent event)

用來處理點擊事件中的各種方法,返回結果便是是否消耗當前的點擊事件。如果不消耗,則在同一個事件序列中,當前view無法再次接收到事件。

事件處理過程僞代碼
public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume = false;
    if(onInterceptTouchEvent(ev)){//判斷是否攔截事件
        consume = onTouchEvent(ev);//是否消耗當前事件
    }else{
        consume = child.dispatchTouchEvent(ev);//子事件處理
    }
    return consume;//返回false會調用父控件的onTouchEvent方法
}
常用事件處理方法的優先級

OnTouchListener>onTouchEvent>onClickListener

2.2、事件傳遞相關結論

1、事件序列:從手指接觸屏幕的那一刻起,到手指離開屏幕的那一刻結束,在這個過程中產生的一系列事件。
2、一個事件序列真能被一個view攔截且消耗。除非在onTouchEvent中強行傳遞給其它view
3、如果view的onTouchEvent返回false,則事件不會向下傳遞,而會調用父控件的onTouchEvent方法進行處理。
4、如果view只消耗ACTION_DOWN事件,那麼這個點擊事件的其他後續事件會返回給Activity進行處理。
5、ViewGroup默認不攔截任何事件。
6、非ViewGroup的View沒有onInterceptTouchEvent方法,一旦有點擊事件傳遞給它,那麼它就會執行onTouchEvent方法。
7、非ViewGroup的View會默認消耗onTouchEvent,除非把clickable和longclickable同時設置爲false。
8、onClick發生情況必須是當前view是可點擊的,並且它收到了down和up事件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章