闲谈自定义控件源码-view如何展示在界面上

来源:互联网 发布:python progressbar 编辑:程序博客网 时间:2024/06/07 03:32
布局是如何展示在activity上面的?
    直观的看到 在activity 的oncreate(Bundle savedInstanceState)回调中的setContentView(View view)方法中设置布局。
   Oncreate()回调是由谁来调用的呢?setContentView(View view)作用?
    
  1. final void performCreate(Bundle icicle) {
  2. onCreate(icicle);
  3. mActivityTransitionState.readState(icicle);
  4. performCreateCommon();
  5. }
 由performCreate()去调用,performCreate()在何时被调用不得而知。
  1. public void setContentView(@LayoutRes int layoutResID) {
  2. getWindow().setContentView(layoutResID);
  3. initWindowDecorActionBar();
  4. }
通过调用成员变量window的setContentView(int resId)方法,知道phoneWindow继承自window 看PhoneWindow的源码
  1. @Override
  2. public void setContentView(int layoutResID) {
  3. // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
  4. // decor, when theme attributes and the like are crystalized. Do not check the feature
  5. // before this happens.
  6. if (mContentParent == null) {
  7. installDecor();
  8. } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
  9. mContentParent.removeAllViews();
  10. }
  11. if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
  12. ...
  13. } else {
  14. mLayoutInflater.inflate(layoutResID, mContentParent);
  15. }
  16. mContentParent.requestApplyInsets();
  17. final Callback cb = getCallback();
  18. if (cb != null && !isDestroyed()) {
  19. cb.onContentChanged();
  20. }
  21. }
mContentParent 是用来放置DecorView或DecorView的child,如果没有装载Decorview就去装载。再看installDecor()源码
  1. private void installDecor() {
  2. if (mDecor == null) {
  3. mDecor = generateDecor();
  4. ...
  5. }
  6. if (mContentParent == null) {
  7. mContentParent = generateLayout(mDecor);
  8. ..
  9. }
  10. }
generateDecor() 创建一个DecorView,gennerateLayout()
  1. protected ViewGroup generateLayout(DecorView decor) {
  2. // Apply data from current theme.
  3. TypedArray a = getWindowStyle();
  4. ...
  5. }
应用theme 再看一下setContentView核心的一句代码:mLayoutInflater.inflate(layoutResID, mContentParent);点进去看:
  1. public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
  2. return inflate(resource, root, root != null);
  3. }
继续点进去看
  1. public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
  2. final Resources res = getContext().getResources();
  3. ..
  4. final XmlResourceParser parser = res.getLayout(resource);
  5. try {
  6. return inflate(parser, root, attachToRoot);
  7. } finally {
  8. parser.close();
  9. }
  10. }
继续点击inflate(parser, root, attachToRoot);
  1. public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
  2. synchronized (mConstructorArgs) {
  3. Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
  4. final Context inflaterContext = mContext;
  5. final AttributeSet attrs = Xml.asAttributeSet(parser);
  6. Context lastContext = (Context) mConstructorArgs[0];
  7. mConstructorArgs[0] = inflaterContext;
  8. View result = root;
  9. try {
  10. // Look for the root node.
  11. int type;
  12. while ((type = parser.next()) != XmlPullParser.START_TAG &&
  13. type != XmlPullParser.END_DOCUMENT) {
  14. // Empty
  15. }
  16. if (type != XmlPullParser.START_TAG) {
  17. throw new InflateException(parser.getPositionDescription()
  18. + ": No start tag found!");
  19. }
  20. final String name = parser.getName();
  21. ...
  22. if (TAG_MERGE.equals(name)) {
  23. if (root == null || !attachToRoot) {
  24. throw new InflateException("<merge /> can be used only with a valid "
  25. + "ViewGroup root and attachToRoot=true");
  26. }
  27. rInflate(parser, root, inflaterContext, attrs, false);
  28. } else {
  29. // Temp is the root view that was found in the xml
  30. final View temp = createViewFromTag(root, name, inflaterContext, attrs);
  31. ViewGroup.LayoutParams params = null;
  32. if (root != null) {
  33. ...
  34. // Create layout params that match root, if supplied
  35. params = root.generateLayoutParams(attrs);
  36. if (!attachToRoot) {
  37. // Set the layout params for temp if we are not
  38. // attaching. (If we are, we use addView, below)
  39. temp.setLayoutParams(params);
  40. }
  41. }
  42. ...
  43. // Inflate all children under temp against its context.
  44. rInflateChildren(parser, temp, attrs, true);
  45. // We are supposed to attach all the views we found (int temp)
  46. // to root. Do that now.
  47. if (root != null && attachToRoot) {
  48. root.addView(temp, params);
  49. }
  50. // Decide whether to return the root that was passed in or the
  51. // top view found in xml.
  52. if (root == null || !attachToRoot) {
  53. result = temp;
  54. }
  55. }
  56. } catch (XmlPullParserException e) {
  57. ...
  58. } catch (Exception e) {
  59. ...
  60. } finally {
  61. ...
  62. }
  63. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  64. return result;
  65. }
  66. }

看到
1:LayoutInflator 底层是通过XmlPullParser的pull解析来讲xml文件转化为java实例。
2:当遇到<merge/>标签时只会讲标签里面的子节点加入到父节点中。在写item的布局时顶层用<merge/>标签可以减少布局嵌套。
3:如果参数ViewRoot为空,attchToRoot是true和false都是没有意义的。如果ViewRoot不为空,attchToRoot为true则使用ViewRoot的LayoutParams,否则创建一个LayoutParams
0 0
原创粉丝点击