添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

View与ViewGroup layout 过程

这个过程相比measure要简单一点,我们先从View 的layout()看起,ViewGroup的layout主要还是判断一些条件之后调用View 的 layout() View.java
  1. public void layout ( int l , int t , int r , int b ) {
  2. if ( DBG_SYSTRACE_LAYOUT ) {
  3. Trace . traceBegin ( Trace . TRACE_TAG_VIEW , "layout : " + getClass (). getSimpleName ());
  4. }
  5. // !=0 表示在measure 的时候设置这个标志,也就是measure的时候根据measure cache 测量,而不是直接调用onMeasure(),所以这里要重新onMeasure一次
  6. if (( mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT ) != 0 ) {
  7. /// M: Monitor onLayout time if longer than 3s print log.
  8. if ( DBG_LAYOUT || DBG_MEASURE_LAYOUT ) {
  9. Xlog . d ( VIEW_LOG_TAG , "view onMeasure start (measure cache), this =" + this
  10. + ", widthMeasureSpec = " + MeasureSpec . toString ( mOldWidthMeasureSpec )
  11. + ", heightMeasureSpec = " + MeasureSpec . toString ( mOldHeightMeasureSpec ));
  12. }
  13. long logTime = System . currentTimeMillis ();
  14. onMeasure ( mOldWidthMeasureSpec , mOldHeightMeasureSpec );
  15. long nowTime = System . currentTimeMillis ();
  16. if ( nowTime - logTime > DBG_TIMEOUT_VALUE ) {
  17. Xlog . d ( VIEW_LOG_TAG , "[ANR Warning]onMeasure time too long, this =" + this
  18. + "time =" + ( nowTime - logTime ) + " ms" );
  19. }
  20. if ( DBG_LAYOUT || DBG_MEASURE_LAYOUT ) {
  21. Xlog . d ( VIEW_LOG_TAG , "view onMeasure end (measure cache), this =" + this
  22. + ", mMeasuredWidth = " + mMeasuredWidth + ", mMeasuredHeight = "
  23. + mMeasuredHeight + ", time =" + ( nowTime - logTime ) + " ms" );
  24. }
  25. mPrivateFlags3 &= ~ PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT ; //将标志位恢复
  26. }
  27. int oldL = mLeft ;
  28. int oldT = mTop ;
  29. int oldB = mBottom ;
  30. int oldR = mRight ;
  31. //视觉/光学边界布局,一般是false
  32. boolean changed = isLayoutModeOptical ( mParent ) ?
  33. setOpticalFrame ( l , t , r , b ) : setFrame ( l , t , r , b );
  34. if ( DBG_LAYOUT || DBG_MEASURE_LAYOUT ) {
  35. Xlog . d ( VIEW_LOG_TAG , "view layout start, this = " + this + ", mLeft = " + mLeft
  36. + ", mTop = " + mTop + ", mRight = " + mRight + ", mBottom = " + mBottom
  37. + ", changed = " + changed );
  38. }
  39. //PFLAG_LAYOUT_REQUIRED这个标志也是在measure 的时候设置的.
  40. if ( changed || ( mPrivateFlags & PFLAG_LAYOUT_REQUIRED ) == PFLAG_LAYOUT_REQUIRED ) {
  41. /// M: Monitor onLayout time if longer than 3s print log.
  42. long logTime = System . currentTimeMillis ();
  43. onLayout ( changed , l , t , r , b ); //这个方法view和ViewGroup都没有实现,具体是有子类去实现
  44. long nowTime = System . currentTimeMillis ();
  45. if ( nowTime - logTime > DBG_TIMEOUT_VALUE ) {
  46. Xlog . d ( VIEW_LOG_TAG , "[ANR Warning]onLayout time too long, this =" + this
  47. + "time =" + ( nowTime - logTime ) + " ms" );
  48. }
  49. if ( DBG_LAYOUT || DBG_MEASURE_LAYOUT ) {
  50. Xlog . d ( VIEW_LOG_TAG , "view layout end, this =" + this + ", mLeft = " + mLeft
  51. + ", mTop = " + mTop + ", mRight = " + mRight + ", mBottom = " + mBottom
  52. + ", time =" + ( nowTime - logTime ) + " ms" );
  53. }
  54. mPrivateFlags &= ~ PFLAG_LAYOUT_REQUIRED ; //将标志位恢复
  55. ListenerInfo li = mListenerInfo ;
  56. if ( li != null && li . mOnLayoutChangeListeners != null ) {
  57. ArrayList < OnLayoutChangeListener > listenersCopy =
  58. ( ArrayList < OnLayoutChangeListener >) li . mOnLayoutChangeListeners . clone ();
  59. int numListeners = listenersCopy . size ();
  60. for ( int i = 0 ; i < numListeners ; ++ i ) {
  61. listenersCopy . get ( i ). onLayoutChange ( this , l , t , r , b , oldL , oldT , oldR , oldB ); //接口回调
  62. }
  63. }
  64. } else {
  65. if ( DBG_LAYOUT || DBG_MEASURE_LAYOUT ) {
  66. Xlog . d ( VIEW_LOG_TAG , "view layout end 2 (use previous layout), this = " + this
  67. + ", mLeft = " + mLeft + ", mTop = " + mTop
  68. + ", mRight = " + mRight + ", mBottom = " + mBottom );
  69. }
  70. }
  71. mPrivateFlags &= ~ PFLAG_FORCE_LAYOUT ;
  72. mPrivateFlags3 |= PFLAG3_IS_LAID_OUT ;
  73. if ( DBG_SYSTRACE_LAYOUT ) {
  74. Trace . traceEnd ( Trace . TRACE_TAG_VIEW );
  75. }
  76. }
