android-----LayoutInflater的inflate方法详解

        前面在使用Fragment的时候,我们通常都要重写他的onCreateView方法,这个方法的方法的使用如下:

        public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
                  return inflater.inflate(R.layout.fragment1,container,false);
        }

        可以看到我们通常都是调用LayoutInflater的inflate方法来获得这个View的,但是传入的这些参数到底是干什么的呢?我们有必要去看看android源码是怎么实现的了:

        从android官方API中我们可以看到LayoutInflater共有四个inflate方法:

  android-----LayoutInflater的inflate方法详解_第1张图片

        这四个方法最终都会调用public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)这个方法,所以我们只需要看看这个方法源码就可以了:

        这个方法的前面大半部分是通过XmlPullParser进行xml解析并且对解析的结果进行各种判断的,不是本方法的重点,我们仅看重要部分:

if (TAG_MERGE.equals(name)) {  
    if (root == null || !attachToRoot) {  
        throw new InflateException("<merge /> can be used only with a valid "  
                + "ViewGroup root and attachToRoot=true");  
    }  
  
    rInflate(parser, root, attrs, false);  
} else {  
    // Temp is the root view that was found in the xml  
    View temp;  
    if (TAG_1995.equals(name)) {  
        temp = new BlinkLayout(mContext, attrs);  
    } else {  
        temp = createViewFromTag(root, name, 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);  
        }  
    }  
    rInflate(parser, temp, attrs, true);  
    if (root != null && attachToRoot) {  
        root.addView(temp, params);  
    }  
    if (root == null || !attachToRoot) {  
        result = temp;  
    } 
}

        第1行TAG_MERGE等于name,不管root和attachToRoot的值,都会执行rInflate方法,这个方法的源码是:

void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {

        final int depth = parser.getDepth();
        int type;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final String name = parser.getName();
            
            if (TAG_REQUEST_FOCUS.equals(name)) {
                parseRequestFocus(parser, parent);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else if (TAG_1995.equals(name)) {
                final View view = new BlinkLayout(mContext, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true);
                viewGroup.addView(view, params);                
            } else {
                final View view = createViewFromTag(parent, name, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }

        if (finishInflate) parent.onFinishInflate();
    }

        重点看第25~30行以及32~36行代码,我们发现两者都会获取到父视图,并且递归的调用自己,递归结束后将自己添加到ViewGroup中,这样就将父视图下的所有子元素全部添加到ViewGroup中,最终形成一个DOM结构,最后把这个DOM结构的最顶层的根布局返回;

        回到inflate方法,当TAG_MERGE不等于name的时候,第11行会首先判断当前name是否是blink内核(android4.4以上采用blink内核),如果是的话调用BlinkLayout构造函数生成当前子视图的View,如果不是blink内核的话,会调用createViewFromTag这个方法将节点名和参数传递进去,通过这些参数构造出一个View并且返回,来看看他的源码:

View createViewFromTag(View parent, String name, AttributeSet attrs) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }

        if (DEBUG) System.out.println("******** Creating view: " + name);

        try {
            View view;
            if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
            else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
            else view = null;

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
            }
            
            if (view == null) {
                if (-1 == name.indexOf('.')) {
                    view = onCreateView(parent, name, attrs);
                } else {
                    view = createView(name, null, attrs);
                }
            }

            if (DEBUG) System.out.println("Created view is: " + view);
            return view;

        } catch (InflateException e) {
            throw e;

        } catch (ClassNotFoundException e) {
            InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class " + name);
            ie.initCause(e);
            throw ie;

        } catch (Exception e) {
            InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class " + name);
            ie.initCause(e);
            throw ie;
        }
    }

        可以看到这个方法中会判断工厂中是否存在该View,如果存在的话,则直接调用onCreateView方法获取到该View即可,如果不存在的话,则会通过createView来反射创建该View;        

        回到inflate方法,第19行首先会判断是否存在父视图,如果存在的话,则在25行获得子视图的布局参数,这里的attrs是通过XmlPullParser解析到的子视图资源文件中的xml标签所对应的set集合,第26行通过判断attachToRoot如果是false的话,则只将temp(是一个声明为View的局部变量)的layout属性设置为是子视图的,其他部分还是显示的父视图的;

        如果root非空,并且attachToRoot为true的话,则会将当前子视图添加到父视图中;

 if (root != null && attachToRoot) {
           root.addView(temp, params);
 }
        如果root为空或者attachToRoot为false的话,则直接返回我们创建的temp,这里的temp将只是子视图

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

        下面通过实例来看看传入不同的参数的效果:

         定义parent.xml作为父视图:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fatherLinearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#00ff00" >
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是父视图"/>
</LinearLayout>
        定义child.xml作为子视图:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffff00" >
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是子视图"/>
</LinearLayout>
       第一种方式:inflate(view,null)
public class LayoutInflaterActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		View childView = getLayoutInflater().inflate(R.layout.child, null);
		setContentView(childView);
	}
}
        输出结果:

  android-----LayoutInflater的inflate方法详解_第2张图片
        第二种方式:inflate(view,root)

public class LayoutInflaterActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		ViewGroup parentView = (ViewGroup) getLayoutInflater().inflate(R.layout.parent, null);
		View childView = getLayoutInflater().inflate(R.layout.child, parentView);
		setContentView(childView);
	}
}

        输出结果:

   android-----LayoutInflater的inflate方法详解_第3张图片
        这种方式的输出结果和inflate(view,root,true)是一致的;

        第三种方式:inflate(view,root,false)

public class LayoutInflaterActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		ViewGroup parentView = (ViewGroup) getLayoutInflater().inflate(R.layout.parent, null);
		View childView = getLayoutInflater().inflate(R.layout.child, parentView,false);
		setContentView(childView);
	}
}
        输出结果:

  android-----LayoutInflater的inflate方法详解_第4张图片
        可以看到这种方式和root==null的显示效果是一样的,这一点从源码中也能找到答案:

 if (root == null || !attachToRoot) {
         result = temp;
 }
        只要inflate(view,root,attachToRoot)中root为null或者attachToRoot为false有一个成立的话,最后都会只显示当前子视图,我们可以对上面Activity代码进行修改,手动将子视图添加到父视图中:

public class LayoutInflaterActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		ViewGroup parentView = (ViewGroup) getLayoutInflater().inflate(R.layout.parent, null);
		View childView = getLayoutInflater().inflate(R.layout.child, null,false);
		parentView.addView(childView);
		setContentView(parentView);
	}
}
        输出结果:

 android-----LayoutInflater的inflate方法详解_第5张图片

        注意这种方式和inflate()方式将子视图加入父视图显示的效果是不一样的;这种方式是以父视图为主,inflate方式是以子视图为主;





        第2行会首先判断当前name是否是blink内核(android4.4以上采用blink内核),如果是的话调用BlinkLayout构造函数生成当前子视图的View,如果不是blink内核的话,会调用

你可能感兴趣的:(android,LayoutInflater,inflate方法)