前言:
由于ScrollView和ListView共存的时候,由于项目中是用的自定义的ListView,通过又不能更换ListView,只能用公司框架的,所以我是通过计算ListView每个Item的高度来计算ListView的高度来设定ListView的高度。这样在ListView就能在ScrollView中正常显示了。但是由于这样把所有的Item都计算出来,这样的ListView就相当于一个Linearlayout包裹了一些Item一样的效果,你在界面滑动的并不是ListView 而是ScrollView,失去了ListView原有的回收机制。
下面是我计算ListView高度的代码:
/** 计算并设置listview的高度 */
public static void setListViewHeight(XListView listView, Context context) {
if (listView == null) {
return;
}
int totalHeight = 0;
for (int i = 1; i < listView.getAdapter().getCount(); i++) {
View listItem = listView.getAdapter().getView(i, null, listView);
listItem.measure(0, 0);//这里通过源码可以看到调用了item的onMeasure()通过把MeasureSpec传进去
int imageHeight = context.getResources().getDimensionPixelOffset(R.dimen.item_image_height);
int itemHeight = listItem.getMeasuredHeight();
if(listItem.getTag().equals("1")){
if(imageHeight > itemHeight){
totalHeight += imageHeight;
}else{
totalHeight += itemHeight;
}
}else if(listItem.getTag().equals("0")){
totalHeight += itemHeight;
}
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listView.getAdapter()
.getCount() - 1));
listView.setLayoutParams(params);
}
会通知父View来调用他的onMeasure()和onLayout()来测量他的宽高以及设置他位置。
public void setLayoutParams(ViewGroup.LayoutParams params) {
if (params == null) {
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
}
requestLayout();
}
而我在getView中又开启了线程,来获取服务端的图片(由于服务端没写小图接口,我直接拿到的是大图,他传过来的仅仅是base64的字符串,所以这样也不会OOM),当我通过缓存,以及根据我item中的ImageView的宽高来压缩图片显示到ListView中的时候, 发现了一个奇怪的问题,就是有些Item的图片并没有显示出来。
刚开始还以为是因为在滑动的时候由于ListView的回收机制,RecycleBin导致被回收的Item中请求的图片占用了新滑进来的Item的ImageView。相信这个都有了解,我就不做详细的详述了,所以我通过在getView()中为ImageView设置Tag来解决这个问题:
ImageView saryIMG = (ImageView) view.findViewById(R.id.image);
saryIMG.setTag(position+"image");
后来经过测试还是不行,通过观察ListView的源码,以及在后台打印的log才知道问题出现在哪。
由于android的机制,View的绘制是由OnMeasure----OnLayout-----OnDraw这个流程的,而这个流程一般情况会最少会执行2次,由于我们这是计算ListView的高度来解决与ScrollView共存显示的冲突的,所以ListView中的每个Item的都会绘制出来,而ListView中的RecycleBin的这个回收机制相当于是不存在了,同时ListView在进行2次Layout的时候(这里我们假设是两次,因为超过2次之后的流程与第二次是差不多的),都会通过getView()这个方法来关联item的View(ListView的源码我这就不做详细分析了,如果想了解的朋友,可以通过官方文档了解),而项目中的图片是在getView()方法中开启线程来获取图片的,所以分别在开启线程之前打印了一次Log:
这是在开启线程前的Log
可以根据上图得知,一共进行了10次getView()操作(服务端数据的5条),
在开启线程后,拿到图片我又进行了一次Log来判断到底是哪几个item获得了图片
以下是我在请求服务端拿到图片成功之后打印的Log:
根据上面Log日志可以得出,少了两次,由此可以得出结论,由于拿到的数据过大,或者说是网络原因有2条线程是卡死在那,导致ANR,相信这个都不会陌生,所以两条item没有获取到图片信息,所以才会导致图片不显示。(这里我也通过了线程池来控制核心线程的数量,但是同样有时候还是会获取不到图片,在网络请求的时候超过5秒,导致请求超时造成ANR);所以这里的解决方案是要求服务端重新弄过一个小图接口,来解决这个ANR的问题,由于大图的数据大,(这里在没有转成bitmap的时候同样还是很大,因为我们的listview中的item不可能是1条),今天服务端也终于把小图接口弄好了,经过测试发现没问题,所以在listview中的item如果有图片的话尽量还是要求服务端压缩,毕竟手机还是没有PC端那么给力。
总结:
android的一些源码是很重要的,能避免我们在一些地方造成不必要的问题和困扰,只有了解其中的运行机制,才能够很好的解决问题,同时也能提升自己。当然,由于项目中的item是非常少的,顶多都不超过5条左右,所以刚开始的时候才没要求服务端弄成小图,才导致这样的问题,哈哈,希望大家别像我这样2B,该怎么做还是怎么做,毕竟ListView在一般的情况下,如果item中有图片的话都是经过服务端压缩之后再发过来的。不要以为自己是节省了开发时间,可能因为一时的偷懒,导致一些问题浪费了更多的时间,我祝大家能尽快解决项目中遇到和我一样的问题。