美文网首页
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