Android 面试题总结之Android 进阶二.docx

上传人:b****5 文档编号:28738319 上传时间:2023-07-19 格式:DOCX 页数:37 大小:1,018.20KB
下载 相关 举报
Android 面试题总结之Android 进阶二.docx_第1页
第1页 / 共37页
Android 面试题总结之Android 进阶二.docx_第2页
第2页 / 共37页
Android 面试题总结之Android 进阶二.docx_第3页
第3页 / 共37页
Android 面试题总结之Android 进阶二.docx_第4页
第4页 / 共37页
Android 面试题总结之Android 进阶二.docx_第5页
第5页 / 共37页
点击查看更多>>
下载资源
资源描述

Android 面试题总结之Android 进阶二.docx

《Android 面试题总结之Android 进阶二.docx》由会员分享,可在线阅读,更多相关《Android 面试题总结之Android 进阶二.docx(37页珍藏版)》请在冰豆网上搜索。

Android 面试题总结之Android 进阶二.docx

Android面试题总结之Android进阶二

Android面试题总结之Android进阶

(二)

掌握

Window是什么?

View的绘制流程

View的事件分发机制

View与SurfaceView,GLSurfaceView

View的绘制流程

在理解View的绘制流程之前我们应该知道这几个类:

View:

最基本的UI组件,表示屏幕上的一个矩形区域。

Window:

是一个抽象基类,作用于外观用户界面和行为策略表示一个窗口,它包含一个Viewtree和窗口的layout参数。

Viewtree的rootView可以通过getDecorView得到。

还可以设置Window的ContentView。

其实现类是PhoneWindow。

Activity,Dialog,Toast,都包含一个Window,该Window在Activity的attach()函数中mWindow=newPhoneWindow(this);创建。

DecorView:

该类是PhoneWindow类的内部类,继承自FrameLayout,它是所有应用窗口的根View,PhoneWindow设置DecorView为应用窗口的根视图。

PhoneWindow:

PhoneWindow对象帮我们创建了一个PhoneWindow内部类DecorView(父类为FrameLayout)窗口顶层视图

ViewRootImpl:

ViewRootImpl是连接WindowManager与DecorView的纽带,View的整个绘制流程的三大步(measure、layout、draw)以及我们一些addView()的操作,都是通过ViewRootImpl完成的。

WindowManager:

应用程序界面和窗口管理器

在ActivityonCreate使用的setContentView()就是设置的ContentView,通过LayoutInflater将xml内容布局解析成View树形结构添加到DecorView顶层视图中id为content的FrameLayout父容器上面。

那么DecorView是如何绘制的呢?

我们分两个步骤来理解:

DecorView添加到Window的过程

DecorView的绘制过程

DecorView添加到Window的过程

我们根据下图步骤来解析DecorView添加到Window的过程,以便让我们更容易的理解。

**Activity初始化:

**Activity启动,关于Activity的创建过程啊或者其他细节,因为不是本篇幅重点故不做详细讨论。

我们尽量简化理解View的绘制流程。

PhoneWindow的创建:

Activity对象创建完成后,初始化了PhoneWindow对象,该Window在Activity的attach()函数中mWindow=newPhoneWindow(this);创建,相关代码块如下:

finalvoidattach(Contextcontext,ActivityThreadaThread..){

..........

mFragments.attachHost(null/*parent*/);

//创建PhoneWindow对象

mWindow=newPhoneWindow(this);

mWindow.setCallback(this);

mWindow.setOnWindowDismissedCallback(this);

mWindow.getLayoutInflater().setPrivateFactory(this);

..........

}

DecorView添加Window:

ActivityThread.Java类会调用handleResumeActivity方法将顶层视图DecorView添加到PhoneWindow窗口,因此通过PhoneWindow的setContentView将Activity与Window进行关联了。

finalvoidhandleResumeActivity(IBindertoken,

booleanclearHide,booleanisForward,booleanreallyResume){

//获得当前Activity的PhoneWindow对象

r.window=r.activity.getWindow();

//获得当前PhoneWindow内部类DecorView对象

Viewdecor=r.window.getDecorView();

//设置DecorView为可见

decor.setVisibility(View.INVISIBLE);

//获取Activity的WindowManager

ViewManagerwm=a.getWindowManager();

WindowManager.LayoutParamsl=r.window.getAttributes();

a.mDecor=decor;

l.type=WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

l.softInputMode|=forwardBit;

if(a.mVisibleFromClient){

//标记已添加至Window

a.mWindowAdded=true;

//添加DecorView到Window

wm.addView(decor,l);

}

接着DecorView通过WindowManager设置到ViewRootImpl中,然后就是下面DecorView的绘制流程了。

因此我们知道在Activity的onCreate和onResume方法中调用View.getWidth()和View.getMeasuredHeight()返回值是0,因为View还没有开始绘制。

View的绘制过程

ViewRootImpl是连接WindowManager与DecorView的纽带,View的整个绘制流程的三大步(measure、layout、draw)都是通过ViewRootImpl完成的,

绘制是从根节点开始,对布局树进行measure和draw。

整个View树的绘图流程在ViewRootImpl.java类的performTraversals()函数展开,该函数所做的工作可简单概括为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw),结合DecorView添加至Window过程,整体大概的流程图如下:

那么我们围绕图上过程来分析View的绘制流程,首先我们进入ViewRootImpl.java中,查看performTraversals函数,这个函数非常长,View的绘制三大流程将在此展开。

“`privatevoidperformTraversals(){

//缓存DecorView,因为在下面用的比较多

finalViewhost=mView;

…..

if(measureAgain){

if(DEBUG_LAYOUT)Log.v(TAG,

“Andheylet’smeasureoncemore:

width=”+width

+”height=”+height);

performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);

}

…..

//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.width和lp.height表示DecorView根布局宽和高

intchildWidthMeasureSpec=getRootMeasureSpec(mWidth,lp.width);

intchildHeightMeasureSpec=getRootMeasureSpec(mHeight,lp.height);

//执行测量操作

performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);

.....

//执行布局操作

performLayout(lp,desiredWindowWidth,desiredWindowHeight);

......

//执行绘制操作

performDraw();

}

“`

主要分下面三大步骤。

measure

measure操作主要用于计算视图的大小

我们知道View的MeasureSpec由父容器的MeasureSpec和其自身的LayoutParams共同确定,而对于DecorView是由它的MeasureSpec由窗口尺寸和其自身的LayoutParams共同确定。

在ViewRootImpl的performTraversals方法中,完成了创建DecorView的MeasureSpec的过程,相应的代码片段如下:

//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.width和lp.height表示DecorView根布局宽和高

intchildWidthMeasureSpec=getRootMeasureSpec(mWidth,lp.width);

intchildHeightMeasureSpec=getRootMeasureSpec(mHeight,lp.height);

我们知道Activity的根视图总是全屏的,因为ViewRootImpl在创建DecorView的MeasureSpec的过程测量模式是EXACTLY,而Size是windowSize,相应的代码片段如下:

privatestaticintgetRootMeasureSpec(intwindowSize,introotDimension){

intmeasureSpec;

switch(rootDimension){

//匹配父容器时,测量模式为MeasureSpec.EXACTLY,测量大小直接为屏幕的大小,也就是充满真个屏幕

caseViewGroup.LayoutParams.MATCH_PARENT:

//Windowcan'tresize.ForcerootviewtobewindowSize.

measureSpec=MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.EXACTLY);

break;

......

}

returnmeasureSpec;

}

View的measure过程

measure在performMeasure开始的,该函数在view中定义为final类型,要求子类不能修改。

measure()函数中又会调用onMeasure()函数,相应的代码片段如下:

publicfinalvoidmeasure(intwidthMeasureSpec,intheightMeasureSpec){

...........

//如果上一次的测量规格和这次不一样,重新测量视图View的大小

if((mPrivateFlags&PFLAG_FORCE_LAYOUT)==PFLAG_FORCE_LAYOUT||

widthMeasureSpec!

=mOldWidthMeasureSpec||

heightMeasureSpec!

=mOldHeightMeasureSpec){

//firstclearsthemeasureddimensionflag

mPrivateFlags&=~PFLAG_MEASURED_DIMENSION_SET;

resolveRtlPropertiesIfNeeded();

intcacheIndex=(mPrivateFlags&PFLAG_FORCE_LAYOUT)==PFLAG_FORCE_LAYOUT?

-1:

mMeasureCache.indexOfKey(key);

if(cacheIndex<0||sIgnoreMeasureCache){

//measureourselves,thisshouldsetthemeasureddimensionflagback

onMeasure(widthMeasureSpec,heightMeasureSpec);

mPrivateFlags3&=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

}else{

longvalue=mMeasureCache.valueAt(cacheIndex);

//Castingalongtointdropsthehigh32bits,nomaskneeded

setMeasuredDimensionRaw((int)(value>>32),(int)value);

mPrivateFlags3|=PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

}...........

}

实际为整个Viewtree计算大小是onMeasure()函数,里面直接调用setMeasuredDimension()提供一个默认模式View计算大小,相应的代码片段如下:

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec));

}

其中默认使用getDefaultSize()获取默认尺寸大小,如果自定义View不重写onMesure(),在布局中使用wrap_content就相当于使用match_parent的效果相应的代码片段如下:

publicstaticintgetDefaultSize(intsize,intmeasureSpec){

intresult=size;

//获得测量模式

intspecMode=MeasureSpec.getMode(measureSpec);

//获得父亲容器留给子视图View的大小

intspecSize=MeasureSpec.getSize(measureSpec);

switch(specMode){

caseMeasureSpec.UNSPECIFIED:

result=size;

break;

caseMeasureSpec.AT_MOST:

caseMeasureSpec.EXACTLY:

result=specSize;

break;

}

returnresult;

}

普通View的measure()函数是由ViewGroup在measureChild方法中调用的,ViewGroup调用其子View的measure时即传入了该子View的widthMeasureSpec和heightMeasureSpec,共同决定了View的大小。

而DecorView是继承自FrameLayout的,所以我们看下面ViewGroup的measure过程。

ViewGroup的measure过程

ViewGroup需要先完成子View的measure过程,才能完成自身的measure过程,在ViewGroup的onMeasure()函数中,不同的布局(LinearLayout、RelativeLayout、FrameLayout等等)有不同的实现。

FrameLayout的onMeasure()方法代码如下:

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

//获取子View的个数

intcount=getChildCount();

finalbooleanmeasureMatchParentChildren=

MeasureSpec.getMode(widthMeasureSpec)!

=MeasureSpec.EXACTLY||

MeasureSpec.getMode(heightMeasureSpec)!

=MeasureSpec.EXACTLY;

mMatchParentChildren.clear();

intmaxHeight=0;

intmaxWidth=0;

intchildState=0;

for(inti=0;i

finalViewchild=getChildAt(i);

if(mMeasureAllChildren||child.getVisibility()!

=GONE){

//测量FrameLayout下每个子视图View的宽和高

measureChildWithMargins(child,widthMeasureSpec,0,heightMeasureSpec,0);

finalLayoutParamslp=(LayoutParams)child.getLayoutParams();

maxWidth=Math.max(maxWidth,

child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin);

maxHeight=Math.max(maxHeight,

child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin);

childState=combineMeasuredStates(childState,child.getMeasuredState());

if(measureMatchParentChildren){

if(lp.width==LayoutParams.MATCH_PARENT||

lp.height==LayoutParams.MATCH_PARENT){

mMatchParentChildren.add(child);

}

}

}

}

.............

}

至此View的measure过程大致清楚了,下面是View的layout过程。

layout

layout在view中定义为final类型,要求子类不能修改,用于设置子View的位置,因而是由父容器获取子View的位置参数后,调用child.layout方法并传入已获取的位置参数,从而完成对子View的layout。

相应的代码片段如下:

publicvoidlayout(intl,intt,intr,intb){

//判断是否需要重新测量

if((mPrivateFlags3&PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT)!

=0){

onMeasure(mOldWidthMeasureSpec,mOldHeightMeasureSpec);

mPrivateFlags3&=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

}

intoldL=mLeft;

intoldT=mTop;

intoldB=mBottom;

intoldR=mRight;

booleanchanged=isLayoutModeOptical(mParent)?

setOpticalFrame(l,t,r,b):

setFrame(l,t,r,b);

//判断布局是否发生改变,重新布局

if(changed||(mPrivateFlags&PFLAG_LAYOUT_REQUIRED)==PFLAG_LAYOUT_REQUIRED){

//回调onLayout的方法,该方法由ViewGroup实现

onLayout(changed,l,t,r,b);

mPrivateFlags&=~PFLAG_LAYOUT_REQUIRED;

ListenerInfoli=mListenerInfo;

if(li!

=null&&li.mOnLayoutChangeListeners!

=null){

ArrayListlistenersCopy=

(ArrayList)li.mOnLayoutChangeListeners.clone();

intnumListeners=listenersCopy.size();

for(inti=0;i

listenersCopy.get(i).onLayoutChange(this,l,t,r,b,oldL,oldT,oldR,oldB);

}

}

}

mPrivateFlags&=~PFLAG_FORCE_LAYOUT;

mPrivateFlags3|=PFLAG3_IS_LAID_OUT;

}

通过上面代码我们知道layout主要完成两个操作:

setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来,onLayout()是空方法由ViewGroup实现,在ViewGroup中,onLayout是一个抽象方法,因为对于不同的布局管理器类,对子元素的布局方式是不同的。

而DecorView是继承自FrameLayout的,所以我们看下面DecorView的onLayout代码片段:

@Override

protectedvoidonLayout(booleanchanged,intleft,inttop,intright,intbottom){

layoutChildren(left,top,right,bottom,false/*noforceleftgravity*/);

}

voidlayoutChildren(intleft,inttop,intright,intbottom,

boolean

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 初中教育 > 数学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1