从上面的代码可以看到,layout 流程先会根据之前在measure流程中是否设置了 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT 变量来判断是否需要重新measure一遍,然后调用 setFrame 来确定自己的4个点的位置,4个点分别是left,top.right,bottom.有了这4个点,后续才能准确的绘制 . 接着根据在measure里面设置的 PFLAG_LAYOUT_REQUIRED 变量来执行 onLayout 流程,其实这个方法View和ViewGroup都没有去实现,真正的实现是在各个子类里面的,后面会分析.再这之后,如果这个视图设置过 OnLayoutChangeListener 接口,还会回调这些接口的 onLayoutChange()
  1. protected void onMeasure ( int widthMeasureSpec , int heightMeasureSpec ) {
  2. if ( mOrientation == VERTICAL ) {
  3. measureVertical ( widthMeasureSpec , heightMeasureSpec );
  4. } else {
  5. measureHorizontal ( widthMeasureSpec , heightMeasureSpec );
  6. }
  7. }
这里我们只分析垂直布局
  1. void layoutVertical ( int left , int top , int right , int bottom ) {
  2. final int paddingLeft = mPaddingLeft ;
  3. int childTop ;
  4. int childLeft ;
  5. // Where right end of child should go
  6. final int width = right - left ;
  7. int childRight = width - mPaddingRight ;
  8. // Space available for child
  9. int childSpace = width - paddingLeft - mPaddingRight ;
  10. final int count = getVirtualChildCount ();
  11. final int majorGravity = mGravity & Gravity . VERTICAL_GRAVITY_MASK ;
  12. final int minorGravity = mGravity & Gravity . RELATIVE_HORIZONTAL_GRAVITY_MASK ;
  13. switch ( majorGravity ) {
  14. case Gravity . BOTTOM :
  15. // mTotalLength contains the padding already
  16. childTop = mPaddingTop + bottom - top - mTotalLength ;
  17. break ;
  18. // mTotalLength contains the padding already
  19. case Gravity . CENTER_VERTICAL :
  20. childTop = mPaddingTop + ( bottom - top - mTotalLength ) / 2 ;
  21. break ;
  22. case Gravity . TOP :
  23. default :
  24. childTop = mPaddingTop ;
  25. break ;
  26. }
  27. for ( int i = 0 ; i < count ; i ++) {
  28. final View child = getVirtualChildAt ( i );
  29. if ( child == null ) {
  30. childTop += measureNullChild ( i );
  31. } else if ( child . getVisibility () != GONE ) {
  32. final int childWidth = child . getMeasuredWidth ();
  33. final int childHeight = child . getMeasuredHeight ();
  34. final LinearLayout . LayoutParams lp =
  35. ( LinearLayout . LayoutParams ) child . getLayoutParams ();
  36. int gravity = lp . gravity ;
  37. if ( gravity < 0 ) {
  38. gravity = minorGravity ;
  39. }
  40. final int layoutDirection = getLayoutDirection ();
  41. final int absoluteGravity = Gravity . getAbsoluteGravity ( gravity , layoutDirection );
  42. switch ( absoluteGravity & Gravity . HORIZONTAL_GRAVITY_MASK ) {
  43. case Gravity . CENTER_HORIZONTAL :
  44. childLeft = paddingLeft + (( childSpace - childWidth ) / 2 )
  45. + lp . leftMargin - lp . rightMargin ;
  46. break ;
  47. case Gravity . RIGHT :
  48. childLeft = childRight - childWidth - lp . rightMargin ;
  49. break ;
  50. case Gravity . LEFT :
  51. default :
  52. childLeft = paddingLeft + lp . leftMargin ;
  53. break ;
  54. }
  55. if ( hasDividerBeforeChildAt ( i )) {
  56. childTop += mDividerHeight ;
  57. }
  58. childTop += lp . topMargin ;
  59. setChildFrame ( child , childLeft , childTop + getLocationOffset ( child ),
  60. childWidth , childHeight );
  61. childTop += childHeight + lp . bottomMargin + getNextLocationOffset ( child );
  62. i += getChildrenSkipCount ( child , i );
  63. }
  64. }
  65. }
