view是怎么被展示在手机上的?

  

  我们先了解下window、windowManager等相关知识:

  在activity的attach方法里(我不管谁调用了,只知道这个是开始就会做的事情),我们可以看到activity有一个window(PhoneWindow)成员变量,在这里初始化的。然后接着又给这个window设置了WindowManager(其实就是contextImpl中static块中初始化好的windowManger,系统级的,只有一个),这个windowManage又是什么呢,他是个接口,继承了ViewManager接口(它定义了addView,updateVIewlayout,removeView三个方法)。他的实现类其实就是一个WindowManagerImpl,就是说给windowManager塞了一个WindowManagerImpl的实例,这个WindowManagerImpl里有个单例成员,是windowManagerGlobal,它里面有一些list,存放了view,viewRootImpl,windowmanager.layoutparams,它保存了当前应用程序添加的所有的View对象,已经相对应的ViewRootImpl对象和添加时使用的WindowManager.LayoutParams属性对象。

  

1.1  PhoneWindow

PhoneWindow是Android中的最基本的窗口系统,每个Activity 均会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。

1.2  DecorView

DecorView是当前Activity所有View的祖先,它并不会向用户呈现任何东西,它主要有如下几个功能,可能不全:

A.  Dispatch ViewRoot分发来的key、touch、trackball等外部事件;

B.  DecorView有一个直接的子View,我们称之为System Layout,这个View是从系统的Layout.xml中解析出的,它包含当前UI的风格,如是否带title、是否带process bar等。可以称这些属性为Window decorations。

C.  作为PhoneWindow与ViewRoot之间的桥梁,ViewRoot通过DecorView设置窗口属性。

1.3  System Layout

目前android根据用户需求预设了几种UI 风格,通过PhoneWindow通过解析预置的layout.xml来获得包含有不同Window decorations的layout,我们称之为System Layout,我们将这个System Layout添加到DecorView中,目前android提供了8种System Layout,如下图。

预设风格可以通过PhoneWindow方法requestFeature()来设置,需要注意的是这个方法需要在setContentView()方法调用之前调用。

1.4  Content Parent

