国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Android - Android進(jìn)階之深入理解View的布局(Layout)流程原理

Android進(jìn)階之深入理解View的布局(Layout)流程原理

2021-09-18 01:06Android開發(fā)編程 Android

View的layout方法作用是確定View的位置,ViewGroup的layout方法不僅要確定自身的位置,還有確定子View的位置。

Android進(jìn)階之深入理解View的布局(Layout)流程原理

前言

前一篇我們講解了View的Measure過程,那今天我們來講解下Layout;

View的layout方法作用是確定View的位置,ViewGroup的layout方法不僅要確定自身的位置,還有確定子View的位置;

Android進(jìn)階之深入理解View的測量(Measure)流程機(jī)制

一、Layout流程源碼詳解

Android進(jìn)階之深入理解View的布局(Layout)流程原理

1、performLayout

View三大工作流程是從ViewRootImpl#performTraversals開始的,其中performMeasure、performLayout、performDraw方法分別對應(yīng)了View的測量、布局、繪制;

從performLayout開始分析View布局流程;

  1. privatevoidperformLayout(WindowManager.LayoutParamslp,intdesiredWindowWidth,
  2. intdesiredWindowHeight){
  3. mLayoutRequested=false;
  4. mScrollMayChange=true;
  5. mInLayout=true;
  6. finalViewhost=mView;
  7. Trace.traceBegin(Trace.TRACE_TAG_VIEW,"layout");
  8. try{
  9. host.layout(0,0,host.getMeasuredWidth(),host.getMeasuredHeight());
  10. //省略...
  11. }finally{
  12. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  13. }
  14. mInLayout=false;
  15. }

方法中的mView其實就是DecorView,那么host也就代表了DecorView,DecorView其實是個FrameLayout,ViewGroup并沒有重寫layout方法,所以我們來看下View#layout方法

