通过计算ListView的高度导致ListView失去它原有回收机制,同时造成ANR

前言:

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


通过上面的代码可以看到listView.setLayoutParams(params);这句代码,而这里最终会调用requestLayout()

会通知父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中有图片的话都是经过服务端压缩之后再发过来的。不要以为自己是节省了开发时间,可能因为一时的偷懒,导致一些问题浪费了更多的时间,我祝大家能尽快解决项目中遇到和我一样的问题。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值