View的LayoutInflater源码分析

2017-08-11  本文已影响0人  楚灵彦

1.layout布局转换view方法

2个重载方法,最终都是到最后一个方法

LayoutInflater.from(this).inflate();

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
}


public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
}

2.真正的解析方法

Android里面的layout都是xml形式,因此出现XmlResourceParser解析器,用于解析xml布局各个节点(tag)view. 继续看方法inflate(parser, root, attachToRoot),很明显解析器作为参数传入方法进行解析。

 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
            .......
            View result = root;
            try {
                // Look for the root node. 此处检查根布局tag
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }
                final String name = parser.getName();
               
                if (TAG_MERGE.equals(name)) {  //merge标签
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                    ViewGroup.LayoutParams params = null;
                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }
                    // Inflate all children under temp against its context. 递归调用解析view树
                    rInflateChildren(parser, temp, attrs, true);

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        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) {
                        result = temp;
                    }
                }

            } catch (Exception e) {
               .......
            } finally {
               .......
            }
            return result;
        }
    }

以下是几个关键点:

View result = root;
 final View temp = createViewFromTag(root, name, inflaterContext, attrs);
  ViewGroup.LayoutParams params = null; //初始布局参数
final AttributeSet attrs = Xml.asAttributeSet(parser);

 final View temp = createViewFromTag(root, name, inflaterContext, attrs);

 ViewGroup.LayoutParams params = null;

 params = root.generateLayoutParams(attrs);  //获取attrs布局参数

  rInflateChildren(parser, temp, attrs, true);
  
  
//此处root!=null,就获取布局里面的布局参数
if (root != null) {
// Create layout params that match root, if supplied params =  
params = root.generateLayoutParams(attrs);  //获取attrs布局参数
if (!attachToRoot) {
    // Set the layout params for temp if we are not
    // attaching. (If we are, we use addView, below)
    temp.setLayoutParams(params);
}
}

// We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
if (root != null && attachToRoot) {
    root.addView(temp, params);
}

if (root == null || !attachToRoot) {
    result = temp;
}

2.分析结论

上面的tempView代表整个layout布局转换出来的view,而最外层的根view,大小是由其自己和父view共同测量决定,也就是在父view里面的布局参数。

1.1 inflate(int resource, ViewGroup root)==inflate(resource, root, root != null)

1.2 inflate(int resource, ViewGroup root, boolean attachToRoot)

1.3 inflate(parser, root, attachToRoot)

以上结论,大家可以自己验证,可以借助Layout Inpector查看布局的层级结构。

3. Android 源码出现的例子(基于7.0)

 public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    if (convertView == null) {
    holder=new ViewHolder();  
    convertView = mInflater.inflate(R.layout.item, null);
    }else {holder = (ViewHolder)convertView.getTag(); }
      
//只能填充以下的例子
1.convertView = mInflater.inflate(R.layout.item, parent,false);

2.convertView = mInflater.inflate(R.layout.item, null);

2.convertView = mInflater.inflate(R.layout.item, null,false);


这样添加会异常

convertView = mInflater.inflate(R.layout.item, parent);

@Override
public void addView(View child, LayoutParams params) {
        throw new UnsupportedOperationException("addView(View, LayoutParams) "
                + "is not supported in AdapterView");
}
public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
}

//PhoneWindow实现
@Override
public void setContentView(int layoutResID) {
        
if (mContentParent == null) {
    installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    mContentParent.removeAllViews();
}

if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
            getContext());
    transitionTo(newScene);
} else {
    mLayoutInflater.inflate(layoutResID, mContentParent); //将布局view放入到DecorVew
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
    cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

将布局view放入到DecorVew

if (mContentParent == null) {
    installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    mContentParent.removeAllViews();
}

 mLayoutInflater.inflate(layoutResID, mContentParent);

Dialog布局

 final Window w = new PhoneWindow(mContext);
 
 public void setContentView(@LayoutRes int layoutResID) {
        mWindow.setContentView(layoutResID);
 }
 

DecorView布局参数

 mDecor = mWindow.getDecorView();
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
    final ApplicationInfo info = mContext.getApplicationInfo();
    mWindow.setDefaultIcon(info.icon);
    mWindow.setDefaultLogo(info.logo);
    mActionBar = new WindowDecorActionBar(this);
}

WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
    WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
    nl.copyFrom(l);
    nl.softInputMode |=
            WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
    l = nl;
}

mWindowManager.addView(mDecor, l);

上一篇下一篇

猜你喜欢

热点阅读