2、layout

  1. /**
  2. *源碼分析起始點:layout()
  3. *作用:確定View本身的位置,即設(shè)置View本身的四個頂點位置
  4. */
  5. publicvoidlayout(intl,intt,intr,intb){
  6. //當(dāng)前視圖的四個頂點
  7. intoldL=mLeft;
  8. intoldT=mTop;
  9. intoldB=mBottom;
  10. intoldR=mRight;
  11. //1.確定View的位置:setFrame()/setOpticalFrame()
  12. //即初始化四個頂點的值、判斷當(dāng)前View大小和位置是否發(fā)生了變化&返回
  13. //setFrame()->分析1
  14. //setOpticalFrame()->分析2
  15. booleanchanged=isLayoutModeOptical(mParent)?setOpticalFrame(l,t,r,b):setFrame(l,t,r,b);
  16. //2.若視圖的大小&位置發(fā)生變化
  17. //會重新確定該View所有的子View在父容器的位置:onLayout()
  18. if(changed||(mPrivateFlags&PFLAG_LAYOUT_REQUIRED)==PFLAG_LAYOUT_REQUIRED){
  19. onLayout(changed,l,t,r,b);
  20. //對于單一View的laytou過程:由于單一View是沒有子View的,故onLayout()是一個空實現(xiàn)->分析3
  21. //對于ViewGroup的laytou過程:由于確定位置與具體布局有關(guān),所以onLayout()在ViewGroup為1個抽象方法,需自定義重寫實現(xiàn)(下面的章節(jié)會詳細(xì)說明)
  22. }
  23. /**
  24. *分析1:setFrame()
  25. *作用:根據(jù)傳入的4個位置值,設(shè)置View本身的四個頂點位置
  26. *即:最終確定View本身的位置
  27. */
  28. protectedbooleansetFrame(intleft,inttop,intright,intbottom){
  29. //通過以下賦值語句記錄下了視圖的位置信息,即確定View的四個頂點
  30. //從而確定了視圖的位置
  31. mLeft=left;
  32. mTop=top;
  33. mRight=right;
  34. mBottom=bottom;
  35. mRenderNode.setLeftTopRightBottom(mLeft,mTop,mRight,mBottom);
  36. }
  37. /**
  38. *分析2:setOpticalFrame()
  39. *作用:根據(jù)傳入的4個位置值,設(shè)置View本身的四個頂點位置
  40. *即:最終確定View本身的位置
  41. */
  42. privatebooleansetOpticalFrame(intleft,inttop,intright,intbottom){
  43. InsetsparentInsets=mParentinstanceofView?
  44. ((View)mParent).getOpticalInsets():Insets.NONE;
  45. InsetschildInsets=getOpticalInsets();
  46. //內(nèi)部實際上是調(diào)用setFrame()
  47. returnsetFrame(
  48. left+parentInsets.left-childInsets.left,
  49. top+parentInsets.top-childInsets.top,
  50. right+parentInsets.left+childInsets.right,
  51. bottom+parentInsets.top+childInsets.bottom);
  52. }
  53. //回到調(diào)用原處
  54. /**
  55. *分析3:onLayout()
  56. *注:對于單一View的laytou過程
  57. *1.由于單一View是沒有子View的,故onLayout()是一個空實現(xiàn)
  58. *2.由于在layout()中已經(jīng)對自身View進(jìn)行了位置計算:setFrame()/setOpticalFrame()
  59. *3.所以單一View的layout過程在layout()后就已完成了
  60. */
  61. protectedvoidonLayout(booleanchanged,intleft,inttop,intright,intbottom){
  62. //參數(shù)說明
  63. //changed當(dāng)前View的大小和位置改變了
  64. //left左部位置
  65. //top頂部位置
  66. //right右部位置
  67. //bottom底部位置
  68. }

3、setFrame

layout方法是用來確定自身位置的,其內(nèi)部調(diào)用了setOpticalFrame、setFrame和onLayout方法,setOpticalFrame內(nèi)部又會調(diào)用setFrame。所以我們先來看setFrame方法,如下

  1. protectedbooleansetFrame(intleft,inttop,intright,intbottom){
  2. booleanchanged=false;
  3. if(mLeft!=left||mRight!=right||mTop!=top||mBottom!=bottom){
  4. //判斷View的位置是否發(fā)生改變
  5. changed=true;
  6. //Rememberourdrawnbit
  7. intdrawn=mPrivateFlags&PFLAG_DRAWN;
  8. intoldWidth=mRight-mLeft;//獲取原來的寬度
  9. intoldHeight=mBottom-mTop;//獲取原來的高度
  10. intnewWidth=right-left;//獲取新的寬度
  11. intnewHeight=bottom-top;//獲取新的高度
  12. //判斷View的尺寸是否發(fā)生改變
  13. booleansizeChanged=(newWidth!=oldWidth)||(newHeight!=oldHeight);
  14. //Invalidateouroldposition
  15. invalidate(sizeChanged);
  16. //對mLeft、mTop、mRight、mBottom初始化,View自身的位置也就確定了。
  17. mLeft=left;
  18. mTop=top;
  19. mRight=right;
  20. mBottom=bottom;
  21. mRenderNode.setLeftTopRightBottom(mLeft,mTop,mRight,mBottom);
  22. mPrivateFlags|=PFLAG_HAS_BOUNDS;
  23. //如果View尺寸發(fā)生改變,將執(zhí)行View#sizeChange方法,在sizeChange方法內(nèi)部會調(diào)用View#onSizeChanged方法。
  24. if(sizeChanged){
  25. sizeChange(newWidth,newHeight,oldWidth,oldHeight);
  26. }
  27. //省略...
  28. }
  29. returnchanged;
  30. }

在setFrame方法中對mLeft、mTop、mRight 、mBottom進(jìn)行初始化,mLeft、mTop分別對應(yīng)View左上角的橫坐標(biāo)和縱坐標(biāo),mRight 、mBottom分別對應(yīng)了View右下角的橫坐標(biāo)和縱坐標(biāo),View的四個頂點的坐標(biāo)確定了,View自身的位置也就確定了;

Android進(jìn)階之深入理解View的布局(Layout)流程原理

4、FrameLayout#onLayout

再回到layout方法,在通過setFrame方法確定了自身位置后,接下來會調(diào)用onLayout方法,這個方法其實用來確定子View的位置的;

不過View和ViewGroup都沒有真正實現(xiàn)onLayout,因為onLayout和onMeasure類似,其過程都與具體的布局有關(guān);

以FrameLayout為例來分析onLayout過程,F(xiàn)rameLayout#onLayout

  1. @Override
  2. protectedvoidonLayout(booleanchanged,intleft,inttop,intright,intbottom){
  3. layoutChildren(left,top,right,bottom,false/*noforceleftgravity*/);
  4. }
  5. 其內(nèi)部調(diào)用了layoutChildren方法
  6. voidlayoutChildren(intleft,inttop,intright,intbottom,
  7. booleanforceLeftGravity){
  8. finalintcount=getChildCount();//獲取子View的數(shù)量
  9. //parentLeft、parentTop分別代表子View所占區(qū)域左上角的橫坐標(biāo)和縱坐標(biāo)
  10. //parentRight、parentBottom分別代表子View所占區(qū)域右下角的橫坐標(biāo)和縱坐標(biāo)
  11. finalintparentLeft=getPaddingLeftWithForeground();
  12. finalintparentRight=right-left-getPaddingRightWithForeground();
  13. finalintparentTop=getPaddingTopWithForeground();
  14. finalintparentBottom=bottom-top-getPaddingBottomWithForeground();
  15. mForegroundBoundsChanged=true;
  16. //遍歷子View
  17. for(inti=0;i<count;i++){
  18. finalViewchild=getChildAt(i);
  19. if(child.getVisibility()!=GONE){
  20. finalLayoutParamslp=(LayoutParams)child.getLayoutParams();
  21. //獲取子View的測量寬、高
  22. finalintwidth=child.getMeasuredWidth();
  23. finalintheight=child.getMeasuredHeight();
  24. intchildLeft;
  25. intchildTop;
  26. //獲取子View設(shè)置的Gravity,如果子View沒有設(shè)置Gravity,則用默認(rèn)的Gravity:DEFAULT_CHILD_GRAVITY。
  27. intgravity=lp.gravity;
  28. if(gravity==-1){
  29. gravity=DEFAULT_CHILD_GRAVITY;
  30. }
  31. finalintlayoutDirection=getLayoutDirection();
  32. finalintabsoluteGravity=Gravity.getAbsoluteGravity(gravity,layoutDirection);
  33. finalintverticalGravity=gravity&Gravity.VERTICAL_GRAVITY_MASK;
  34. //水平方向上,通過設(shè)置的Gravity,來確定childLeft,即每個子View左上角的橫坐標(biāo)
  35. switch(absoluteGravity&Gravity.HORIZONTAL_GRAVITY_MASK){
  36. caseGravity.CENTER_HORIZONTAL:
  37. childLeft=parentLeft+(parentRight-parentLeft-width)/2+
  38. lp.leftMargin-lp.rightMargin;
  39. break;
  40. caseGravity.RIGHT:
  41. if(!forceLeftGravity){
  42. childLeft=parentRight-width-lp.rightMargin;
  43. break;
  44. }
  45. caseGravity.LEFT:
  46. default:
  47. childLeft=parentLeft+lp.leftMargin;
  48. }
  49. //豎直方向上,通過設(shè)置的Gravity,來確定childTop,即每個子View左上角的縱坐標(biāo)
  50. switch(verticalGravity){
  51. caseGravity.TOP:
  52. childTop=parentTop+lp.topMargin;
  53. break;
  54. caseGravity.CENTER_VERTICAL:
  55. childTop=parentTop+(parentBottom-parentTop-height)/2+
  56. lp.topMargin-lp.bottomMargin;
  57. break;
  58. caseGravity.BOTTOM:
  59. childTop=parentBottom-height-lp.bottomMargin;
  60. break;
  61. default:
  62. childTop=parentTop+lp.topMargin;
  63. }
  64. //調(diào)用子View的layout方法
  65. child.layout(childLeft,childTop,childLeft+width,childTop+height);
  66. }
  67. }
  68. }

在該方法內(nèi)部遍歷所有子View過程中,通過子View設(shè)置的Gravity,獲去其childLeft、childTop即子View的左上角的橫坐標(biāo)和縱坐標(biāo),最后執(zhí)行子View的layout方法,來確定子View的位置

5、LinearLayout#onLayout

LinearLayout復(fù)寫的onLayout()分析

  1. /**
  2. *源碼分析:LinearLayout復(fù)寫的onLayout()
  3. *注:復(fù)寫的邏輯和LinearLayoutmeasure過程的onMeasure()類似
  4. */
  5. @Override
  6. protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
  7. //根據(jù)自身方向?qū)傩裕x擇不同的處理方式
  8. if(mOrientation==VERTICAL){
  9. layoutVertical(l,t,r,b);
  10. }else{
  11. layoutHorizontal(l,t,r,b);
  12. }
  13. }
  14. //由于垂直/水平方向類似,所以此處僅分析垂直方向(Vertical)的處理過程->分析1
  15. /**
  16. *分析1:layoutVertical(l,t,r,b)
  17. */
  18. voidlayoutVertical(intleft,inttop,intright,intbottom){
  19. //子View的數(shù)量
  20. finalintcount=getVirtualChildCount();
  21. //1.遍歷子View
  22. for(inti=0;i<count;i++){
  23. finalViewchild=getVirtualChildAt(i);
  24. if(child==null){
  25. childTop+=measureNullChild(i);
  26. }elseif(child.getVisibility()!=GONE){
  27. //2.計算子View的測量寬/高值
  28. finalintchildWidth=child.getMeasuredWidth();
  29. finalintchildHeight=child.getMeasuredHeight();
  30. //3.確定自身子View的位置
  31. //即:遞歸調(diào)用子View的setChildFrame(),實際上是調(diào)用了子View的layout()->分析2
  32. setChildFrame(child,childLeft,childTop+getLocationOffset(child),
  33. childWidth,childHeight);
  34. //childTop逐漸增大,即后面的子元素會被放置在靠下的位置
  35. //這符合垂直方向的LinearLayout的特性
  36. childTop+=childHeight+lp.bottomMargin+getNextLocationOffset(child);
  37. i+=getChildrenSkipCount(child,i);
  38. }
  39. }
  40. }
  41. /**
  42. *分析2:setChildFrame()
  43. */
  44. privatevoidsetChildFrame(Viewchild,intleft,inttop,intwidth,intheight){
  45. child.layout(left,top,left++width,top+height);
  46. //setChildFrame()僅僅只是調(diào)用了子View的layout()而已
  47. //在子View的layout()又通過調(diào)用setFrame()確定View的四個頂點
  48. //即確定了子View的位置
  49. //如此不斷循環(huán)確定所有子View的位置,最終確定ViewGroup的位置
  50. }

