android 深入理解LayoutInflater.inflate()

LayoutInflater类做过android开发的人都用过,它是负责把一个layout文件变成View对象的,LayoutInflater类有一个inflater(),它重载的方法有四个


我们就说前二个,现在对他的形参做下解释:

第一个参数是resource:表示是传递的layout文件

第二个参数:把第一个参数layout文件是否挂载在该ViewGroup中,

第三个参数:是一个boolean类型,是否第一个参数是否挂载在第二个参数viewgroup中


我在网上看了下对第三个参数的解释,还是挺好的:

被填充的层是否应该附在root参数内部?如果是false,root参数只是用于为XML根元素View创建正确的LayoutParams的子类

其实意思就是:如果attachToRoot是true的话,那第一个参数的layout文件就会被填充并附加在第二个参数所指定的ViewGroup内。方法返回结合后的View,根元素是第二个参数ViewGroup。如果是false的话,第一个参数所指定的layout文件会被填充并作为View返回。这个View的根元素就是layout文件的根元素。不管是true还是false,都需要ViewGroup的LayoutParams来正确的测量与放置layout文件所产生的View对象。

attachToRoot传入true代表layout文件填充的View会被直接添加进ViewGroup,而传入false则代表创建的View会以其他方式被添加进ViewGroup

相当于viewgroup.addView(view,layoutparams)现在写个demo演示下:

package com.zhimore.layoutinflaterdemo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity {
    private LinearLayout ll_root;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_main);
        ll_root = (LinearLayout) findViewById(R.id.ll_root);
        LayoutInflater inflater = LayoutInflater.from(this);
        inflater.inflate(R.layout.btn_content,ll_root,true);
    }
}

content_main.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
     >
    <TextView
        android:text="Hello World!"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        />
    <TextView
        android:text="Hello World!"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        />
    <TextView
        android:text="Hello World!"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        />
    <TextView
        android:text="Hello World!"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        />
</LinearLayout>
我们运行起来看下:


现在我们把第二个和第三方参数改成

inflater.inflate(R.layout.btn_content,null,false);

发现这样并没有任何效果,

我们现在自定义一个LinearLayout,

package com.zhimore.layoutinflaterdemo.com.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import com.zhimore.layoutinflaterdemo.R;
/**
 * Created by Administrator on 2016/2/25.
 */
public class MyLinearLayout extends LinearLayout{
    public MyLinearLayout(Context context) {
        super(context);
    }
    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater inflater = LayoutInflater.from(context);
        inflater.inflate(R.layout.btn_content,this,true);
    }
    public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

运行起来


我们发现在自定义ViewGroup的时候,使用这个就是往viewgroup中添加子view的,

当在Fragment的onCreateView()方法中填充并返回View时,要将attachToRoot设为false。如果传入true,会抛出IllegalStateException,因为指定的子View已经有父View了。你需要指定在哪里将Fragment的View放进Activity里,而添加、移除或替换Fragment则是FragmentManager的事情。

FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.root_viewGroup);
 
if (fragment == null) {
    fragment = new MainFragment();
    fragmentManager.beginTransaction().add(R.id.root_viewGroup, fragment).commit();
}

上面代码中root_viewGroup就是Activity中用于放置Fragment的容器,它会作为inflate()方法中的第二个参数被传入onCreateView()中。它也是你在inflate()方法中传入的ViewGroup。FragmentManager会将Fragment的View添加到ViewGroup中,你可不想添加两次。

public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup, false);
    
    return view;
}

问题是:如果我们不需在onCreateView()中将View添加进ViewGroup,为什么还要传入ViewGroup呢?为什么inflate()方法必须要传入根ViewGroup?

原因是及时不需要马上将新填充的View添加进ViewGroup,我们还是需要这个父元素的LayoutParams来在将来添加时决定View的size和position。

你在网上一定会遇到一些不正确的建议。有些人会建议你如果将attachToRoot设置为false的话直接将根ViewGroup传入null。但是,如果有父元素的话,还是应该传入的。

null-root

Lint会警告你不要讲null作为root传入。你的App不会挂掉,但是可能会表现异常。当你的子View没有正确的LayoutParams时,它会自己通过generateDefaultLayoutParams)计算。

你可能并不想要这些默认的LayoutParams。你在XML指定的LayoutParams会被忽略。我们可能已经指定了子View要填充父元素的宽度,但父View又wrap_content导致最终的View小很多。

下面是一种没有ViewGroup作为root传入inflate()方法的情况。当为AlertDialog创建自定义View时,还无法访问父元素。

AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
View customView = inflater.inflate(R.layout.custom_alert_dialog, null);
...
dialogBuilder.setView(customView);
dialogBuilder.show();

在这种情况下,可以将null作为root ViewGroup传入。后来我发现AlertDialog还是会重写LayoutParams并设置各项参数为match_parent。但是,规则还是在有ViewGroup可以传入时传入它。

避开崩溃、异常表现与误解

希望这篇文章可以帮助你在使用LayoutInflater时避开崩溃、异常表现与误解。下面整理了文章的要点:

1:如果可以传入ViewGroup作为根元素,那就传入它。

2:避免将null作为根ViewGroup传入。

3:当我们不负责将layout文件的View添加进ViewGroup时设置attachToRoot参数为false。

4:不要在View已经被添加进ViewGroup时传入true。

5:自定义View时很适合将attachToRoot设置为true








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值