Android View 布局 过程
来源:互联网 发布:网站模板源码免费下载 编辑:程序博客网 时间:2024/06/04 19:48
当Android 的View测量完毕之后,才可以去布局。我们在测量时获取到的MeasuredHeight
和MeasuredWidth
,在布局的时候就会用到,毕竟只有知道尺寸才好确定位置。
布局相对于测量来说就相对简单了很多。布局的过程就是确定View在界面显示位置的过程。View是一个矩形,决定一个View在界面显示的位置的信息主要是四个边到父View的边距,这里会有四个变量来记录各个边到父View的距离:
- mLeft:View左边缘距离父View左部边缘的距离
- mTop:View上边缘距离父View上部边缘的距离
- mBottom:View底边缘距离父View上部边缘的距离
- mRight:View右边缘距离父View左部边缘的距离
注意:这里的距离单位是像素;Android 屏幕的像素原点在屏幕的左上角,所以View的宽高计算如下:
View的宽=mRight-mLeft
View的高=mBottom-mTop
这里的宽高和测量来的MeasuredWidth和MeasuredHeight是不同的,不要混淆.看它们的源码:
测量得到的高
public final int getMeasuredHeight() { return mMeasuredHeight & MEASURED_SIZE_MASK;}
View实际的高
public final int getHeight() { return mBottom - mTop;}
width和MeasuredWidth代码跟上面的代码类似就不在贴了。
布局是绘制的前提,只有布局完毕才可以去绘制。View布局时调用的方法是layout(int l, int t, int r, int b)
它的是个参数就是代表View四个边距离父View的距离。虽然View了layout 方法,但是不提倡去重写它,如果有需要我们应该去重写 onLayout(boolean changed, int left, int top, int right, int bottom)
,我们先看一下layout方法:
public void layout(int l, int t, int r, int b) { //判断onMeasure是否应该跳过,(变量mPrivateFlags3存着一些关于Layout的信息,应该是用来判断View是否测量过,具体的没研究过) 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; //根据布局模式来判断是调用setOpticalFrame还是setFrame; //setOpticalFrame内部也会再次调用setFrame,所以不论isLayoutModeOptical是false还是true,都会调用setFrame //setFrame用来分配View的位置和大小,如果新传入的位置和尺寸与之前的位置和尺寸有差别就会返回true。 //所以这里获取的是View的位置或者尺寸是否发生变化 boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { //如果View的位置或者尺寸发生变化或者满足(mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED,就会首先调用onLayout //onLayout是空的,没有任何实现,需要我们自己去实现。 //凡是继承ViewGroup的类一般都要重写这方法,来确定其中的子view的位置和尺寸 onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; //这里设置 OnLayoutChangeListener 监听事件 //ListenerInfo里面专门有个ArrayList来存储OnLayoutChangeListener ListenerInfo li = mListenerInfo; if (li != null && li.mOnLayoutChangeListeners != null) { //对ArrayList中的OnLayoutChangeListener中的事件监听器进行克隆 ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for (int i = 0; i < numListeners; ++i) { //遍历添加进来的事件监听器,依次调用onLayoutChange方法;这样layout就能在收到布局发生变化时得到响应了 listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); } } } mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;}
onLayout(boolean changed, int left, int top, int right, int bottom)
方法很简单,首先会判断一下是需要测量一下,如果不需要,记录一下传入的四个边到父View的边距;然后判断View的尺寸和位置是否变化过,如果变化去就调用onLayout方法,并且遍历OnLayoutChangedListener监听器。
onLayout(boolean changed, int left, int top, int right, int bottom)
这个方法是空的,是需要子类去重写的。它的主要作用就是确定该View中子view的位置。通常会遍历该View中所有的子view,然后依次调用子view的layout方法,来确定子View的位置和尺寸。我们看一下FrameLayout中部分代码:
@Overrideprotected 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) { //获取子View的个数 final int count = getChildCount(); //获取父View的四边 final int parentLeft = getPaddingLeftWithForeground(); final int parentRight = right - left - getPaddingRightWithForeground(); final int parentTop = getPaddingTopWithForeground(); final int parentBottom = bottom - top - getPaddingBottomWithForeground(); //遍历所有的子view for (int i = 0; i < count; i++) { final View child = getChildAt(i); //只有在子view可见并且不占据位置的时候才去设置子view的尺寸和位置 if (child.getVisibility() != GONE) { //……省略子View在各个Gravity情况下四边距离屏幕边距的数值…… child.layout(childLeft, childTop, childLeft + width, childTop + height); } }}
所以我们在继承ViewGroup时,重写onLayout方法也可以做类似的处理。
在上面我们提到布局变化监听,其实在View里提供了一个onSizeChanged(newWidth, newHeight, oldWidth, oldHeight)
;当View的尺寸发生变化时就会调用这个方法。不过它刚加入的View树中时,它使用还之前的尺寸,都是0。
在源码里,它是在 setFrame(int left, int top, int right, int bottom)
中调用的。上面提到了setFrame是用来分配View的位置和大小,如果新传入的位置和尺寸与之前的位置和尺寸有差别就会返回true。简单看一下setFrame调用onSizeChanged的过程:
protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; //……省略log //新传入边距跟之前记录的边距不同,就说明View的尺寸或者位置发生变化 if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true; int drawn = mPrivateFlags & PFLAG_DRAWN; int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; int newWidth = right - left; int newHeight = bottom - top; //根据新边距和旧边距,判断尺寸是否发生变化 boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight); //重新绘图 invalidate(sizeChanged); //将新边距赋值 mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); mPrivateFlags |= PFLAG_HAS_BOUNDS; //如果尺寸发生变化就会调用sizeChange,onSizeChanged就是在sizeChange方法里调用的 if (sizeChanged) { sizeChange(newWidth, newHeight, oldWidth, oldHeight); } if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) { mPrivateFlags |= PFLAG_DRAWN; invalidate(sizeChanged); invalidateParentCaches(); } mPrivateFlags |= drawn; mBackgroundSizeChanged = true; if (mForegroundInfo != null) { mForegroundInfo.mBoundsChanged = true; } notifySubtreeAccessibilityStateChangedIfNeeded(); } return changed;}private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) { //调用onSizeChanged方法,这个方法是空的;如果有操作需要在尺寸发生变化时,可以重写这个方法 onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); if (mOverlay != null) { mOverlay.getOverlayView().setRight(newWidth); mOverlay.getOverlayView().setBottom(newHeight); } rebuildOutline();}
上面代码没有什么难度,在setFrame方法里根据新边距和旧边距,判断尺寸是否发生变化,如果变化就去调用sizeChange方法,onSizeChanged就是在sizeChange中调用的。
- Android View 布局 过程
- android View的布局过程
- Android中将布局文件/View添加至窗口过程分析
- 源码解析Android中View的layout布局过程
- 源码解析Android中View的layout布局过程
- Android中将布局文件/View添加至窗口过程分析
- 源码解析Android中View的layout布局过程
- View的布局(layout)过程
- Android之View布局
- Android布局---List View
- android View控件布局
- android常用view布局
- Android View深入学习(二),View的布局(Layout)过程
- Android View框架总结(六)View布局流程之Draw过程
- Android View框架总结(六)View布局流程之Draw过程
- android视图学习---Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android View measure过程
- Android View绘制过程
- jsoup解析网站资源。Android
- 支付宝集成最新常见问题详解
- jenkins构建
- 如何在Android Studio中使用LeakCanary检测内存泄露
- Servlet生命周期,工作原理,eclipse第一个Servlet,Servlet线程安全
- Android View 布局 过程
- 外部系统调用AWS ML服务的接口 - Python
- Android Studio导入项目一直卡在Building gradle project info最快速解决方案
- 数据结构 B+树c代码实现
- Windows系统远程访问ubuntu系统
- 初识 Oracle启动的7个服务
- 顶级域名与二级域名共享Cookie测试
- Linux C++多线程编程
- XYXFilterMenu