试述网站建设的流程.星空传媒有限公司网站
- 作者: 五速梦信息网
- 时间: 2026年03月21日 08:46
当前位置: 首页 > news >正文
试述网站建设的流程.,星空传媒有限公司网站,微网站怎么注册,网站首页制作的过程都2023年了#xff0c;不会还有人不知道什么是View吧#xff0c;不会吧#xff0c;不会吧。按我以往的面试经验来看#xff0c;View被问到的概率不比Activity低多少哦#xff0c;个人感觉View在Android中的重要性也和Activity不相上下#xff0c;所以这篇文章将介绍下Vie…都2023年了不会还有人不知道什么是View吧不会吧不会吧。按我以往的面试经验来看View被问到的概率不比Activity低多少哦个人感觉View在Android中的重要性也和Activity不相上下所以这篇文章将介绍下View的基础知识及其工作原理。
一、初识View
什么是View 所谓的View就是Android中所有控件的基类例如Button、TextView、LinearLayout等等甚至是ViewGroup也是继承自View的然而ViewGroup是一个控件组里面包含许多控件也就是一组View所以这就意味着View本身既可以是单个控件也可以是由许多控件组成的一个控件这样看来View其实也没那么玄乎啊。 View的位置参数
首先我们要知道在Android手机中坐标系是以手机屏幕的最左上角为原点而建立的大家可以参考下面的图片理解下。 其次View的初始位置由四个属性决定分别是top、left、right、bottom。其中top和left为View左上角的纵坐标和横坐标而bottom和right为View右下角的纵坐标和横坐标。看官注意这些坐标全都是相对于View所在的父容器来说的是一个相对坐标并不是在手机屏幕中的实际坐标同样在下面放上图示帮助大家理解 所以通过上面的一通分析我们可以得出View的宽高和坐标之间的关系
width right - left
height bottom -top细心的看官可能发现了在上面我把”初始位置“四个字给标红了。没错那四个属性不仅仅是初始位置而且在你的View不论是发生旋转或者平移时候他们都不会改变改变的其实是另外的位置参数。从Android3.0开始View增加了几个额外的参数它们分别是x、y、translationX、translationY,其中x和y是View左上角的坐标而translationX、translationY是View左上角相对于父容器的偏移量,注意这几个参数同样是针对父容器而言的并且translationX和translationY的默认值是0它们有以下的换算关系
x left translationX
y top translationY所以不难看出当View发生位置改变时改变的其实是x、y、translationX、translationY这四个参数。好了以上就是View位置参数的全部内容如果以上内容各位看起来比较轻松的话那么接下来的内容可能比较费劲接下来继续发车了。
二、DecorView和MeasureSpec View的三大流程无非就是Measure、Layout、Draw但这三大流程都是基于DecorView中呈现的然而想要呈现出View还需要知道View的大小在测量过程中MeasureSpec又是其中的关键所以接下来我们有必要了解下他们。 初识DecorView
DecorView是Activity里的顶级View它一般来说是一个竖直方向的LinearLayout这与Android的版本和主题有关,在这个LinearLayout里面有上下两部分上面是标题栏下面是内容栏。我们在Activity的onCreate()方法中通过setContentView所设置的布局文件就被加入到了DecorView的内容栏之中内容栏的id为content通过ViewGroup content findViewById(R.android.id.content)可以得到content,View层的事件都会先经过DecorView之后才继续向下传递的。同样是一图胜千言 理解MeasureSpec
第一个问题什么是MeasureSpec在《Android开发艺术探索》一书中对它的解释是这样的MeasureSpec翻译过来是”测量规格“或者”测量说明书“是一个32位的int值高2位代表SpecMode低30位代表SpecSize。而SpecMode指测量模式SpecSize指在某种测量模式下的规格大小。
第二个问题MeasureSpec是干啥的同样书中的解释是它在很大程度上决定了一个View的尺寸规格之所以说是很大程度上是因为这个过程还受父容器的影响因为父容器影响View的MeasureSpec的创建过程。在测量过程中系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec然后再根据这个measureSpec来测量出View的宽高。
可谓是听君一席话胜似听君一席话我一开始看也是云里雾里的接下来我尽量不深入代码带大家通俗的理解一下
我回答下第一个问题MeasureSpec其实就是一串数字一串长度为32位的数字里面包含着一些View的测量信息。
第二个问题的答案就是通过这一串数字可以帮助系统测量出View的宽和高。
这够通俗了吧这就跟你网购的快递一样的快递单号就是那一串莫名其妙的数字MeasureSpec通过这一串数字能帮助你查询到快递运到哪了换算到View上不就是帮助系统知道View的宽和高嘛这是一个道理听懂就来点掌声。
那么接下来就要详细了解下系统是怎么通过这串数字MeasureSpec来测量出View的宽高的。在上面提到SpecMode和SpecSize。 SpecSize比较简单通俗理解就是View在父容器下的实际大小或者是可用大小,有人可能会问为什么还会有个可用大小这里我解释下当你在布局文件中给定一个View一个确切的大小时那么SpecSize就是实际大小例如android:layout_width x dp、android:layout_height x dp。反之如果这两个属性给的值为”match_parent“、或者是wrap_content时此时的SpecSize就是父容器下的可用大小。 SpecMode相对于SpecSize而言削微有那么点抽象它分为三类分别如下 UNSPECIFIE父容器不对View有任何限制要多大就给大多这一般用于系统内部表示一种测量的状态一般来说可忽视。EXACTLY父容器已经检测出View所需要的精确大小这个时候View的最终大小就是SpecSize所指定的值它对应于LayoutParams中的match_parent和赋予具体数值的这两种模式AT_MOST父容器指定了一个可用大小即SpecSizeView的大小不能大于这个值具体是什么值需要看不同View的具体实现。它对应于LayoutParams中的wrap_content 通俗的说你在layout布局文件中给View的”match_parent“、和wrap_content属性设置不同的值再根据父容器的SpecMode加以对应就会得到View实际的SpecMode具体的对应关系如下图来图中的parentSize指的是父容器中目前可使用的大小表格中的UNSPECIFIED中的Size为 0 表示忽略在普通的View中是不会出现的只会在例如DecorView这种系统级别的才会出现
parentSpecMode /childLayoutParamsEXACTLYAT_MOSTUNSPECIFIEDdp/pxEXACTLY childSizeEXACTLY childsizeEXACTLY childsizematch_parentEXACTLY parentSizeAT_MOST parentSizeUNSPECIFIED 0wrap_contentAT_MOST parentSizeAT_MOST parentSizeUNSPECIFIED 0
所以只要提供了父容器的MeasureSpec和子元素的LayoutParams就可以确定出子View的MeasureSpec了从而就可以进一步确定出View测量后的大小了至此MeasureSpec扫盲结束。让我们喝口水继续讲述View的三大流程。等等你刚才说喝什么
三、View的三大流程
此为面试热点面试官一般会从这里引入然后不断对你进行摸底各位要跳槽的看官要注意了View的三大流程是指measure、layout、draw,即测量、布局和绘制。其中measure确定View的测量宽高layout确定View的最终宽高和四个顶点位置而draw则是将View绘制到屏幕上。所以我们逐一进行分析同样我也不深入代码有想深入了解代码的可以自己查阅相关信息或者参考《Android开发艺术探索》一书。
measure过程 measure过程主要分为两类一类是单个View的measure另一类是对于ViewGroup的measure。单个View的measure比较简单直接通过调用自身的measure方法就完成了测量过程。然而对于ViewGroup而言除了会完成自身的measure过程外还会去遍历调用所有子元素的measure方法各个子元素再递归去执行这个流程。 1. View的measure过程
上文有提到View的measure通过调用自身的measure()方法就完成了测量过程,然而measure()方法中又会去调用onMeasure()具体代码如下
//View的measure方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {…// measure ourselves, this should set the measured dimension flag backonMeasure(widthMeasureSpec, heightMeasureSpec);…
}//View的onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}从上面的代码我们可以知道两点一是宽和高分别有自己的MeasureSpec至于什么是MeasureSpec上文有提到过了。二是宽或高的测量大小是通过getDefaultSize()方法得到的至于他是怎么得到的我们继续往下看
public static int getDefaultSize(int size, int measureSpec) { int result size; int specMode MeasureSpec.getMode(measureSpec); int specSize MeasureSpec.getSize(measureSpec); switch (specMode) {case MeasureSpec.UNSPECIFIED: result size; break;case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result specSize; break; } return result;
}对于MeasureSpec.getMode()和MeasureSpec.getSize()方法我详细解释下这是系统提供的一个解包方法也可以理解为解密吧其实也就是二进制的与操作将一个measureSpec通过解包从而得到sepcMode和specSize再往后就是根据specMode返回对应的大小了。
另外getDefaultSize()方法还传入了一个getSuggestedMinimumHeight()或getSuggestedMinimumWidth()参数这看名字应该是一个系统推荐的默认值至于这个默认值怎么来的我就不带着大家分析代码了我直接给出结论有兴趣的看官可以自行了解我这里以getSuggestedMinimumWidth()为例给结论就行因为getSuggestedMinimumHeight()也是一模一样的。 getSuggestedMinimumWidth()结论 如果View没有设置背景那么返回android:minWidth这个属性所指定的值这个值可以为0如果View设置了背景则返回android:minWidth和背景的最小宽度这两者中的最大值. 面试点细节总结
敲黑板了注意了此为魔鬼细节和面试问点各位看官要注意了 面试官直接继承自View的自定义控件需要注意什么 我当然是需要重写onDraw()方法啦 面试官…这简直是一句犀利的废话。 直接继承View的自定义View需要重写onMeasure方法并设置wrap_content时的大小否则在布局中使用wrap_content时就相当于match_parent,导致使用match_parent和使用wrap_content时完全没有区别。 之所以会出现这样的现象是因为View在布局中使用wrap_content时那么它的specMode是AT_MOST模式在这种模式下它的宽、高specSize都是parentSize这一部分前面有讲过可以查阅表格如果懒得往上翻的朋友我会在下面再放一次表格而parentSize代表的是父容器中目前可以使用的大小。所以在这种情况下View的宽高就会等于父容器可使用空间大小我们可以再看表格艾巧了当我们使用match_parent时specSize同样也是parentSize所以呈现的效果完全一致这下大家都明白了吧 parentSpecMode /childLayoutParamsEXACTLYAT_MOSTUNSPECIFIEDdp/pxEXACTLY childSizeEXACTLY childsizeEXACTLY childsizematch_parentEXACTLY parentSizeAT_MOST parentSizeUNSPECIFIED 0wrap_contentAT_MOST parentSizeAT_MOST parentSizeUNSPECIFIED 0
呐还是给大家举个栗子帮助理解。 大家可以看到我在布局中加了两个控件一个是普通的View背景色为黑色这里我们也可以看成自定义View另一个是TextView背景色为白色。这控件的宽高属性全部是wrap_content然而我们自定义的View却撑满了整个屏幕TextView却没有。这是因为TextView中已经重写了onMeasure()方法在方法中对specMode为AT_MOST时做了特殊处理大家感兴趣可以自己查看源码而View中没有处理。所以出现了上述的问题。
所以当大家在写自定义View时记得也加入这样的处理我在下面为大家放上一个解决方案具体值得大小还需要你们自己去灵活定义
//代码来源于《Android开发艺术探索》
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize MeasureSpec.getSize(widthMeasureSpec); int widthMode MeasureSpec.getMode(widthMeasureSpec); int heightSize MeasureSpec.getSize(heightMeasureSpec); int heightMode MeasureSpec.getMode(heightMeasureSpec); if (widthMode MeasureSpec.AT_MOST heightMode MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, mHeight); } else if (widthMode MeasureSpec.AT_MOST) { setMeasuredDimension(mWidth, heightSize); } else if (heightMode MeasureSpec.AT_MOST) { setMeasuredDimension(widthSize, mHeight); }
}2. ViewGroup的measure过程
ViewGroup自身是没有重写onMeasure()方法的而View是有重写的。但是ViewGroup提供了一个measureChild方法其作用就是取出子元素的LayoutParams进一步获得子元素的MeasureSpec接着将MeasureSpec传递给View的measure方法进行测量View的measure测量流程已经在上面做了详细分析了。
大家看到这应该也明白了为什么自定义View不用必须重写onMeasure而自定义ViewGroup必须重写onMeasure方法的原因了 所以ViewGroup的测量流程简单而言可以分为两块内容第一块递归对子View进行measure第二块根据每个子View的测量结果累计加总测量出ViewGroup自身的宽高。第一块内容在上文详细介绍过因此我们主要关注第二块内容接下来以LinearLayout为例子进行介绍我们先来看下LinearLayout的onMeasure方法 Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mOrientation VERTICAL) {measureVertical(widthMeasureSpec, heightMeasureSpec);} else {measureHorizontal(widthMeasureSpec, heightMeasureSpec);}
}上述代码可谓简单明了我们就只以VERTICAL方向上去看一下另一个也大同小异大家可以自行了解由于measureVertical方法比较长我就截取部分源码描述下大概逻辑首先看代码
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {mTotalLength 0;…// See how tall everyone is. Also remember max width.//遍历每个子View的高度并且记录下总高度其中mTotalLength就是总高度for (int i 0; i count; i) {final View child getVirtualChildAt(i);…// Determine how big this child would like to be. If this or// previous children have given a weight, then we allow it to// use all available space (and we will shrink things later// if needed).final int usedHeight totalWeight 0 ? mTotalLength : 0;measureChildBeforeLayout(child, i, widthMeasureSpec, 0,heightMeasureSpec, usedHeight);final int childHeight child.getMeasuredHeight();final int totalLength mTotalLength;mTotalLength Math.max(totalLength, totalLength childHeight lp.topMargin lp.bottomMargin getNextLocationOffset(child));…}//所有子元素遍历结束开始测量自身大小// Add in our padding加顶部和底部的padding统计进总高度mTotalLength mPaddingTop mPaddingBottom;int heightSize mTotalLength;// Check against our minimum heightheightSize Math.max(heightSize, getSuggestedMinimumHeight());// Reconcile our calculated size with the heightMeasureSpec// 根据父容器的大小和自身的MeasureSpec计算出最终高度因为子元素高度总和是不能超过父元素剩余空间的int heightSizeAndState resolveSizeAndState(heightSize, heightMeasureSpec, 0);…maxWidth mPaddingLeft mPaddingRight;// Check against our minimum widthmaxWidth Math.max(maxWidth, getSuggestedMinimumWidth());//传入最终测量出的宽高尺寸从而设置ViewGroup的宽高setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),heightSizeAndState);
}上述代码的主要逻辑就是系统会遍历所有子元素并对他们执行measureChildBeforeLayout方法在这个方法内部会调用子元素的measure方法也就是进入到了第一块内容中上文已经讲解过系统还通过mTotalLength来记录LinearLayout在竖直方向的总高度每测量出一个子元素mTotalLength就会增加增加的部分主要包括子元素的高度以及子元素在竖直方向上的margin、padding等。最终设置ViewGroup的测量宽高至此测量完成
面试点细节总结 自定义ViewGroup继承ViewGroup后必须要重写onMeasure方法测量自身和子View进而重写onLayout这点与自定义View差别较大需要特别注意. 不论是自定义View还是自定义ViewGroup他们在measure过程得到的宽高都不是最终宽高仅仅是测量宽高。最终宽高是在onLayout过程中才真正确定的所以要获取一个控件的宽高最好在onLayout方法中去获取。当然大多数情况下控件的测量宽高和最终宽高是相等的. 由于View的measure过程和Activity的生命周期方法是不同步执行的因此无法保证Activity执行了onCreate、onStart、onResume时某个View已经被测量完毕了如果此时View还没有测量完毕我们获取到的宽高值将会是0要解决这种问题我们可以采用view.post(runnable)方法通过post将一个runnable投递到消息队列尾部等View初始化完成后就会从Looper中调用此runnable从而拿到测量出的宽高值代码示例如下 mView.post(new Runnable() {Overridepublic void run() {int width mView.getMeasuredWidth();int height mView.getMeasuredHeight();}});layout过程
Layout流程是用于确定View或ViewGroup的位置因为layout流程相对于measure而言比较简单我们先看看view的layout大致代码逻辑:
public void layout(int l, int t, int r, int b) {…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);…}…
}在上述代码中大致流程是layout方法首先会通过setFrame方法来设定View的四个顶点位置即初始化mLeft、mTop、mRight、mBottom这四个值View的四个顶点一旦确定View在父容器中的位置也就随之确定了。接下来就会调用onLayout方法这个方法的用途是父容器确定子元素位置的通俗而言就是layout是确定自身的位置onLayout是确定其子View的位置因为单个View没有子元素ViewGroup类布局的不确定性所以他们均对onLayout方法都是空实现即如下所示
/*** Called from layout when this view should* assign a size and position to each of its children.** Derived classes with children should override* this method and call layout on each of* their children.*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}因为LinearLayout继承自ViewGroup所以它必然实现了onLayout方法所以我们继续以它为例看看它是如何实现的代码如下所示
Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {if (mOrientation VERTICAL) {layoutVertical(l, t, r, b);} else {layoutHorizontal(l, t, r, b);}
}啊哈看来和onMeasure()中的实现逻辑类似我们还是以layoutVertical进行讲解同样继续给出主要代码逻辑
void layoutVertical(int left, int top, int right, int bottom) {…final int count getVirtualChildCount();…for (int i 0; i count; i) {final View child getVirtualChildAt(i);if (child null) {childTop measureNullChild(i);} else if (child.getVisibility() ! GONE) {final int childWidth child.getMeasuredWidth();final int childHeight child.getMeasuredHeight();final LinearLayout.LayoutParams lp (LinearLayout.LayoutParams) child.getLayoutParams();…if (hasDividerBeforeChildAt(i)) {childTop mDividerHeight;}childTop lp.topMargin;setChildFrame(child, childLeft, childTop getLocationOffset(child),childWidth, childHeight);childTop childHeight lp.bottomMargin getNextLocationOffset(child);i getChildrenSkipCount(child, i);}}
}所以综上所述layoutVertical方法会遍历所有子元素并调用setChildFrame方法来为子元素指定对应位置其中childTop记录当前末端子元素的高度位置childTop会逐渐增大意味着后面的子元素会被放置在靠下的位置这正是竖直方向上LinearLayout的特性。 再简单说一下setChildFrame方法它仅仅是调用子元素这里我们可以看成是儿子元素的layout方法然后当该元素(儿子元素)确定了自己的位置以后又调用onLayout方法安排其子元素孙子元素的位置。好家伙这简直就是俄罗斯套娃递归。 draw过程
Draw过程就更简单了尤其是对于做了许多自定义View的友友来说。它的作用就是将View绘制到屏幕上面绘制过程大致如下 绘制背景background.draw(canvas).绘制自己 (onDraw).绘制children (dispatchDraw).绘制装饰 (onDrawScrollBars). 关于绘制过程是三大流程中最为简单的了看官可以自行查看源码这里就不再赘述了另外ViewGroup一般不用重写onDraw来绘制自己只需要对子View进行绘制就可以。但明确知道一个ViewGroup需要通过onDraw来绘制内容时我们需要调用setViewNotDraw(false)来进行设置;
好了至此View三大工作流程已经讲解完毕没错接下来当然是划重点啦。
四、自定义View相关总结
自定义View的分类
继承View需重写onDraw方法一般用于实现一些不规则的效果。继承ViewGroup需重写onMeasure、onLayout方法即自己定义一种除了像linearLayout、RelativeLayout等这几种系统布局之外的布局这种情况比较少继承特定的View比如继承ImgView等一般用于拓展某种已有的View的功能。继承特定的ViewGroup 比如继承LinearLayout,一般也是用于拓展功能。但好处是它不需要自己重写onMeasure和onLayout方法并且也比较常用。
自定义View的注意事项
让View支持wrap_content这一点在上面View的measure过程的面试点细节总结里详细介绍过。在自定义View时不要在onDraw()方法中定义变量和执行循环操作不然会导致内存溢出和卡顿掉帧的现象。如果View中有线程或者动画需要及时停止否则会造成内存泄露的情况。View带有滑动嵌套情形时需要处理好滑动冲突。不建议使用自定义的Handler而应该使用view.post(runnable)方法进行替代。如果有必要让你的View支持padding。对于直接继承View的控件如果不在onDraw()方法中处理padding那么padding属性是没有效果的。对于直接继承自ViewGroup的控件需要在onMeasure()和onLayout()中考虑padding和子元素的margin对其造成的影响否则也会导致padding和子元素的margin失效。
更多Android 面试知识点https://qr18.cn/CKV8OZ
- 上一篇: 试描述一下网站建设的基本流程wordpress 试听
- 下一篇: 试玩网站建设医药网站建设方案
相关文章
-
试描述一下网站建设的基本流程wordpress 试听
试描述一下网站建设的基本流程wordpress 试听
- 技术栈
- 2026年03月21日
-
饰品网站建设网站建设方案哪家好 推荐
饰品网站建设网站建设方案哪家好 推荐
- 技术栈
- 2026年03月21日
-
事业单位网站备案物联网设备
事业单位网站备案物联网设备
- 技术栈
- 2026年03月21日
-
试玩网站建设医药网站建设方案
试玩网站建设医药网站建设方案
- 技术栈
- 2026年03月21日
-
视差设计网站无锡高端网站定制
视差设计网站无锡高端网站定制
- 技术栈
- 2026年03月21日
-
视觉传达毕业设计网站优未网络科技秦皇岛有限公司
视觉传达毕业设计网站优未网络科技秦皇岛有限公司
- 技术栈
- 2026年03月21日