Content Parent这个ViewGroup对象才是真真正正的ContentView的parent,我们的ContentView终于找到了寄主,它其实对应的是System Layout中的id为”content”的一个FrameLayout。这个FrameLayout对象包括的才是我们的Activity的layout(每个System Layout都会有这么一个id为”content”的一个FrameLayout)。

  我们先看看activity的onCreate里的setContentView方法,在这个方法里:

  public void setContentView(View view) {      getWindow().setContentView(view); //这里先获得phoneWindow      initWindowDecorActionBar();  }

 在phoneWindow的setContentView里: 好。我们开始看setContentView里面一个重要的部分就是installDecor(); 这个类创建了DecorView,这是一个什么东西呢?其实自身就是一个frameLayout,就是如果我们看activity层级结构的时候

可以看到最上层是个decorView,然后是一个linearlayout(顺带说一下:
会根据feattures得到窗口修饰文件布局,这个其实就是图上的linearLayout,并add到decorView中
还记得我们平时写应用Activity时设置的theme或者feature吗(全屏啥的,NoTitle等)?我们一般是不是通过XML的android:theme属性或者java的requestFeature()方法来设置的呢?譬如:
通过java文件设置:
requestWindowFeature(Window.FEATURE_NO_TITLE);
通过xml文件设置:
android:theme="@android:style/Theme.NoTitleBar"

对的,其实我们平时requestWindowFeature()设置的值就是在这里通过getLocalFeature()获取的;而android:theme属性也是通过这里的getWindowStyle()获取的。

),Linearlayout下面有一个id为content的frameLayout,和一个action_mode_bar_stub的ViewStub(性能优化之ViewStub,通过inflate后展示)。

  接着再看,setContentView里执行了LayoutInflater的inflate方法,参数是我们传入的布局id,和上面获得的id为content的framelayout(所以,merge其中的一个用法就找到了所用之处,如果我们的根布局是个framelayout或者只有一个元素,就一个使用这个标签减少层级,至于我们为什么要减少层级,继续往下看),如此我们就知道上面的布局图为什么是那样了。//从此开始了将xml展示到屏幕中的长征

    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) //这个方法有三个参数  //看第一个我们关心的重要的地方:这里先判断了如果我们的xml布局的根标签是<merge>的,那么就直接执行rInflate()否者按照另外一种方式处理,这种处理方式最终就是放到了rootView中,使用的是addView方法,所以我们在listView的getView中不能使用这样的方式
  if (TAG_MERGE.equals(name)) {      if (root == null || !attachToRoot) {          throw new InflateException("<merge /> can be used only with a valid "                  + "ViewGroup root and attachToRoot=true");      }

      rInflate(parser, root, attrs, false, false);    } else {
  // Temp is the root view that was found in the xml  final View temp = createViewFromTag(root, name, attrs, false);
  ......
  // Create layout params that match root, if supplied  params = root.generateLayoutParams(attrs);  if (!attachToRoot) {// 如果root不是null并且这个方法的第三个参数是false,就会把root的layoutParams赋给temp      // Set the layout params for temp if we are not      // attaching. (If we are, we use addView, below)      temp.setLayoutParams(params);  }  ......
  rInflate(parser, temp, attrs, true, true);//这个方法是做递归解析xml while()循环,添加到这个temp中去,利用addView。这里就是为什么我们要减少层级的一个原因,层级越多,就越耗时间循环啊!!!!!!教导了我们,尽可能使用相对布局  if (DEBUG) {      System.out.println("-----> done inflating children");  }    // We are supposed to attach all the views we found (int temp)  // to root. Do that now.  if (root != null && attachToRoot) {//如果第三个参数是true.则会把temp给add到root里去      root.addView(temp, params);  }  // Decide whether to return the root that was passed in or the  // top view found in xml.  if (root == null || !attachToRoot) {//如果没有null或者第三个参数是false,则返回temp(这里的如果root不是Null,则temp已经有layoutParams了)      result = temp;  }
  从上面这段就能看出来,当我们使用listView的时候,在getView中 inflate(R.layout.xxxxx,null)的时候,直接返回了layout的根view,它的layoutParam为null,所以它的跟布局中的宽高会没有效果  所以总结下,就是如果调用的inflate(R.layout.xxxxx,null),只是返回了xml的根布局view,inflate(R.layout_xxxxxxx,rootview),这个在listView中会报错,rootView是listView,然而listView不支持addView,  在其他地方就是往rootView中addview(view)了,会直接展示出来。  inflate(R.layout_xxxxx,rootView,false),返回xml布局根布局view,并且拥有layoutPrarm,根宽高参数有效。

  setContentView最后一个回调
if (cb != null && !isDestroyed()) {    cb.onContentChanged();} 到这里setContentView结束了,view都准备好啦~那么我们这个时候显示到了屏幕上么?貌似木有吧,那又是怎么显示的?  这里我们又需要学习下activityThread了,在这个类里,会执行一段handleResumeActivity方法,在这个方法里,有一句r.activity.makeVisible();
void makeVisible() {    if (!mWindowAdded) {        ViewManager wm = getWindowManager();//这里的vm到底是什么,viewManager是个接口,实际上我们获得的这个vm就是之前在attach里面得到的一个WindowManagerImpl实例        wm.addView(mDecor, getWindow().getAttributes());//再看看WindowManagerImpl的addView方法其实就是保存了view,viewRootImpl,params,最终还有句话!!!!root.setView(view, wparams, panelParentView);这里还是有点疑问,是不是到底是不是因为HandleResumeActivity方法导致最终显示的?还有个说法是在phoneWindow中setContentView最后的mContentParent.addView(view, params);里调用的ViewGroup的addView里面有一句invalidate。然后开始执行的runnable方法开始了三部曲。
        mWindowAdded = true;    }    mDecor.setVisibility(View.VISIBLE);}
    以上代码我们可以看到把mDecorView给set到ViewRootImpl中,这个setView方法中有句话:view.assignParent(this);他把DecorView的parent给置成了这个ViewRootImpl;  最终mDecor被设置为Visible;

    说接下来的内容前,我们普及点知识:MeasureSpec,这里面全是static方法,他主要用来操作转换size、mode、测量结果,这里我们可以知道他主要是通过一个32位的二进制来计算的,最高2位表示的mode  mode有三大类,UNSPECIFEID(不限制大小,view的宽高设置0或者没有设置的时候),EXACITLY(精确的,view设置的match_parent),AT_MOST(根据自己尺寸有多大就是多大,view设置的wrap_parent),剩下的30位用来保存size  利用二进制的按位与和按位或来判断与取值
   

接着看,在之前我们已经看到了在ViewRootImpl中调用setView,然后在这个方法里,调用了requestLayout方法,然后执行了一个runnable,最终我们能看到一句话
// Ask host how big it wants to beperformMeasure(childWidthMeasureSpec, childHeightMeasureSpec);   开始计算宽高啦,这个方法里面调用了mView.measure方法(这个方法是final的),这个mView其实就是我们之前setView传过来的根布局decorView!,在measure里面会调用onMeasure方法,我们都是通过重写这个方法重新设置宽高的这里必须要注意了,decorView调用measure方法,然后在这个方法内部调用了onMeasure方法,这个方法不是view的方法,而是decorView的父类frameLayout的onMeasure方法,记住这个就好办了,不然都不能理解怎么一层一层计算子view的大小的呢!!!!!  view的onMeasure方法里只调用一个方法
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  继续往里面看,一个view的最小宽高其实是由他的backgroud和他的minSize属性来决定的。然后当父类要求的测量模式是UNSPECIFIED的时候,那么就用这个view的大小,否则都使用的是父类规格的大小,一般来说这个onMeasure方法由系统实现好了  我们直接调用就可以了。

   我们看一下ScrollView的onMeasure方法
  if (getChildCount() > 0) {      final View child = getChildAt(0);//只看第一个child,为什么只看第一个呢?因为scrollView里面只能有一层.....    ......  }    再看看ListView,如果我们在ListView外面套一层ScrollView,那么将会只能显示一行数据.....这是为什么呢?一句话,看看ListView源码吧
if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||        heightMode == MeasureSpec.UNSPECIFIED)) {    final View child = obtainView(0, mIsScrap);

    measureScrapChild(child, 0, widthMeasureSpec);

    childWidth = child.getMeasuredWidth();    childHeight = child.getMeasuredHeight();    childState = combineMeasuredStates(childState, child.getMeasuredState());

    if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(            ((LayoutParams) child.getLayoutParams()).viewType)) {        mRecycler.addScrapView(child, 0);    }}

