Android之View2

将window添加到activity

在activity的attach()方法中

创建Decorview

在setContentView中

@Override
public void setContentView(int layoutResID) {
  if (mContentParent == null) {
    // mContentParent即为上面提到的ContentView的父容器,若为空则调用installDecor()生成
    installDecor();//如果没有decorView就创建一个
  } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 具有FEATURE_CONTENT_TRANSITIONS特性表示开启了Transition
    // mContentParent不为null,则移除decorView的所有子View
    mContentParent.removeAllViews();
  }
  if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 开启了Transition,做相应的处理,我们不讨论这种情况
    // 感兴趣的同学可以参考源码
    . . .
  } else {
    // 一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局
    // 填充布局也就是把我们设置的ContentView加入到mContentParent中
    mLayoutInflater.inflate(layoutResID, mContentParent);
  }
  . . .
  // cb即为该Window所关联的Activity
  final Callback cb = getCallback();
  if (cb != null && !isDestroyed()) {
    // 调用onContentChanged()回调方法通知Activity窗口内容发生了改变
    cb.onContentChanged();
  }

  . . .
}

通过LayoutInflater.inflate()将xml加载到contentView;至于titleView呢?好像是默认根据manifest来加载,也使用了布局解析,怎么解析呢?似乎是使用pull方法来解析,就是依次遍历xml中的标记

将decorView绑定到window

这个操作在handleResumeActivity中进行,大概就是onResume()对应那个
使用windowManager的addView方法,然后里面有一个ViewRootImpl实例,让它也setView达到绑定效果,之后就用ViewRootImpl来控制View的绘制流程

view绘制流程的起点是viewRoot调用performTraversals()。里面有三个主要方法,performMeasure()、performLayout()、performDraw(),这是顶层View–decorView执行的操作,然后在这三个方法里面会将操作传递给子View
measure阶段

(1)measureHierarchy先给定根容器对里面view的约束条件

childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);

(2)performeasure会转向调用

 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

在里面会调用到

 onMeasure(widthMeasureSpec, heightMeasureSpec);
对于decorView

如果是decorView调用onMeasure,那么它的具体方法是

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int count = getChildCount();
  . . .
  int maxHeight = 0;
  int maxWidth = 0;

  int childState = 0;
  for (int i = 0; i < count; i++) {
    final View child = getChildAt(i);
    if (mMeasureAllChildren || child.getVisibility() != GONE) {
      measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
      final LayoutParams lp = (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());

      . . .
    }
  }

  // Account for padding too
  maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
  maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

  // Check against our minimum height and width
  maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
  maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

  // Check against our foreground's minimum height and width
  final Drawable drawable = getForeground();
  if (drawable != null) {
    maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
    maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
  }

  setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
        resolveSizeAndState(maxHeight, heightMeasureSpec,
        childState << MEASURED_HEIGHT_STATE_SHIFT));
  . . . 
}

从以上代码的执行流程中,我们可以看到,容器View通过measureChildWithMargins()方法对所有子View进行测量后,才能得到自身的测量结果。也就是说,对于ViewGroup及其子类来说,要先完成子View的测量,再进行自身的测量(考虑进padding等)。

ViewGroup的onMeasure过程类似于DecorView,先递归测量子view的,然后确定ViewGroup的测量值
普通View进行测量:直接测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        //这玩意根据specMode,specSize和view本身的layout.params来获取测量值
}
layout:由根View开始,递归地完成整个控件树的布局
对于普通View的布局
public void layout(int l, int t, int r, int b) {
    // l为本View左边缘与父View左边缘的距离
    // t为本View上边缘与父View上边缘的距离
    // r为本View右边缘与父View左边缘的距离
    // b为本View下边缘与父View上边缘的距离
    . . .
    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);
        . . .
            
    }
    . . .
}

调用了setFrame()方法来确定该控件相对于它的父View的位置

对于ViewGroup的onLayout()
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}

void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
  final int count = getChildCount();
  final int parentLeft = getPaddingLeftWithForeground();
  final int parentRight = right - left - getPaddingRightWithForeground();
  final int parentTop = getPaddingTopWithForeground();
  final int parentBottom = bottom - top - getPaddingBottomWithForeground();

  for (int i = 0; i < count; i++) {
    final View child = getChildAt(i);
    if (child.getVisibility() != GONE) {
      final LayoutParams lp = (LayoutParams) child.getLayoutParams();
      final int width = child.getMeasuredWidth();
      final int height = child.getMeasuredHeight();
      int childLeft;
      int childTop;
      int gravity = lp.gravity;

      if (gravity == -1) {
        gravity = DEFAULT_CHILD_GRAVITY;
      }

      final int layoutDirection = getLayoutDirection();
      final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
      final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

      switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
        case Gravity.CENTER_HORIZONTAL:
          childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
          break;

        case Gravity.RIGHT:
          if (!forceLeftGravity) {
            childLeft = parentRight - width - lp.rightMargin;
            break;
          }

        case Gravity.LEFT:
        default:
          childLeft = parentLeft + lp.leftMargin;

      }

      switch (verticalGravity) {
        case Gravity.TOP:
          childTop = parentTop + lp.topMargin;
          break;

        case Gravity.CENTER_VERTICAL:
          childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
          break;

        case Gravity.BOTTOM:
          childTop = parentBottom - height - lp.bottomMargin;
          break;

        default:
          childTop = parentTop + lp.topMargin;
      }
      child.layout(childLeft, childTop, childLeft + width, childTop + height);//在这里调用子view的layout
    }
  }
}
draw阶段
View.draw()
public void draw(Canvas canvas) {
  . . . 
  // 绘制背景,只有dirtyOpaque为false时才进行绘制,下同
  int saveCount;
  if (!dirtyOpaque) {
    drawBackground(canvas);
  }

  . . . 

  // 绘制自身内容
  if (!dirtyOpaque) onDraw(canvas);

  // 绘制子View
  dispatchDraw(canvas);

   . . .
  // 绘制滚动条等
  onDrawForeground(canvas);

}
ViewGroup类的dispatchDraw()方法中会依次调用drawChild()方法来绘制子View
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
  return child.draw(canvas, this, drawingTime);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值