LayoutInflater.inflate()方法解析

本文介绍了Android中LayoutInflater.inflate()方法的作用,它是如何将XML布局文件转换为View,并详细解析了inflate()方法的参数,包括root和attachToRoot的含义。同时,文章讨论了为何在某些情况下需要设置root,以及attachToRoot为false的使用场景。最后给出了使用inflate()时的最佳实践建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、基本介绍

在开发中 LayoutInflater. inflate() 这个方法还是非常有用的,它的作用类似于 findViewById()。不同点是 inflate() 是用来找 res/layout/ 下的 xml 布局文件,并且实例化,而 findViewById() 是找 xml 布局文件下的具体 widget 控件(如 Button、TextView 等)。

对于一个没有被载入或者想要动态载入的界面,都需要使用 LayoutInflater.inflate() 来载入。而对于一个已经载入的界面,就可以使用 Activity.findViewById() 方法来获得其中的界面元素。

2、获取LayoutInflater实例

获得 LayoutInflater 实例的几种方式:

1.
 LayoutInflater inflater = getLayoutInflater();  //调用Activity的getLayoutInflater()

2.
 LayoutInflater localinflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

3.
 LayoutInflater inflater = LayoutInflater.from(context);  

其实,这三种方式本质是相同的。第一种方式调用的 getLayoutInflater() 调用了 PhoneWindow 的 getLayoutInflater() 方法,从其源码看:

public PhoneWindow(Context context) {  
       super(context);  
       mLayoutInflater = LayoutInflater.from(context);  
}  

可以看到它又调用了第二种方法的方法,我们接着看 from(Context) 方法的源码:

public static LayoutInflater from(Context context) {   
    LayoutInflater LayoutInflater =   
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);   
    if (LayoutInflater == null) {   
        throw new AssertionError("LayoutInflater not found.");   
    }   
    return LayoutInflater;   
} 

所以这三种方式最终本质是都是调用的 Context.getSystemService()。

3、inflate()介绍

除了 LayoutInflater.inflate() 还有 View.inflate() 这个方法,我们看看它的源码:

public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
    LayoutInflater factory = LayoutInflater.from(context);
    return factory.inflate(resource, root);
}

可见 View.inflate() 是通过 LayoutInfater.inflate() 实现了 xml 文件到 View 或者 Widget 的转化。

inflate() 有几种加载方式,我们常用的是如下两种:

  1. public View inflate (int resource, ViewGroup root)

  2. public View inflate (int resource, ViewGroup root, boolean attachToRoot)

所以我们就来介绍这两种方法,resource 这个参数因为我们之前说过 inflate() 加载 res/layout 下的 xml 布局文件,所以这个是 xml 布局文件的 id。

root 的英文解释是根,所以这个参数的值是 resource 的父 View。inflate() 这个方法就是将 resource 布局添加到 root 父布局里。而 attachToRoot 这个参数是控制是否添加布局,true 为添加,false 则不添加。

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

</LinearLayout>

item_btn.xml:

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/btn"
    android:text="inflate"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

</Button>
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LinearLayout layout = (LinearLayout) findViewById(R.id.ll);
        LayoutInflater inflater = LayoutInflater.from(this);
        inflater.inflate(R.layout.item_btn, layout);
    }
}

可以看到在主布局里加载了 item.xml。现在我们为 inflate() 方法加上 attachToRoot 这个参数并为它赋值 false。

这样就取消加载 item_btn.xml 了。这时候如果我们在下面有调用 addView() 方法再将 Button 添加,Button 就有会出现在界面上了。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LinearLayout layout = (LinearLayout) findViewById(R.id.ll);
        LayoutInflater inflater = LayoutInflater.from(this);
        Button button = (Button) inflater.inflate(R.layout.item_btn, layout, false);
        layout.addView(button);
    }
}

布局文件转换为 View,不都是为了放在组件里,显示在界面上吗?为何还要多此一举,分两步来做这样的事情呢?Android 中有些组件有其自身的“add View”机制,如:Fragment.onCreateView。

public class CustomFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater,
            @Nullable ViewGroup container, Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.layout_fragment, container, false);

        return view;
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FragmentManager fm = getFragmentManager();
        FragmentTransaction transaction = fm.beginTransaction();

        transaction.replace(R.id.ll, new CustomFragment());
        transaction.commit();
    }
}

“add View”的逻辑由 FragmentTransaction 进行处理了,如果我们这里将 attachToRoot 设置为true,就会有 IllegalStateException。所以组件自己有“add View”的机制,我们就不需要也不能再设置为 true啦。

4、设置root

大家可能会有疑问既然没有将创建的 View 添加到 root 中,为什么还要添加 root 参数呢?直接使用如下代码:

View view = inflater.inflate(R.layout.item_btn, null);

这样不是更简单吗?但如果这样写,lint 会给出警告 。xml 布局文件在解析成 View 的时候,需要 root 父布局的布局信息(View 的布局参数要受到 root 的限制)。如果不填写 root,使用 xml 布局文件生成 View 的时候就会使用默认的 LayoutParams。而这个不一定是符合要求的,View 可能比设定的要小。

有些时候无法明确的知道 View 的 root,当我们实例化一个 AlterDialog时:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LayoutInflater inflater = LayoutInflater.from(this);
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
        View customView = inflater.inflate(R.layout.item_btn, null);
        dialogBuilder.setView(customView);
        dialogBuilder.show();
    }
}

在这种情况下,传递 null 就可以了。通用的规则是,如果可以知道root,一定要传递这个值给参数。

综上所述,我们应当注意:

  1. 如果知道 root,一定要传,尽量避免传 null。
  2. 当不需要将布局文件生成的 View 添加到组件中时(组件有其自身的添加逻辑),attachToRoot 设置成 false。
  3. 当 View 已经添加到一个 root 中时,attachToRoot 设置成 false。
  4. 自定义组件应该将 attachToRoot 设置成true。

结束语:本文仅用来学习记录,参考查阅。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值