一 view基础
位置参数、MotionEvent、TouchSlop、VelocityTracker、GestureDetector和滑动
1.1 位置参数
相对于父容器
left:左上角横坐标 right:右下角横坐标 top:左上角纵坐标 bottom:右下角纵坐标
x:左上角横坐标 y:左上角纵坐标 translationX:左上角想对于父容器的横向偏移量 translationY:左上角想对于父容器的纵向偏移量 偏移量默认为0;
八个参数都有get/set方法:e.g:setLeft(); getLeft();
width = right - left;
height = bottom - top;
view平移时:left、right、top、bottom原始信息,值不变,对应改变的是x、y、translationX、translationY;
1.2 MotionEvent && TouchSlop
1.2.1 MotionEvent:
事件:ACTION_DOWN、ACTION_UP、ACTION_MOVE、ACTION_CANCEL等
点击事件坐标:
getX/getY,当前view左上角坐标;
getRawX/getRawY,想对于手机屏幕左上角坐标;
1.2.2 TouchSlop:
被系统认定为滑动的最小距离
获取这个值:ViewConfiguration.get(Context).getScaledTouchSlop();
ViewConfiguration中还定义了很多其他值
1.3 VelocityTracker、GestureDetector、滑动
1.3.1 velocityTracker速度追踪
//追踪当前速度
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
//获取1000ms时间的速度
velocityTracker.computeCurrentVelocity(1000);
int xVelocity = (int) velocityTracker.getXVelocity();
int yVelocity = (int) velocityTracker.getYVelocity();
//回收
velocityTracker.clear();
velocityTracker.recycle();
1.3.2 GestureDetector
检测单击、滑动、长按、双击等行为。
//创建对象,实现OnGestureListener或者OnDoubleTapListener接口。this代表实现的接口
GestureDetector mGestureDetector = new GestureDetector(this);
//解决长安无法拖动
mGestureDetector.setIsLongpressEnabled(false);
//在onTouchEvent中接管监听view
boolean onsume = mGestureDetector.onTouchEvent(event);
return onsume;
```javascript
然后在OnGestureListener和OnDoubleTapListener监听
OnGestureListener:
onDown:即ACTION_DOWN
onShowPress:手指按下,未松开未拖动
onSingleTapUp:单击行为的ACTION_UP
onScroll:按下并拖动
onLongPress:长按
onFling:按下,快速滑动后松开
onDoubleTapListener:
onSingleTapConfirmed:单击行为 (不会是双击中的一次)
onDoubleTap:双击
onDoubleTapEvent:双击行为中的ACTION_DOWN ACTION_MOVE ACTION_UP都会触发
###1.3.3 滑动
scrollTo/scrollBy
只滑动内容,不改变view在布局中的位置
mScrollX、mScrollY,view内容位置和布局位置的距离
从左向右滑动100,mSrollX = -100; 其他类似
#二 view的事件分发
##2.1 事件传递规则
用一张图就可以掌握
![](http://upload-images.jianshu.io/upload_images/3633065-46e707cef0d7f2a0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
箭头代表代码会这样走,会如何去调用
onInterceptTouchEvent事件拦截,只有viewgroup才有
除了onInterceptTouchEvent其他的返回true都是消费。
理清这个图,在脑海里有个U型概念,记住onInterceptTouchEvent,其它规则都相似
##2.2 滑动冲突
理解了事件的分发,滑动冲突就很好解决了。方法通常有两种
外部拦截:父视图需要就拦截,不需要就分发下去。
内部拦截:父视图都不拦截,子视图需要就消费,不需要就传递给父视图消费。
这里推荐使用外部拦截:即onInterceptTouchEvent
```javascript
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;
三 View的工作原理
3.1 绘制过程
三大过程:onMeasure测量、onLayout布局、onDraw画
3.1.1
如何绘制出来:
DecorView:顶级view
Viewroot:即viewrootImpl,WindowManager创建ViewRoot来管理DecorView
DecorView一般为一个竖直方向的LinearLayout,LinearLayout分为上下两个部分(主要跟设置主题有关)
上面是标题栏,下面是内容栏。例:activity中setContentView而不是setView
3.1.2三大流程
viewrootImpl中performTraversals函数开始绘制DecorView
performTraversals中依次调用performMeasure、performLayout、performDraw
Measure
(viewrootImpl)performTraversals -> (viewrootImpl)performMeasure -> (DecorViwe)measure -> (DecorViwe)onMeasure
DecorViwe的onMeasure中调用super.onMeasure即 (FrameLayout)onMeasure
在(FrameLayout)onMeasure中:
int count = getChildCount();
...
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
...
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
...
如此遍历所有view树,layout过程类似
onDraw
(viewrootImpl)performTraversals -> (viewrootImpl)performDraw -> (viewrootImpl)Draw -> (viewrootImpl)drawSoftware
看下drawSoftware有这样一句话
mView.draw(canvas);
这个draw方法就是view.java中的draw
在View中draw有如下代码:
...
drawBackground(canvas);
...
onDraw(canvas);
...
dispatchDraw(canvas);
...
最后调用到ViewGroup的dispatchDraw,遍历子view
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
看下drawChild
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}