Android View相关总结与View绘制原理解析

一 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);
    }

你可能感兴趣的:(Android View相关总结与View绘制原理解析)