博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android GUI之View布局
阅读量:6935 次
发布时间:2019-06-27

本文共 6425 字,大约阅读时间需要 21 分钟。

  在清楚了View绘制机制中的第一步测量之后,我们继续来了解分析View绘制的第二个过程,那就是布局定位。继续跟踪分析源码,根据之前的流程分析我们知道View的绘制是从RootViewImpl的performTraversals方法开始的,在此方法中依次调用了performMeasure、performLayout、performDraw等方法进行测量、布局、绘制,那么下面我们就看看则方performLayout中都做了哪些事情,该方法的关键源码如下:

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,            int desiredWindowHeight) {        mLayoutRequested = false;        mScrollMayChange = true;        mInLayout = true;        final View host = mView;          ……        try {            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());……        } finally {            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }        mInLayout = false;    }

  从中看出,最关键的代码就是调用了host.layout方法,那么大家还记不记得host是个什么东东呢?对了,正是我们之前说的根视图DecorView。那么我们就回到DecorView看看它在layout方法中到底做了什么事情。令人失望的是,我们在DecorView中并没有发现该方法,不要急,根据该类的继承体系,我们最终追踪到layout方法在View中。

public void layout(int l, int t, int r, int b) {        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;        }        int oldL = mLeft;        int oldT = mTop;        int oldB = mBottom;        int oldR = mRight;        boolean changed = isLayoutModeOptical(mParent) ?                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {            onLayout(changed, l, t, r, b);            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;            ListenerInfo li = mListenerInfo;            if (li != null && li.mOnLayoutChangeListeners != null) {                ArrayList
listenersCopy = (ArrayList
)li.mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for (int i = 0; i < numListeners; ++i) { listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); } } } mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; }protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}

  该方法中的4个参数代表了当前的View与父View之间4个方向上的距离,同时从说明中可以看出,此方法不应该被子类重写,如果需要重新布局,可以在子类中重写的方法是onLayout,此方法在View中是个空方法,什么都没有写。可实际上layout的方法在View中并没有被标识为final,这就意味是可以被重写的。

  继续查看ViewGoup中的相关代码,果然layout被重写了并添加了final标识,同时onLayout被标识为抽象方法,所以继承了ViewGroup的类是,是不能重写layout方法的,并且要实现onLayout方法。从代码可以看出,虽然ViewGroup重写了layout,实际本质上还是调用了View的layout,然后通过调用onLayout方法最终完成布局定位的。

@Override    public final void layout(int l, int t, int r, int b) {        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {            if (mTransition != null) {                mTransition.layoutChange(this);            }            super.layout(l, t, r, b);        } else {            // record the fact that we noop'd it; request layout when transition finishes            mLayoutCalledWhileSuppressed = true;        }    }    @Override    protected abstract void onLayout(boolean changed,            int l, int t, int r, int b);

  在DecorView中,并没有发现onLayout方法,所以它使用的肯定是其父类FrameLayout中的,找到FrameLayout的源码,可以查看到onLayout方法,具体如下:

@Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        layoutChildren(left, top, right, bottom, false /* no force left gravity */);    }    void layoutChildren(int left, int top, int right, int bottom,                                  boolean forceLeftGravity) {        final int count = getChildCount();        final int parentLeft = getPaddingLeftWithForeground();        final int parentRight = right - left - getPaddingRightWithForeground();        final int parentTop = getPaddingTopWithForeground();        final int parentBottom = bottom - top - getPaddingBottomWithForeground();        mForegroundBoundsChanged = true;                for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() != GONE) {                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                final int width = child.getMeasuredWidth();                final int height = child.getMeasuredHeight();                int childLeft;                int childTop;                int gravity = lp.gravity;                if (gravity == -1) {                    gravity = DEFAULT_CHILD_GRAVITY;                }                final int layoutDirection = getLayoutDirection();                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {                    case Gravity.CENTER_HORIZONTAL:                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +                        lp.leftMargin - lp.rightMargin;                        break;                    case Gravity.RIGHT:                        if (!forceLeftGravity) {                            childLeft = parentRight - width - lp.rightMargin;                            break;                        }                    case Gravity.LEFT:                    default:                        childLeft = parentLeft + lp.leftMargin;                }                switch (verticalGravity) {                    case Gravity.TOP:                        childTop = parentTop + lp.topMargin;                        break;                    case Gravity.CENTER_VERTICAL:                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +                        lp.topMargin - lp.bottomMargin;                        break;                    case Gravity.BOTTOM:                        childTop = parentBottom - height - lp.bottomMargin;                        break;                    default:                        childTop = parentTop + lp.topMargin;                }                child.layout(childLeft, childTop, childLeft + width, childTop + height);            }        }    }

  从方法中,我们可以看出在Framelayout中最终调用了layoutChildren方法,在该方法中根据测量结果和一些布局属性对容器中每一个View都调用了layout方法进行了布局。根据以上的代码分析,我们可以得出View布局定位的流程图如下。

 

  疑问咨询或技术交流,请加入官方QQ群: (452379712)

 

作者:
出处:
 
本文版权归和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 

转载于:https://www.cnblogs.com/jerehedu/p/4735772.html

你可能感兴趣的文章
Go Ticker资源泄露案例
查看>>
SVN
查看>>
利用Python来执行可执行jar包。
查看>>
[note]CSS相关(# 和 . 的区别,及CSS常用中文字体英文名称对照表
查看>>
cisco IOS更新
查看>>
mysql导出存储过程或函数
查看>>
mahout 安装
查看>>
【漏洞复现】 CVE-2018-9995 DVR登陆绕过漏洞
查看>>
Using Basemap 1.0.7 in Python 3.6
查看>>
Android禁止自动同步网络时间
查看>>
输入两个时间戳,计算差值
查看>>
对frameset、iframe、frame的js操作
查看>>
UML的9种图
查看>>
4月第2周中国五大顶级域名总量净增4.7万 美国净减4.3万
查看>>
8月第3周全球域名商(国际域名)新增注册量TOP16
查看>>
JS类似PHP的格式化时间
查看>>
解决:找不到或无法加载主类
查看>>
RFC2326(2) RSTP
查看>>
awk的基本使用方法
查看>>
geowebcache发布arcgislayer图层,并且修改行列号范围计算错误的bug
查看>>