if (widthMode == MeasureSpec.UNSPECIFIED) {    widthSize = mListPadding.left + mListPadding.right + childWidth +            getVerticalScrollbarWidth();} else {    widthSize |= (childState&MEASURED_STATE_MASK);}

if (heightMode == MeasureSpec.UNSPECIFIED) {    heightSize = mListPadding.top + mListPadding.bottom + childHeight +            getVerticalFadingEdgeLength() * 2;}

if (heightMode == MeasureSpec.AT_MOST) {    // TODO: after first layout we should maybe start at the first visible position, not 0    heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);}

setMeasuredDimension(widthSize , heightSize);可以看到如果父类指定的是UNSPECIFIED,那么就会先计算listView第一个view的高度,并在之后赋给listView自己作为高度。 ListView、ScrollView都不限制子视图的高度,可以超过它本身。所以我们曾经使用一个方法,重写了ListView的onMeasure方法
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, //后30位是大小,现在就是00111111............            MeasureSpec.AT_MOST);    super.onMeasure(widthMeasureSpec, mExpandSpec);}这句话的意思就是,我自己指定用AT_MOST来替换掉了ScrollView的UNSPECIFIED,并且高度是最大。那么再看看源码,这下它会用
 heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);来计算新的高度,
endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;final AbsListView.RecycleBin recycleBin = mRecycler;final boolean recyle = recycleOnMeasure();final boolean[] isScrap = mIsScrap;

