布局加载器——LayoutInflate

1 LayoutInflate基本使用

1.1 LayoutInflate使用场景

LayoutInflate是什么?

答:一个用于加载布局的系统服务,就是实例化与Layout XML文件对应的View对象,称之为布局加载器,不能直接使用, 需要通过getLayoutInflater( )方法或getSystemService( )方法来获得与当前Context绑定的 LayoutInflater实例!

在介绍LayoutInflate使用,可以回顾一下哪些地方用过LayoutInflate:

  • 在Activity中
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(R.layout.activity_main, null);
  • 在Fragment中
        View view = inflater.inflate(R.layout.fragment_guide_one, container, false);
        return view;
  • 在Adapter中
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = LayoutInflater.from(convertView.getContext()).inflate(R.layout.activity_main, parent, false);
        return view;
    }
  • 某些特殊情况下,需要使用LayoutInflate
        LayoutInflater inflater  =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

这些使用场景都有一个共同的特点:需要将一个XML布局文件转化为View,LayoutInflate的主要功能就是将XML文件实例化为相应的Veiw对象。

1.2 LayoutInflate简单使用

关于LayoutInflate的简单使用流程如下:

  1. 获取LayoutInflate的实例
  2. 调用加载布局的方法
  3. 通过LayoutInflate.LayoutParams设置相关属性

LayoutInflate本身是一个抽象类,不可以直接通过new的方式去获得它的实例,主要有以下三种方法:

LayoutInflater inflater1 = LayoutInflater.from(context); 
LayoutInflater inflater2 = getLayoutInflater();  // 在Activity中调用
LayoutInflater inflater3 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); 

三种方法的底层实现其实一样

这里看一下前两种方法的底层实现:

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;
}

在Activity内部调用getLayoutInflater方法,其实是调用PhoneWindow的mLayoutInflate:

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

所以,这几个方法实际上殊途同归,都是通过调用Context的getSystemService方法去获取。获取到的是PhoneLayoutInflater这个实现类,具体的获取过程就不在这里展开分析了。

public class Policy implements IPolicy {
    ...
    public LayoutInflater makeNewLayoutInflater(Context context) {
        return new PhoneLayoutInflater(context);
    }
}  

关于加载布局的方法

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

该方法的三个参数依次是:

  • resource:要加载的布局对应的资源ID
  • root:为该布局的外部在嵌套一层父布局,如不需要,填写null即可
  • attachToRoot:是否为加载的布局文件的最外层嵌套一层root布局。root不为null,默认为true,如果为false,则root失去作用,如果root为null,attachToRoot无作用。

除了上述的加载布局方法,还有以下重载方法:

inflate(int resource, ViewGroup root);

inflate(XmlPullParser parser, ViewGroup root);

inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)

加载布局完,通过LayoutInflate.LayoutParams设置相关属性

1.3 纯Java代码加载布局

特定情况下,需要使用Java代码往布局中动态添加组件或布局

加载布局流程

  • 创建容器,例如:
LinearLayout ly = new LinearILayout(this);
  • 创建组件
Button btnOne = new Button(this);
  • 为容器或组件设置属性:例如LinearLayout,可以设置组件排列方向:
ly.setOrientation(LinearLayout.VERTICAL);
  • 将组件或容器添加到容器中:
    这时候可能需要设置组件的添加位置或者大小,需要用到:LayoutParams类,可以看作时布局容器的一个信息包,封装位置与大小等信息的一个类。例如设置大小:
	LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams( 
        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

设置位置,一般考虑RelativeLayout,用到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);

简单布局加载示例:

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);  
    }  
}  

备注:前述的setId()方法会报错

方案一:通过调用View.generateViewId()作为setId的参数,但此方案不是最佳方案,因为View.generateViewId()方法必须为SDK版本17及以上才行,否则报错。

方案二:在res/values/下添加id.xml(名字可随意)文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="my_view" type="id" />
</resources>

然后在代码中做如下设置即可:

my_view.setId(R.id.my_view);
  • 最后调用setContentView()方法加载布局对象,移除组件可以调用容器.removeView(要移除的组件)

1.4 Java动态添加控件或XML布局

在实际中更多时候时动态地添加View控件或XML布局


动态增加View

动态添加组件的写法有两种,区别在于是否需要先setContentView(R.layout.activity_main)

假设布局文件为:

<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()即可!


动态加载XML布局

  • 假定主布局文件和动态加载的布局文件如下:
    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" >
        <Button
            android:id="@+id/btnLoad"
            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动态加载XML布局:

  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.btnLoad);  
          btnLoad.setOnClickListener(new 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(    
                          LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);    
                  lp.addRule(RelativeLayout.CENTER_IN_PARENT);    
                  rly.addView(ly,lp);  
              }  
          });  
      }  
  }

①获取容器对象:

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);

猜你喜欢

转载自blog.csdn.net/weixin_43499030/article/details/89462246