美文网首页
Android View回顾

Android View回顾

作者: 生活简单些 | 来源:发表于2018-09-29 21:57 被阅读9次

Android View中的各种x和y都是啥

view_property.png

从Android3.0开始View增加了几个参数:x,y,translationX和translationY

xy是view左上角坐标,TranslationX和TranslationY是相对父容器的偏移量,TranslationX和TranslationY默认是0,getX() = mLeft + getTranslationX(),想要注意的是:View在平移的过程中,top和left表示的是原始左上角的位置信息,其值并不会变化,此时改变的是x、y、TranslationX和TranslationY。

mScrollX, mScrollY

  • mScrollX的值等于View的左边缘与View内容左边缘的距离,默认左边框和左边内容重合,即mScrollX值为0,要开始滑动肯定是从右往左滑动,所以越往左边滑值越大
  • mScrollY的值等于View的上边缘与View的内容的上边缘的垂直距离
  • scrollBy与scrollTo都只能改变View内容的位置,并不能改变View的在布局中的实际位置

MotionEvent

当手指触摸屏幕并滑动一系列事件中,通过回调获取MotionEvent对象可以得到点击事件发生的x和y坐标,系统提供了2组api:getX/getY和getRawX/getRawY,它们分别返回的是当前View左上角X和Y的坐标,以及相对于屏幕左上角的x和y坐标;

Android滑动

在android开发中有多种方式实现View的滑动,常见的有以下几种:

  1. 通过Scroller配合手势实现滑动;
  2. 通过ValueAnimator的updateListener更新差值变化再掉用View的scrollTo实现滑动动画;
  3. 通过Handler的postDelay循环scrollTo;

事件分发

事件分发涉及到三个方法:

public boolean dispatchTouchEvent(MotionEvent event);
public boolean onInterceptTouchEvent(MotionEvent ev);
public boolean void onTouchEvent(MotionEvent event);

它们的关系如下面伪代码所示:

public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume = false;
    if (onInterceptTouchEvent(ev)){
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    
    return consume;
}

上述伪代码已经将三者关系描述非常清楚:对于一个ViewGroup来说,点击事件产生后首先会传递给它,于是它的 dispatchTouchEvent就会被调用,接着事件就会交给ViewGroup处理,即它的onTouchEvent会被调用;
如果整个ViewGroup的onInterceptTouchEvent返回了false,表示它将不拦截当前事件,这时候子view的dispatchTouchEvent会被调用,如此反复直到事件被最终处理。

View绘制

View的绘制是从ViewRoot的performTraversals()开始的,它经过measure(), layout()和draw()三个过程才将一个View绘制出来。
其中,performTraversals()又会调用performMeasure(), performLayout()和performDraw(), 随后performMeasure()触发回调onMeasure(), performLayout()触发回调onLayout(), performDraw()触发onDraw()

MeasureSpec

MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式,而SpecSize是指某种测量模式下规格大小。
SepcMode有三类:

  1. UNSPECIFIED: 父容器不对View做任何限制,要多大给多大;
  2. EXACTLY: 在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少,对于与LayoutParams中的MATCH_PARENT或者具体size;
  3. AT_MOST: 这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小,对于LayoutParams中的WRAP_CONTENT;
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);

    int size = Math.max(0, specSize - padding);

    int resultSize = 0;
    int resultMode = 0;

    switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Child wants a specific size... so be it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {
            // Child wants a specific size... let him have it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size... find out how big it should
            // be
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size.... find out how
            // big it should be
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

由上可见,子view的size和mode由父容器的specMode以及子view自己的LayoutParams决定。

相关文章

网友评论

      本文标题:Android View回顾

      本文链接:https://www.haomeiwen.com/subject/twsvmftx.html