Android LayoutInflater 布局服务
LayoutInflater ( 布局服务 ) 使用 Android 内置的 Pull 解析器来解析布局,一般用于动态加载布局或者添加控件
平时我们创建一个布局的 xml,然后调用 Activity 的 setContentView()
加载布局,然后显示
到屏幕上的过程,底层使用的就是 LayoutInflater
LayoutInflater
LayoutInflater
一个用于加载布局的系统服务,就是实例化与 Layout XML 文件对应的 View 对象
LayoutInflater
不能直接使用,需要通过 getLayoutInflater()
方法或 getSystemService()
方法获得与当前 Context 绑定的 LayoutInflater 实例
LayoutInflater 的方法
-
获取 LayoutInflater 实例的三种方法
LayoutInflater inflater1 = LayoutInflater.from(this); LayoutInflater inflater2 = getLayoutInflater(); LayoutInflater inflater3 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
后面两个其实底层都会调用第一种方法
-
加载布局的方法
public View inflate(int resource, ViewGroup root, boolean attachToRoot)
参数说明
参数 说明 resource 要加载的布局对应的资源 id root 为该布局的外部再嵌套一层父布局,如果不需要的话,设置 null attachToRoot 是否为加载的布局文件的最外层套一层 root 布局 对于参数
attachToRoot
不设置该参数的话- 如果 root 不为 null,则默认为 true
- 如果 root 为 null,attachToRoot 就没有作用了
- root不为 null,attachToRoot 为 true 的情况下,会在加载的布局文件最外层嵌套一层 root布局
- root不为 null,attachToRoot 为 false 的情况下 则root 失去作用
简单的说,就是 是否为加载的布局添加一个root的外层容器
-
通过 LayoutInflater.LayoutParams 来设置相关的属性
RelativeLayout 还可以通过 addRule() 方法添加规则,就是设置位置:是参考父容器呢?还是参考子控件?又或者设置 margin 等等
纯 Java 代码加载布局
平时我们都使用 XML 生成我们需要的布局,但在某些特定的情况下,则需要使用 Java 代码往我们的布局中动态的添加组件或者布局
注意: 我们不推荐大家完全地使用 Java 代码来编写 Android 页面布局,首先一点就是代码会多, 一多久容易乱,而且不利于业务的分离,我们还是建议使用 xml 来完成布局,然后通过 Java 代码对里面的组件进行修改,当然有些时候可能需要使用 Java 动态的来添加组件
使用 LayoutInflater 纯 Java 代码加载布局的流程一般如下
-
创建容器
LinearLayout ly = new LinearLayout(this);
-
创建组件
Button btnOne = new Button(this);
-
为容器或者组件设置相关属性
比如可以设置组件 LinearLayout 的排列方向
ly.setOrientation(LinearLayout.VERTICAL);
或者设置某个控件,比如 Button
btnOne.setText("按钮1");
-
将组件或容器添加到容器中
我们可能需要设置下组件的添加位置,或者设置他的大小
我们需要用到一个类:LayoutParams,可以看成布局容器的一个信息包,封装位置与大小等信息的一个类
比如设置 LinearLayout 大小的方法
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
很简单,接着就到这个设置位置了,设置位置的话,通常我们考虑的只是 RelativeLayout
需要用到
LayoutParams.addRule()
方法如果需要设置组件在父容器中的位置,比如 设置组件的对其方式
RelativeLayout rly = new RelativeLayout(this); RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); Button btnOne = new Button(this); rly.addView(btnOne, lp2);
参照其它组件的对其方式
比如设置 btnOne 居中后,让 BtnTwo 位于 btnOne 的下方以及父容器的右边
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); RelativeLayout rly = new RelativeLayout(this); Button btnOne = new Button(this); btnOne.setText("按钮1"); Button btnTwo = new Button(this); btnTwo.setText("按钮2"); // 为按钮1设置一个id值 btnOne.setId(123); // 设置按钮1的位置,在父容器中居中 RelativeLayout.LayoutParams rlp1 = new RelativeLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); rlp1.addRule(RelativeLayout.CENTER_IN_PARENT); // 设置按钮2的位置,在按钮1的下方,并且对齐父容器右面 RelativeLayout.LayoutParams rlp2 = new RelativeLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); rlp2.addRule(RelativeLayout.BELOW, 123); rlp2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); // 将组件添加到外部容器中 rly.addView(btnTwo, rlp2); rly.addView(btnOne, rlp1); // 设置当前视图加载的View即rly setContentView(rly); } }
-
调用 setContentView() 方法加载布局对象即可
如果你想移除某个容器中的 View,可以调用容器. removeView (要移除的组件);
Java 代码动态添加控件
动态添加组件的写法有两种,区别在于是否需要先调用
setContentView(R.layout.activity_main);
下面的范例演示了两种不同写法添加一个 Button
首先修改布局文件 activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/txtTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="我是xml文件加载的布局"/> </RelativeLayout>
第一种方法是不需要 setContentView() 加载布局文件先
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button btnOne = new Button(this); btnOne.setText("我是动态添加的按钮"); RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp2.addRule(RelativeLayout.CENTER_IN_PARENT); LayoutInflater inflater = LayoutInflater.from(this); RelativeLayout rly = (RelativeLayout) inflater.inflate( R.layout.activity_main, null) .findViewById(R.id.RelativeLayout1); rly.addView(btnOne,lp2); setContentView(rly); } }
第二种是不需要 setContentView() 加载布局文件先
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnOne = new Button(this); btnOne.setText("我是动态添加的按钮"); RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp2.addRule(RelativeLayout.CENTER_IN_PARENT); RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1); rly.addView(btnOne,lp2); } }
两种方式的代码都很简单
在创建按钮后,然后创建一个 LayoutParams 对象,用来设置 Button 的大小, 又通过 addRule() 方法设置了 Button 的位置
-
第一种方法,通过 LayoutInflate 的 inflate() 方法加载了 activity_main 布局,获得了外层容器,接着 addView() 添加按钮进容器,最后 setContentView();
-
第二种方法中,因为我们已经通过 setContetView() 方法加载了布局,所以可以直接通过 findViewById() 找到这个外层容器,接着 addView(),最后 setContentView()
注意, setContentView() 设置的视图节点是整个 XML 的根节点
使用 LayoutInflater 动态加载 XML 布局的步骤
-
获取容器对象
final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);
-
获得 Inflater 对象,同时加载被添加的布局的 xml,通过 findViewById 找到最外层的根节点
final LayoutInflater inflater = LayoutInflater.from(this); LinearLayout ly = (LinearLayout) inflater.inflate(R.layout.inflate, null, false).findViewById(R.id.ly_inflate);
-
为这个容器设置大小与位置信息
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.addRule(RelativeLayout.CENTER_IN_PARENT);
-
添加到外层容器中
rly.addView(ly,lp);
范例
我们写一个 demo,动态地添加 xml 文件
-
创建一个 空的 Android 项目
cn.twle.android.LayoutInflater
-
修改 activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/btn_load" android:layout_marginTop="100dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="动态加载布局"/> </RelativeLayout>
-
创建一个布局文件 inflate.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:gravity="center" android:orientation="vertical" android:id="@+id/ly_inflate" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我是 Java 代码加载的布局" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我是布局里的一个小按钮" /> </LinearLayout>
-
修改 MainActivity.java 动态加载 xml 布局
package cn.twle.android.layoutinflater; import android.app.Activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.RelativeLayout; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获得LayoutInflater对象; final LayoutInflater inflater = LayoutInflater.from(this); //获得外部容器对象 final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1); Button btnLoad = (Button) findViewById(R.id.btn_load); btnLoad.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //加载要添加的布局对象 LinearLayout ly = (LinearLayout) inflater.inflate( R.layout.inflate, null, false).findViewById( R.id.ly_inflate); //设置加载布局的大小与位置 RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); lp.addRule(RelativeLayout.CENTER_IN_PARENT); rly.addView(ly,lp); } }); } }