LayoutInflater有三种加载方法,分别是inflate(layoutId, null ),inflate(layoutId, root, false ) ,inflate(layoutId, root, true ).对于这三种方法加载有什么区别呢?下面我们用一个listview去看一下,新建一个listview,里面只有一个button:
Activity的布局文件:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/id_listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</ListView>
Listview的Item文件:
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/id_btn"
android:layout_width="120dp"
android:layout_height="120dp" >
</Button>
Listview的适配器:
<pre name="code" class="java">public class MyAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
private List<String> mDatas;
public MyAdapter(Context context, List<String> datas)
{
mInflater = LayoutInflater.from(context);
mDatas = datas;
}
@Override
public int getCount()
{
return mDatas.size();
}
@Override
public Object getItem(int position)
{
return mDatas.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
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;
}
private final class ViewHolder
{
Button mBtn;
}
}
主Activity:
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
public class MainActivity extends Activity
{
private ListView mListView;
private MyAdapter mAdapter;
private List<String> mDatas = Arrays.asList("Hello", "Java", "Android");
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.id_listview);
mAdapter = new MyAdapter(this, mDatas);
mListView.setAdapter(mAdapter);
}
}
我们主要关注getView里面的inflate那行代码:下面我依次把getView里的写成:
1、convertView = mInflater.inflate(R.layout.item, null);
2、convertView = mInflater.inflate(R.layout.item, parent ,false);
3、convertView = mInflater.inflate(R.layout.item, parent ,true);
第一个方法加载后,我们会发现listView中的item设置的宽高无效,显示的item为wrap_content的
第二个方法加载后,我们会发现listView中的item设置的宽高得到了正确的执行,显示的item为我们设置的宽高
第三个方法加载后,会报“addView(View) is not supported in AdapterView”错误。
然后观察源码我们会发现:
对于第一种加载方法,没有为其设置相应于父布局的layoutParams所以无法显示正常的宽高
对于第二种加载方法,为其设置了相应于父布局的layoutParams,所以可以正常显示item制定的宽高
对于第三种加载方法,会调用root.addview()方法,而adapterview是不支持addview()的,所以会抛出异常
那么为什么设置layoutParams就无法显示正常的宽高呢?我们从viewgroup和view的onmeasure()方法解析:
ViewGroup的onMeasure方法所做的是:
为childView设置测量模式和测量出来的值。
如何设置呢?就是根据LayoutParams。
如果childView的宽为:LayoutParams. MATCH_PARENT,则设置模式为MeasureSpec.EXACTLY,且为childView计算宽度如果childView的宽为:固定值(即大于0),则设置模式为MeasureSpec.EXACTLY,且将lp.width直接作为childView的宽度如果childView的宽为:LayoutParams. WRAP_CONTENT,则设置模式为:MeasureSpec.AT_MOST
View的onMeasure方法:
主要做的就是根据ViewGroup传入的测量模式和测量值,计算自己应该的宽和高:
一般是这样的流程:
如果宽的模式是AT_MOST:则自己计算宽的值。
如果宽的模式是EXACTLY:则直接使用MeasureSpec.getSize(widthMeasureSpec);