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=33;

private 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 voolean 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事件。

你可能感兴趣的:(android,布局,事件分发)