对项目中的代码进行优化,删除Layout中过度绘制的代码,无意中发现了一个问题,ListView中的item的高度设置后没有效果,listview算是Android开发中最常用的控件之一,一直都在用,但是却没有遇到过item中设置无效,找到解决办法后,意识到自己对平时调用的方法参数不够理解。
问题重现
原来的的xml布局
1 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
不管怎样修改RelativeLayout中height中的值,运行后的item高度,一直保持不变,为32dp。
解决办法
通过内层控件去控制去控制整个item的高度,修改如下
1
2
3
4
5
6
7<ImageView
android:id="@+id/iv_title"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_centerVertical="true"/>为RelativeLayout设置最小高度,添加属性
1
android:minHeight="48dp"
虽然可以解决该问题,但是总觉得指标不治本,如果遇到xml中嵌套多个控件,那么这种上面两种解法就难以实施了。再继续查找解决办法,原来Adapter中用的是
1
LayoutInflater.from(mAct).inflate(layoutId,null);
改为如下则可以解决
1
LayoutInflater.from(mAct).inflate(layoutId,parent,false);
为啥参数不同就导致结果不同呢?看下面的详细分析
LayoutInflater.inflate()的分析
简介
Instantiates a layout XML file into its corresponding {@link android.view.View}
* objects.
实例化一个XML布局文件到相应的View对象中
使用
1 | LayoutInflater layoutInflater = LayoutInflater.from(mContext); |
It is never used directly. Instead, use
* {@link android.app.Activity#getLayoutInflater()} or
* {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
从注释可以看出,不直接创建对象,而是通过去取回一个实例,看from(mContext)的实现可以发现最终也是通过getSystemService获取
1 | public static LayoutInflater from(Context context) { |
inflate()源码分析
inflate()有四个重载的方法,第一二个方法把resource解析为XmlResourceParser
1 | public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { |
最后都调用了第四个方法
1 | public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { |
从上面的代码可以看出,先判断是否为merge标签,如是的话,root不能为空或者attachToRoot不能为false,否则会抛出异常。
不为merge标签,获取xml中的根布局,如果root不为空,则生成xml中的属性params,如果attachToRoot为false,则设置到xml的根布局中,此时属性值有效。
如果attachToRoot为true,则把xml中的根布局以及属性附到root中,此时属性值同样有效。
1 | if (root == null || !attachToRoot) { |
最后,返回view,只有root不为空且attachToRoot为true,才返回root,否则都返回xml的根布局。
可以知道,listView中的getView是返回子item,为了让属性值有效,应该改为
1 | convertView = LayoutInflater.from(mContext).inflate(R.layout.item_listtest, parent,false); |
如果最后的attachToRoot参数为true,那么会发生崩溃,日志如下,因为getView拿到item后,还再执行addView。
1 | Caused by: java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView |
如果想在一个界面中,想让rootView中添加一个View,让其属性有效,那么则改为
1 | convertView = LayoutInflater.from(mContext).inflate(R.layout.item_listtest, parent, true); |
总结
返回布局
- 如果root不为null,attachToRoot为true,xml被addView到root中,返回root布局
- 如果root为null,或者attachToRoot为false,返回xml布局
属性值
- 如果root为null,属性值无效,因为属性值需要root布局去
root.generateLayoutParams()
,其他情况,属性值有效