Android LayoutInflater

一、LayoutInflater

参考
Android LayoutInflater详解
LayoutInflate的使用

在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而findViewById()是找xml布局文件下的具体widget控件(如Button、TextView等)。
具体作用:
1、对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来载入
2、对于一个已经载入的界面,就可以使用Activiyt.findViewById()方法来获得其中的界面元素。
LayoutInflater作用是将layout的xml布局文件实例化为View类对象。

setContentView()一旦调用, layout就会立刻显示UI;而inflate只会把Layout形成一个以view类实现成的对象,有需要时再用setContentView(view)显示出来。一般在activity中通过setContentView()将界面显示出来,但是如果在非activity中如何对控件布局设置操作了,这就需要LayoutInflater动态加载。


//setContentView(R.layout.main);

LayoutInflater inflate = LayoutInflater.from(this);
View view = inflate.inflate(R.layout.main,null);
setContentView(view);

LayoutInflater在Android中是“扩展”的意思,作用类似于findViewById(),不同的是LayoutInflater是用来获得布局文件对象的,而findViewById()是用来获得具体控件的。LayoutInflater经常在BaseAdapter的getView方法中用到,用来获取整个View并返回。

LayoutInflater 是一个抽象类,在文档中如下声明:
public abstract class LayoutInflater extends Object

获得 LayoutInflater 实例的三种方式:

  1. LayoutInflater inflater = getLayoutInflater();//调用Activity的getLayoutInflater()
  2. LayoutInflater inflater = LayoutInflater.from(context);
  3. LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    其实,这三种方式本质是相同的,从源码中可以看出这三种方式最终本质是都是调用的Context.getSystemService()
二、inflater

以下参考Layout Inflation不能这么用

现在我们来仔细看看Android框架关于动态载入布局的场景。

1.Adapter是最常用的场景,我们经常需要使用LayoutInflater来自定义ListView(通过重写getView()方法),具体的方法签名是这样的:
getView(int position, View convertView, ViewGroup parent)
2.Fragment也会用到inflation操作,通过onCreateView()方法创建view的时候会用到。这个方法的签名是这样的:
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

不知你有没有注意到这一点,每次Framework需要你去载入一个布局文件时,都会传入一个ViewGroup参数(最后需要绑定到的根视图)所以你想想看,如果我做绑定操作的话,为什么要给你一个ViewGroup参数呢?事实证明父视图在这个inflation操作过程中是很重要的,它会计算被载入的XML在根元素中的LayoutParams,如果传入null话,就等于是告诉框架“我不知道载入的View要放到哪个父视图中”。
问题在于,android:layout_xxx属性会在父视图对象中被重新计算,结果就是所有你定义的LayoutParams都会被忽略掉(因为没有已知的父视图对象)。然后你就纳闷“为什么框架会忽略掉我自己定义的布局属性呢?还是去StackOverFlow上看看,提一个bug吧”。
如果没有设置LayoutParams,那么最终ViewGroup也会给你生成一个默认的属性,幸运的话(很多时候),这些默认的设置正好和你在XML文件中定义的一样……所以你就察觉不到其实已经出现问题了。

以下参考Android LayoutInflater深度解析 给你带来全新的认识

ListView的Item的布局文件:



ListView的适配器:


public View getView(int position, View convertView, ViewGroup parent)
{

    ViewHolder holder = null;  
    if (convertView == null)  
    {  
        holder = new ViewHolder();  
        convertView = mInflater.inflate(R.layout.item, null);  

// convertView = mInflater.inflate(R.layout.item, parent ,false);
// convertView = mInflater.inflate(R.layout.item, parent ,true);
holder.mBtn = (Button) convertView.findViewById(R.id.id_btn);
convertView.setTag(holder);
} else
{
holder = (ViewHolder) convertView.getTag();
}

    holder.mBtn.setText(mDatas.get(position));  

    return convertView;  
}  

Android LayoutInflater_第1张图片
图1
Android LayoutInflater_第2张图片
图2

图3直接报错


FATAL EXCEPTION: main
java.lang.UnsupportedOperationException:
addView(View, LayoutParams) is not supported in AdapterView

由上面getView三行代码的变化,产生3个不同的结果,可以看到
inflater(resId, null )的确不能正确处理宽高的值,但是inflater(resId,parent,false)并非和inflater(resId, null )效果一致,它可以看出完美的显示了宽和高。而inflater(resId,parent,true)报错了

分析源码已经可以看出:
Inflate(resId , null ) 只创建temp ,返回temp
Inflate(resId , parent, false )创建temp,然后执行temp.setLayoutParams(params);返回temp
Inflate(resId , parent, true ) 创建temp,然后执行root.addView(temp, params);最后返回root
由上面已经能够解释:
Inflate(resId , null )不能正确处理宽和高是因为:layout_width,layout_height是相对了父级设置的,必须与父级LayoutParams一致。而此temp的getLayoutParams为null
Inflate(resId , parent,false ) 可以正确处理,因为temp.setLayoutParams(params);这个params正是root.generateLayoutParams(attrs);得到的。
Inflate(resId , parent,true )不仅能够正确的处理,而且已经把resId这个view加入到了parent,并且返回的是parent,和以上两者返回值有绝对的区别。
图3中的报错是因为源码中调用了root.addView(temp, params);而此时的root是我们的ListView,ListView为AdapterView的子类:直接看AdapterView的源码:


@Override
public void addView(View child) {
throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
}

可以看到这个错误为啥产生了。

你可能感兴趣的:(Android LayoutInflater)