1.view简介
view是android里面所有控件类的基类,包括布局,常见view都是继承自view。
view的位置参数主要有四个left right top bottom(相对参数,相对的是父容器)
还可以确定位置的是(x,y)左上角的坐标,translationX,translationY表示的是位移之后的位置(这两个也是相对位置)
2.事件简介
MotionEvent是android里面的事件抽象,可以分为:
- ACTION_DOWN:手指接触屏幕那一瞬间产生的事件
- ACTION_UP:手指从屏幕离开那一瞬间产生的事件
- ACTION_MOVE:手指在屏幕上移动产生的事件,有可能是多个,其中涉及到一个概念TouchSlop即最小滑动距离,如果滑动的距离小于这个数那么不认为用户在滑动
一个事件序列指的是:手指接触屏幕,然后移动到手指离开屏幕产生的一个事件序列,这个序列以ACTION_DOWN开始,中间有0个或多个ACTION_MOVE,最后以ACTION_UP结束
3.view滑动简介
view的滑动有三个方面需要注意:
- 通过scrollTo(int x,int y),scrollBy(int x,int y),都是对于内容的滑动
- 重新设置translationX和translationY属性来改变位置,接下来的这个例子用的是动画库里面的属性动画。ObjectAnimator.ofFloat(targetView,"translationX",0,100).setDuration(100).start();这种方式可以用来实现复杂的效果
- 重新设置布局方式,修改view的各种margin和padding等等
MarginLayoutParams params=(MarginLayoutParams)mButton1.getLayoutParams(); params.width+=10; params.leftMargin+=100; mButton1.requestLayout();
4.事件分发机制
首先需要明确的是:android事件的传递规则:activity->window->view,然后由底层view不断向子元素分发。事件的传递和处理过程实际上是MotionEvent对象的传递和处理的过程。
4.1activity和window在事件传递里面的角色
所有的事件都会先传给activity,然后传给window再传递给底层view,当所有的都不能处理这个事件的时候,activity的onTouchEvent会被调用。
常见的关于事件拦截和处理的函数(viewgroup和activity)
public boolean dispatchTouchEvent(MotionEvent ev) | 用于事件分发,返回结果表示是否消耗当前事件 |
public boolean onInterceptTouchEvent(MotionEvent ev) | 判断是否拦截某个事件 |
public boolean onTouchEvent(MotionEvent ev) | 用于判断是否消耗某个事件,里面包含了事件的处理逻辑 |
调用关系
public boolean dispatchTouchEvent(MotionEvent e){
boolean consume=false;
if(onInterceptTouchEvent(ev)){
consume=onTouchEvent(ev);
}else{
consume=child.dispatchTouchEvent(ev);
}
return consume;
}
4.2activity传递事件到底层view
从activity到window
public boolean dispatchTouchEvent(MotionEvent ev){
if(getWindow().dispatchTouchEvent(ev)){
return true;//如果被处理了,那么整个事件循环就结束了
}
return onTouchEvent(ev);//如果没有被处理,那么就调用activity自身的onTouchEvent(MotionEvent ev)
}
从window到activity
直接调用getDecorView().dispatchTouchEvent(ev);
这里需要说明一下DecorView这个概念:
DecorView:整个界面的底层容器,继承子FrameLayout和PhoneWindow,作用是设置窗口属性,比如标题栏等等,findViewById就是从DecorView开始遍历。Android的view树结构示意图:
从DecorView不断向下传递就完成了事件分发
4.3一些规则:
- 一个事件序列如果被一个view拦截,那么这个事件序列的剩下的事件会直接发给他而不会调用是否拦截这个函数
- 一个事件序列只能被一个view拦截和消耗
- 某个view如果不消耗ACTION_DOWN那么整个事件序列都不会交给他(交给父元素的onTouchEvent),如果整个事件序列里面只消耗了ACTION_DOWN那么这个事件会消失,消失的事件交给Activity的onTouchEvent处理(不会交给父元素)
- ViewGroup默认不拦截任何事件
- View没有询问是否拦截的方法onInterceptTouchEvent,一旦事件传给他,那么onTouchEvent会被调用
- View是否消耗事件由两个属性分别决定 clickable或者longClickable(一般的默认longClickable都为false)
- 有ACTION_DOWN才会触发onClick
- 除了ACTION_DOWN子元素可以干扰父元素的事件传递过程requestDisallowInterceptTouchEvent
- View如果有onTouchListener那么onTouchEvent不会被调用
5.事件分发机制解决滑动冲突