for (i = startPosition; i <= endPosition; ++i) {    child = obtainView(i, isScrap);

    measureScrapChild(child, i, widthMeasureSpec);.....
// Recycle the view before we possibly return from the methodif (recyle && recycleBin.shouldRecycleViewType(//这里计算高度的时候也会使用到回收栈        ((LayoutParams) child.getLayoutParams()).viewType)) {    recycleBin.addScrapView(child, -1);}
接着会遍历所有的child,计算最终高度。所以我们就可以展示了,但是却丢失了listView利用回收栈的特性。(因为我们给他传的高度是Integer.MAX_VALUE >> 2,最大,所以当在上面计算高度的时候发现够放下所有的item,可以通过断点调试看到这种情况下.mRecycler下面的mActiveView是ListView的总个数)到这里,我们知道真正计算view高度的是在onMeasure中,那么这个方法的2个测量规格参数是怎么来的呢,是他们的父类ViewGroup的getChildMeasureSpec这个方法(以LinearLayout为例,入口onMeasure->measureVertical->measureChildBeforeLayout->measureChildWithMargins->getChildMeasureSpec).在这个方法里面我们可以总结出一个规律
parent child childMeasure
EXACTLY EXACTLY EXACTLY
EXACTLY MATCH_PARENT EXACTLY
EXACTLY WRAP_CONTENT AT_MOST
AT_MOST EXACTLY EXACTLY
AT_MOST MATCH_PARENT AT_MOST
AT_MOST WRAP_CONTENT AT_MOST
UNSPECIFIED EXACTLY EXACTLY
UNSPECIFIED MATCH_PARENT UNSPECIFIED
UNSPECIFIED WRAP_CONTENT UNSPECIFIED
为什么要说上面这个东西呢,我们再来看看ListView的onMeasure方法里面,会发现,当UNSPECIFIED显示会有问题,当AT_MOST的时候,会自己循环计算child可以放多少个,算出高度。只有当EXACTLY的时候,这些统统跳过,直接高度计算完毕。我记得我以前看过,listView的高度能设置match就不要设置wrap,不然adapter里面getView会执行很多次,一直不知道为什么,现在知道了吧!!!!!!所以我们在使用listView的时候,第一不要嵌套在scrollView或者listview内部,第二尽量高度设置具体值或者是match,提高计算高度效率。
performMeasure之后看看performLayout   可以看到host.layout();这个host就是mView,也就是之前setView的时候传递过来的decorView;我们会发现frameLayout中没有重写layout方法,所以直接看viewGroup,发现是final的,并且内部直接调用的spuer.layout。就是View的layout方法。再来看看这个方法
boolean changed = isLayoutModeOptical(mParent) ?        setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);其中有这么句,会发现怎么样都会执行setFrame方法,这个方法为view设置了mLeft、mTop等属性,并且会调用invalidate(这个我们之后再继续看)。看来在这个方法里面就已经把view的layout给框好了再继续看layout()方法,之后还会调用onLayout,这个方法在View里面是空的,然后ViewGroup是个抽象的。子类必须重写,看看frameLayout怎么重写的,就一句话。。。layoutChildren();跟进去,很重要的。for循环了。依次执行child.layout()。看到这里,我觉得,我们重写onLayout,其实不是框当前这个view的,而是为了循环遍历子view去layout的。在onLayout之前,就已经计算好了位置。我们之前的measure方法测量的大小就是为layout服务的。view的getMeasureWidth这样的方法获得是onMeasure之后测量的大小。而view的getWidth是通过mLeft这样的属性计算出来的,也就是layout之后得到的大小,这2个方法得到的数据可能会不一样。
performDraw     开始要画了直接可以看到调用了draw,然后我们会在最后发现出现了分支
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {  ........//硬件加速开启的时候}else{  .....  
  drawSoftware() //软件处理
}
  看到这里,就又要引入新的知识了。硬件加速(GPU).  基于软件的绘图模式

    基于软件的绘图模式在重绘View时,需要如下两个过程Invalidate the hierarchyDraw the hierarchy

   需要进行重绘时,系统发出invalidate()信号,并在View视图树中进行传递,计算需要重新绘制的区域,但是这种绘图方式有两个不足:

   当我们只需要重绘视图树中的一个View时,视图树中的View都将进行重绘,而且遍历视图树也浪费大量时间。例如一个ViewA在另一个ViewB之上,即使B没有发生变化,重绘A的时候,B也会重绘。

    这种方式隐藏了绘制中的bug,例如上面的例子中,由于ViewA、ViewB相互重叠,有需要重绘的the dirty region,那么如果B忘记了进行重绘的逻辑,那么A进行重绘的时候,就会将B重绘,也就是说使用错误的行为来得到了正确的现象。正是因为这个原因,开发者需要保证在View需要发生重绘时,调用正确的invalidate()方法。

   基于硬件的绘图模式

  基于硬件的绘图方式同样使用invalidate()信号来进行重绘,但其绘制和渲染的方式不同。Android内部维护一个display list用于记录视图树的显示状态。当收到invalidate()信号时,系统只需要更改需要重绘的视图的display list,而其他未发生改变的视图只需要使用原来的display list即可,整个过程分为三  部分:Invalidate the hierarchyRecord and update display listsDraw the display lists  使用这种方式,就可以避免软件绘图中第二点的bug。

    例如,假设有一个包含了一个Button对象的ListView对象的LinearLayout布局,那么LinearLayout布局的显示列表如下:

    1. DrawDisplayList(ListView);

    2.DrawDisplayList(Button)

    假设现在要改变ListView对象的不透明度,那么在调用ListView对象的setAlpha(0.5f)方法时,显示列表就包含了以下处理:

    1.SaveLayerAlpha(0.5);

    2.DrawDisplayList(ListView);

    3. Restore;

    4.DrawDisplayList(Button)

  View Layers  LAYER_TYPE_NONE:View对象用普通的方式来呈现,并且不是由屏幕外缓存来返回的。这种类型是默认的行为;LAYER_TYPE_HARDWARE:如果应用程序是硬件加速的,那么该View对象被呈现在硬件的一个硬件纹理中。如果没有被硬件加速,那么这种层类型的行为与LAYER_TYPE_SOFTWARE相同。LAYER_TYPE_SOFTWARE:  View对象会被呈现在软件的一个位图中。使用哪种层的类型,依赖以下目标:  性能:使用硬件层类型,把View呈现到一个硬件纹理中。一旦该View对象被呈现到一个层中,那么它的绘图代码直到调用该View对象的invalidate()方法时才会被执行。对于某些动画,如alpha动画,就能够直接使用该层,这么做对于GPU来说是非常高效的。 视觉效果:使用硬件或软件层类型和一个Paint对象,能够把一  些特殊的视觉处理应用给一个View对象。例如,使用ColorMatrixColorFilter对象绘制一个黑白相间的View对象。 兼容性:使用软件层类型会强制把一个View对象呈现在软件中。如果View对象被硬件加速(例如,如果整个应用程序都被硬件加速)发生呈现问题,那么使用软件层类型来解决硬件呈现管道的限制是一个容  易的方法。
  硬件加速具体使用例子 :动画,我们应用的引导页,动画复杂,多个动画组成,非常的卡,这里就可以利用这个。
  View.setLayerType(View.LAYER_TYPE_HARDWARE, null);   ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);   animator.addListener(new AnimatorListenerAdapter() {   @Override   public void onAnimationEnd(Animator animation) {   view.setLayerType(View.LAYER_TYPE_NONE, null);   }   });   animator.start();  实测效果很好,但是有一点要注意,动画结束的时候一定要取消硬件加速,因为硬件加速是非常耗内存的。如果不取消就会导致oom(实测确实出现了)。  提示和技巧  切换到2D图形硬加速能立即提升性能,但是你仍然需要按照以下建议来有效使用GPU:• 减少你应用中views的数量  系统画的views越多,就越慢.这对软件呈现管线来说也是一样.减少views是优化你的UI的最简单的办法.• 避免过度绘制  不要在彼此的上面画太多layers.移除那些完全被别的不透明view遮盖的view们.如果你需要把多个layer混合画到每个view的上面,应考虑把它们合并到一个layer中.对于当前硬件的一个好原则是画的次数不要超过每帧屏幕上像素的2.5倍(透明像素按位图计数).• 不要在绘制方法们中创建render对象  一个常见的错误就是在每次调用绘制方法时都创建新的Paint或Path.这强制垃圾收集器运行得更频繁,并且导致高速缓存和硬件管道优化不起作用.• 不要太频繁地修改shapes  比如混合shapes,paths,和circles时,是使用纹理遮罩呈现的.每次你创建或修改一个path,硬件管道都创建一个新的遮罩,这个代价是很昂贵的.• 不要太频繁地修改bitmap  你每次改变一个bitmap的内容,你下次去画它时它会作为一个GPU纹理重新上载.• 小心使用alpha(透明度)  当你用setAlpha(),AlphaAnimation,或ObjectAnimator把一个view设为透明,它将被呈现到一个离屏缓冲中,此时就需要双倍的填充速率.当在一个very大的view上应用透明度时,应考虑把view的layer类型设置为LAYER_TYPE_HARDWARE.

  好了现在我们继续看,我们只关心drawSoftware这个方法,可以在这个方法里看到  ......   final Rect dirty = mDirty;   ......   canvas = mSurface.lockCanvas(dirty); //基于Surface,以后再学习........  ...... mView.draw(canvas); ......//基于canvas,这个以后再学习,又可以画各种各样的东西  好了,这个mView,不用多说了,就是DecorView,一路追下去,看到FrameLayout的draw,发现它会画一个mForeground,如果有的话,这个就是覆盖在所有view之上的一个drawable..(发现新玩意.......也许以后可以用到,毛玻璃效果?),最后就到了View的draw,好吧。。。  在这里可以看到6个步骤!!!!  
  /*   * Draw traversal performs several drawing steps which must be executed   * in the appropriate order:   *   *      1. Draw the background   *      2. If necessary, save the canvas' layers to prepare for fading   *      3. Draw view's content   *      4. Draw children   *      5. If necessary, draw the fading edges and restore layers   *      6. Draw decorations (scrollbars for instance)   */
  根据源码注释,2&5不是必须的,2&5是如果scroll 渐变效果开着的,就会去画,跳过不看。看1,3,4,6  第一步  drawBackground(canvas);进去看看,发现如果mBackground == null 就return了,后面繁琐的事情不做了,可以想象出为什么我们画布局的时候,减少有交集的background,过渡渲染层会变少。看到这里大家应该清楚了,这个过渡渲染其实跟xml层级木有关系,跟这个background有关系,只要有覆盖,下层视图看不见,但是还是画了,就是过渡渲染。
  第三步  onDraw,我们会发现view这个方法是空方法,因为每个view长得不一样,所以需要自己去画,ViewGroup也没有重写这个方法。  第四步  dispatchDraw,viewgroup会重写这个方法,会循环编译childView,调用view.draw方法  第六步  onDrawScrollBars,画滚动条,其实每个view都有滚动条,只是画没画的问题
  其实还有个第七步,不过这个只在4.3之上才有的,就是viewoverlay,这是一个在所有页面之上的一个View,没有点击相应事件,可以做一些动画
  到这里三部曲结束。  但是我们还要来看看之前留的一个坑-invalidate和postInvalidate  其实postInvalidate就是通过ViewRootImpl发送了一个消息给ui线程handler,又调用了view.invalidate    看看view的invalidate方法  
  // Propagate the damage rectangle to the parent view.  final AttachInfo ai = mAttachInfo;  final ViewParent p = mParent;  if (p != null && ai != null && l < r && t < b) {      final Rect damage = ai.mTmpInvalRect;      damage.set(l, t, r, b);      p.invalidateChild(this, damage);  }  在这里能看出把刷新区域(自己view有关:0, 0, mRight - mLeft, mBottom - mTop,就是layout之后的4个点的值)放在Rect里面然后让父类调用invalidateChild继续处理,再随便看一个ViewGroup
  