View与ViewGroup layout 过程这儿过程相比measure要简单一点,我们先从View 的layout()看起,ViewGroup的layout主要还是判断一些条件之后调用View 的layout()View.javapublic void layout(int l, int t, int r, int b) { if (DBG_SYSTRACE_LAY
on Layout layout 方法简介 on Layout 方法是 View Group 中子 View 的布局方法,用于放置子 View 的位置。放置子 View 很简单,只需在重写on Layout 方法,然后获取子 View 的实例,调用子 View layout 方法实现布局。在实际开发中,一般要配合on Measure 测量方法一起使用。 on Layout 方法: @Override protected abst
为什么要进行性能优化? 随着项目版本的不断迭代,App的性能问题会逐渐的暴露出来,给用户带来一些卡顿、崩溃的体验。面对给和用户造成的不良效果,做出了性能优化,提升App整体性能,带用户带来良好的用户触感。 有哪些可以进行性能优化? 1.内存优化 内存泄漏是Androi...
Android stuido是Google官方唯一推荐的Android开发IDE,但是在使用 过程 中,必然会遇到一些一问题,Android Studio毕竟还在完善中,在这,分享一下我在使用Android Studio的时候遇到的问题以及解决方案,希望对广大博友有所帮助。 Error:A problem was found with the configuration of task ...
1. 优化布局层级 2. 在onDraw的时候避免做耗时操作,同时尽量不要在ondraw中创建局部对象,onDraw频繁调用会产生大量的临时对象占用过多内存导致多次gc,降低性能; 2.内存方向 1. 内存泄漏相关可以参看我的其他博客内容 2. 内存溢出 1.如果内存泄漏过多,很多对象未被回收,导致内存占用太大,内存溢出; 2.大... 最近参加了几轮面试,发现很多5-7年工作经验的候选人在性能优化这一块,基本上只能说出传统的分析方式,例如 ANR 分析,是通过查看/data/ anr / 下的log,分析主线程堆栈、cpu、锁信息等, 然而,这种方法有一定的局限性,并不是每次都奏效,很多时候是没有堆栈信息给你分析的,例如有些高版本设备需要root权限才能访问/data/ anr / 目录,或者是线上用户的反馈,只有一张 ANR 的截 10-27 09:08:37.031 648 1118 E ActivityManager: +0% 924/kworker/2:0: 0% user + 0% kernel 10-27 09:08:37.031 648 1118 E ActivityManager: +0% 1014/logcat: 0% user + 0% ...
at android. view . View .dispatchTouchEvent( View .java:14540) at android. view . View Group .dispatchTransformedTouchEvent( View Group .java:3120) at android. view . View Group .dispatchTouchEvent( View Group .java:2801) at android. view . View Group .dispatchTransformedTouchEvent( View Group .java:3120) at android. view . View Group .dispatchTouchEvent( View Group .java:2801) at android. view . View Group .dispatchTransformedTouchEvent( View Group .java:3120) at android. view . View Group .dispatchTouchEvent( View Group .java:2801) at android. view . View Group .dispatchTransformedTouchEvent( View Group .java:3120) at android. view . View Group .dispatchTouchEvent( View Group .java:2801) at android. view . View Group .dispatchTransformedTouchEvent( View Group .java:3120) at android. view . View Group .dispatchTouchEvent( View Group .java:2801) at android. view . View Group .dispatchTransformedTouchEvent( View Group .java:3120) at android. view . View Group .dispatchTouchEvent( View Group .java:2801) at android. view . View Group .dispatchTransformedTouchEvent( View Group .java:3120) at android. view . View Group .dispatchTouchEvent( View Group .java:2801)