android LayoutInflate.inflate源码分析及使用区分

之前看了两位android大神关于LayoutInflate.inflate详解,我综合一下两篇文章做了自己的总结。如果觉得我总结的不好或者想看原例请看下面:

郭霖:http://blog.csdn.net/guolin_blog/article/details/12921889

鸿洋_:http://blog.csdn.net/lmj623565791/article/details/38171465

LayoutInflate的inflate方法用于加载R.layout.xxx的布局文件,根据这个布局文件创建一个View实例。有以下几种重载的方式:

 View 

 inflate(int resource, ViewGroup root)
          Inflate a new view hierarchy from the specified xml resource.

View

  inflate(int resource, ViewGroup root, boolean attachToRoot)
          Inflate a new view hierarchy from the specified xml resource.

View 

 inflate(XmlPullParser parser, ViewGroup root)
          Inflate a new view hierarchy from the specified xml node.

 View

   inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
          Inflate a new view hierarchy from the specified XML node.

几种方法都是调用inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)方法。

public View inflate(XmlPullParser parser, ViewGroup root,boolean attachToRoot) {

    synchronized(mConstructorArgs) {

        finalAttributeSet attrs = Xml.asAttributeSet(parser);

       mConstructorArgs[0] = mContext;

        View result =root;

        try {

            inttype;

            while((type = parser.next()) != XmlPullParser.START_TAG &&

                   type != XmlPullParser.END_DOCUMENT) {

            }

            if (type!= XmlPullParser.START_TAG) {

               throw new InflateException(parser.getPositionDescription()

                       + ": No start tag found!");

            }

            finalString name = parser.getName();

            if(TAG_MERGE.equals(name)) {

                if (root == null || !attachToRoot){

                   throw new InflateException("merge can be used only with a validViewGroup root and attachToRoot=true");

                }

                rInflate(parser, root, attrs);

            } else {

                Viewtemp = createViewFromTag(name, attrs);

                ViewGroup.LayoutParams params = null;

                if (root != null) {

                   params = root.generateLayoutParams(attrs);

                   if (!attachToRoot) {

                       temp.setLayoutParams(params);

                   }

               }

                rInflate(parser, temp, attrs);

                if (root != null &&attachToRoot) {

                   root.addView(temp, params);

                }

               if (root == null || !attachToRoot) {

                   result = temp;

               }

            }

        } catch(XmlPullParserException e) {

           InflateException ex = new InflateException(e.getMessage());

            ex.initCause(e);

            throwex;

        } catch(IOException e) {

           InflateException ex = new InflateException(

                   parser.getPositionDescription()

                   + ": " + e.getMessage());

           ex.initCause(e);

            throw ex;

        }

        returnresult;

    }

}

 

对于inflate的实现过程和几个重载方法的区别主要在红色代码和蓝色代码部分。

◎实现过程:首先通过while循环用pull解析器parser.next()获得xml文件的标签类型(或称节点,如Button、TextView等)判断xml文件是否合法。

再name=parser.getName();获得标签名。不同标签代表了不同的组件类。使用temp=createViewFromTag(name, attrs) 根据标签名和参数创建View对象,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。

这样createViewFromTag等到的时一个根布局,还需要创建这个根标签下子标签的组件。此时调用rInflate(parser, temp, attrs)将布局文件、根布局、布局参数都传入。这是一个递归调用自身的方法。目的是递归的将根View(temp)下的子View实例化并addView到其父View中。

这样,把整个布局文件都解析完成后就形成了一个完整的DOM结构,最终会把最顶层的根布局返回,至此inflate()过程全部结束。

◎几个重载方法的区别:主要是区别ViewGrouproot和boolean attachToRoot的null/非null和true/false不同组合时的返回结果。

首先inflate方法返回的是View result,并且初始化引用了root。接下来会创建一个布局参数ViewGroup.LayoutParamsparams=null。

当root不为null,会用root.generateLayoutParams(attrs)实例化params。若root=null,inflate返回的result指向temp,所以此时返回的View的布局参数(getLayoutParams())为null,attachToRoot也没有意义。

接下来,当root!=null,若attachToRoot=false,就将temp的布局参数设为params(temp.setLayoutParams(params))。而若attachToRoot=true,就把temp添加到root组件中,作为嵌套在temp外面的一层布局。

总结一下几种inflate方法的区别:

Øinflate(resId , null ) 只创建temp ,返回temp。

Øinflate(resId , parent, false )创建temp,然后执行temp. setLayoutParams(params);返回temp。

Øinflate(resId , parent, true ) 创建temp,然后执行root.addView(temp,params);最后返回root。

假设resId布局文件只有一个Button组件(button_layout.xml):

<Button xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/id_btn"
   android:layout_width="120dp"
   android:layout_height="120dp"
   android:text="Button" >
</Button>

使用以下方式在Activity中显示:

    (1)

protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mainLayout = (LinearLayout) findViewById(R.id.main_layout);  
        LayoutInflater layoutInflater = LayoutInflater.from(this);  
        View buttonLayout = layoutInflater.inflate(R.layout.button_layout, null);  //①
        mainLayout.addView(buttonLayout);  
    }  

    其中main_layout是一个只有一个LinearLayout的布局文件。上面是将把Button动态添加到mainLayout中,其中使用的是inflate(resId , null )方法。此时显示的效果是

图片

图1

而若将①改为layoutInflater.inflate(R.layout.button_layout, mainLayout,false);显示如下

图片

图2

这是因为root的存在,调用setLayoutParams(params)使得resId的layout_height和layout_width有效。

(2)接下来换一种显示方式

protected void onCreate(Bundle savedInstanceState) 
    { 
       super.onCreate(savedInstanceState); 
        mInflater =LayoutInflater.from(this); 
         Viewview1 = mInflater.inflate(R.layout.button_layout, null); 
        View view2 =mInflater.inflate(R.layout.button_layout, 
               (ViewGroup)findViewById(android.R.id.content), false); 
        View view3 =mInflater.inflate(R.layout.button_layout, 
               (ViewGroup)findViewById(android.R.id.content), true); 
    } 

运行结果view1、view2的并不能显示,而view3可以显示。这是因为view3的root是Activity的内容区域:即android.R.id.content,是一个FrameLayout,我们在setContentView(resId)时,其实系统会自动为了包上一层FrameLayout(id=content)!并且view3的attachToRoot=true,所以虽然没有调用setContentView,还是能将view3 addView到android.R.id.content的FrameLayout根View上!

所以如果在Activity中使用setContentView(R.layout.button_layout)同样会显示图2的效果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值