do {    View view = null;    if (parent instanceof View) {        view = (View) parent;    }

    if (drawAnimation) {        if (view != null) {            view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;        } else if (parent instanceof ViewRootImpl) {            ((ViewRootImpl) parent).mIsAnimating = true;        }    }

    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque    // flag coming from the child that initiated the invalidate    if (view != null) {        if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&                view.getSolidColor() == 0) {            opaqueFlag = PFLAG_DIRTY;        }        if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {            view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;        }    }

    parent = parent.invalidateChildInParent(location, dirty);    if (view != null) {        // Account for transform on current parent        Matrix m = view.getMatrix();        if (!m.isIdentity()) {            RectF boundingRect = attachInfo.mTmpTransformRect;            boundingRect.set(dirty);            m.mapRect(boundingRect);            dirty.set((int) (boundingRect.left - 0.5f),                    (int) (boundingRect.top - 0.5f),                    (int) (boundingRect.right + 0.5f),                    (int) (boundingRect.bottom + 0.5f));        }    }} while (parent != null);
  ViewGroup中做了循环,只要当前View有mParent,就一直循环下去,直到ViewRootImpl为止。  在这里循环过程中,parent = parent.invalidateChildInParent(location, dirty);方法中,有相关dirty.union/dirty.set方法,意思就是一层层传递当前view需要刷新的区域坐合并。最终到ViewRootImpl中看  if (!mWillDrawSoon && (intersected || mIsAnimating)) {
      scheduleTraversals();  }  会发现调用了scheduleTraversals,到这里,就发现又回到刚刚开始说的入口了,viewrootimpl的setView中的requestLayout方法里面也会调用scheduleTraversals。这样就又开始了三部曲。

  常见的引起invalidate方法操作的原因主要有:

  •   直接调用invalidate方法.请求重新draw,但只会绘制调用者本身。
  •   触发setSelection方法。请求重新draw,但只会绘制调用者本身。
  •   触发setVisibility方法。 当View可视状态在INVISIBLE转换VISIBLE时会间接调用invalidate方法,继而绘制该View。当View的可视状态在INVISIBLE\VISIBLE 转换为GONE状态时会间接调用requestLayout和invalidate方法,同时由于View树大小发生了变化,所以会请求measure过程以及draw过程,同样只绘制需要“重新绘制”的视图。
  •   触发setEnabled方法。请求重新draw,但不会重新绘制任何View包括该调用者本身。
  •   触发requestFocus方法。请求View树的draw过程,只绘制“需要重绘”的View。
  
   

转载于:https://www.cnblogs.com/liming-saki/p/5075446.html

view是怎么被展示在手机上的?相关推荐

  1. vconsole 调试 查看LOG VUE在手机上调试 手机查看h5的日志

    简单介绍下vConsole: vConsole是一个由微信公众平台前端团队研发的web前端开发者面板,可用于展示console日志,方便开发.调试. 使用场景1,在vue-cli 构建的项目中使用: ...

  2. python 手机编程termux_如何优雅的在手机上进行Python编程?

    其实谈不上优雅,编程还是老老实实在电脑编程更实用,手机只是某种程度上做一些便捷性的操作. 回到主题,下面介绍两个常用的Python编程工具. 第一款:Termux Termux是一个安卓手机的 Lin ...

  3. 想不想在手机上敲 Python 代码?!这里有一款推荐方案

    超神利器- 相信多数安卓用户都使用过Qpython这款移动端的Python编辑器吧?之前我也研究过一阵子这个工具,但因为一次简单的爬虫让我对它失望之极.Qpython不支持lxml这个模块,然而pyt ...

  4. opencv在手机上实现照片背景虚化

    概述 使用androidstudio开发,在手机上实现照片背景虚化 详细 代码下载:http://www.demodashi.com/demo/10599.html 一.准备工作 1.需要下载安装an ...

  5. 手机上有没有学python的软件-有哪些可以在手机上敲Python代码的App

    相信多数安卓用户都使用过Qpython这款移动端的Python编辑器吧?之前我也研究过一阵子这个工具,但因为一次简单的爬虫让我对它失望之极.Qpython不支持lxml这个模块,然而python中lx ...

  6. 推荐几款可以直接在手机上编程的app(包含Java、C、Python等)

    这里介绍几款可以在手机上编程的app,分别是: 1.java和Android:AIDE集成开发环境. 2.C语言:c语言编译器.C4droid. 3.python:QPython3.Termux. 4 ...

  7. 如何在手机上完成日语翻译中文

    如何在手机上完成日语翻译中文?作为从小看<海贼王>.<名侦探柯南>.<火影忍者>等一系列日漫长大的我们,自然有一种想要学习日语的冲动.但是学习一种语言不是一朝一夕就 ...

  8. 目前html邮件都支持手机屏幕,Html电子邮件不能在手机上阅读媒体查询

    我是开发HTML电子邮件的新手,我遇到媒体查询问题.Html电子邮件不能在手机上阅读媒体查询 这就是发生了什么事情:当我完成我的电子邮件的开发并调整屏幕大小以检查它是否响应时,它按计划工作并适应任何屏 ...

  9. unity vr手柄移动_移动内外VR跟踪,现在可通过Unity在手机上轻松使用

    unity vr手柄移动 VR is all about immersion, and the ability to track the user's position in space is a k ...

最新文章

  1. Python学习系列(五)(文件操作及其字典)
  2. Linux中的文件系统和磁盘管理
  3. 第6篇:Flowable快速工作流脚手架Jsite_请假实战_部门经理审批
  4. Oracle相关练习
  5. STM32 存在字节对齐指针变量的地址都必须是4的倍数
  6. no number java_java.lang.NumberFormatException问题!!!
  7. 错误: -source 1.6 中不支持 diamond 运算符
  8. Dev C++支持c++11标准的编译方法
  9. MySQL全方位练习(学生表 教师表 课程表 分数表)
  10. 扫雷php,扫雷php
  11. ps彩色照片变黑白照片
  12. Limited-Memory Quasi-Newton Methods
  13. Five-degree-of-freedom manipulation of an untethered magnetic device in fluid using a single permane
  14. 【个人思考系列】在美团点评当工程师的第一年总结
  15. 四川SFSCB-A15/4PY智能浪涌后备保护器 遥信功能 上传防雷系统平台
  16. 使用wiki百科的中文语料库训练词向量
  17. CentOS7-查看IP
  18. 20191219 门面设计模式
  19. [hust]校内生存网站集合
  20. ISP基本框架及算法介绍

热门文章

  1. Docker学习笔记【一】
  2. Linux之samba服务器的搭建及详解
  3. nagios使用check_mysql监控mysql
  4. 为什么放弃治疗_百度百科
  5. 设计模式18---设计模式之策略模式(Strategy)(行为型)
  6. 无线传输层安全协议WTLS安全机制详解
  7. vscode安装设置go
  8. 《认知突围》做复杂时代的明白人,读书分享
  9. 使用salt-api来搭建salt自动化平台
  10. 【模板】割点(割顶)