總結(jié)

View的layout流程核心在于覆寫ViewGroup的onLayout方法,它的流程是拿到子View的寬高,然后實現(xiàn)自己的布局子View的邏輯,它一般結(jié)合onMeasure方法使用。

原文鏈接:https://mp.weixin.qq.com/s/tJwWblrSglqBpY54FbHewQ

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲久草 | 亚洲欧美中文字幕 | 一级黄色免费网站 | 中文字幕精品一区 | 国产成人亚洲综合 | 羞羞影视 | 色版视频在线观看 | 在线播放国产一区二区三区 | 大尺度av在线 | 激情图区在线观看 | 免费观看黄视频网站 | 成人爽a毛片一区二区免费 日韩av高清在线 | 天天综合网网欲色 | 久久免费公开视频 | 日韩欧美一区二区三 | 一级国产免费 | 一区二区国产在线观看 | 欧美日韩在线精品 | 激情欧美一区二区三区中文字幕 | 中文字幕第一页在线 | 成人3d动漫一区二区三区91 | 欧美在线观看免费观看视频 | 欧美一区久久 | 久久中文字幕一区二区三区 | 亚洲高清视频网站 | 亚洲四区 | 亚洲毛片一区二区 | 中文在线一区二区三区 | hh99me在线观看 | 国产精品污www在线观看 | 爱爱视频在线 | 亚洲专区 中文字幕 | 奇米一区二区三区 | 亚洲激情在线观看 | 91精品久久久久久久久中文字幕 | 久久亚洲国产精品日日av夜夜 | 午夜电影网址 | 久久久91| 国产精品免费一区 | 亚洲精品久久久久久下一站 | 日韩视频精品在线 |