Android_Adapter,Spanner,listView及其优化

一、Spinner下拉选项框
xml代码:
Android_Adapter,Spanner,listView及其优化
java代码:

Android_Adapter,Spanner,listView及其优化

效果:
Android_Adapter,Spanner,listView及其优化

监听

Android_Adapter,Spanner,listView及其优化

在适配器中可以通过xml自定义样式:
Android_Adapter,Spanner,listView及其优化
添加:
Android_Adapter,Spanner,listView及其优化
效果:
Android_Adapter,Spanner,listView及其优化

二、ListView
(通过自定义适配器定义样式和加载条目)
Android_Adapter,Spanner,listView及其优化
Android_Adapter,Spanner,listView及其优化

布局xml代码:
Android_Adapter,Spanner,listView及其优化
自定义条目的xml文件:
Android_Adapter,Spanner,listView及其优化
java文件:
初始版本:
Android_Adapter,Spanner,listView及其优化
Android_Adapter,Spanner,listView及其优化

结果:
Android_Adapter,Spanner,listView及其优化
加入假数据:
Android_Adapter,Spanner,listView及其优化

Android_Adapter,Spanner,listView及其优化
Android_Adapter,Spanner,listView及其优化

结果:

Android_Adapter,Spanner,listView及其优化

!!!!优化!!!!
优化1:
Android_Adapter,Spanner,listView及其优化
根据以上代码可知,之所以能显示不同的条目是因为getView()方法执行了很多次position变了很多次,才会get出集合中不同的数组
我们知道View view = inflater.inflate(R.layout.items, parent, false)这句代码是new 出一个View对象赋值给view,既然执行多次那每次执行都会new 新对象,在方法中加入log日志打印地址如下:

Android_Adapter,Spanner,listView及其优化
Android_Adapter,Spanner,listView及其优化
根据界面和打印的日志可以看出在初次加载时,adapter并没有将30个条目全都初始化出来,仅仅是将界面显示出来的条目(露出一点也算)进行了初始化这是进行了内部优化,当继续下拉时如下图:
Android_Adapter,Spanner,listView及其优化
Android_Adapter,Spanner,listView及其优化

可以看出每次下拉一个就会初始化一个对象,但是再拉回去时打印如下:
Android_Adapter,Spanner,listView及其优化
同第一次比较:
Android_Adapter,Spanner,listView及其优化
可以看出虽然同时第0、1、2个条目但地址却不一样,当前面的条目被掩盖再次显示后,条目会重新执行getView方法。
那么问题来了:
并且重新执行View view = inflater.inflate(R.layout.items, parent, false)方法new对象,之前的对象将会变成垃圾等待回收。假设有上万的条目,拉一次将会产生上万个垃圾对象,可能会造出内存溢出的问题。
针对上述的问题我们应该进行优化:
1、首先为了能更好的利用对象,思路是:
将即将被隐藏条目中变成垃圾的对象,重新利用赋值给即将显示条目要new的对象
Android_Adapter,Spanner,listView及其优化
2、具体实现:
在覆写getView()方法时,有三个参数如下:

第一个参数是:当前条目数 第三个参数是:包含条目的父容器,此处为ListView
而第二个参数convertView就是被隐藏条目中即将变为垃圾的对象

当convertView为空时代表第一次加载没有垃圾,当不为空时代表有条目被隐藏,可以把即将new对象的条目View view = convertView 便实现了复用,实现如下:
Android_Adapter,Spanner,listView及其优化
日志效果如图:
第一次加载时的地址:
Android_Adapter,Spanner,listView及其优化
经过划动又再次划回的日志:
Android_Adapter,Spanner,listView及其优化
可以看出被隐藏的0,1,2,3的view地址与刚显示出来的8、9、10、11的view相同,实现了对象复用。
当再次拉回时:
Android_Adapter,Spanner,listView及其优化
8、9、10、11被隐藏其view的地址又被,新显示的0、1、2、3所复用

简化写法:
Android_Adapter,Spanner,listView及其优化
如果convertView为空,则new新对象,如果不为空则直接调用缓冲对象,这样就不用经过View,下面的代码调用的要是新对象,要么是缓冲对象

优化2:
代码:日志打印条目中的TextView
Android_Adapter,Spanner,listView及其优化
日志打印如下:
Android_Adapter,Spanner,listView及其优化
可知在此版本的手机中条目中的TextView打印的地址进行了优化,并没有出现无限new地址,而是实现了地址的复用
我们知道每次出现条目时都会重新调用getView()方法, TextView tv = (TextView) convertView.findViewById(R.id.tv); 也会产生新的对象,可能在旧的版本手机中就会出现类似优化一中的现象,导致内存溢出
针对可能出现的此类现象进行第二次优化:
Android_Adapter,Spanner,listView及其优化
在代码中 convertView 代表着整个条目的对象,而 tv 则是 convertView 条目中的一个TextView控件
优化思路:
每次在初始化convertView 顺带的把 tv 也封装到自己的条目convertView 中,下次取得时候就不用再次通过findViewById的方式new出TextView,即把条目封装自己的TextView对象
代码:
Android_Adapter,Spanner,listView及其优化
问题1:当在条目中再加入一个TextView
xml代码:
Android_Adapter,Spanner,listView及其优化
java代码:
首先convertView.setTag(tv);属性一次只能将一个对象放入口袋,也只能取出一个对象,现在有两个TextView需要封装,代码如下:
Android_Adapter,Spanner,listView及其优化
可知无法封装。
解决思路:
既然convertView.setTag();只能封装一个对象,那么我们可以把tv、tv2封装在一起,然后再通过setTag()方法打包,根据面向对象,可以把tv、tv2放入一个类中,代码如下:
①、创建一个类,用来封装tv、tv2
Android_Adapter,Spanner,listView及其优化
②、创建封装类,封装tv、tv2并放入口袋中
Android_Adapter,Spanner,listView及其优化
③、调用封装类的属性
Android_Adapter,Spanner,listView及其优化

这样便可以实现条目中两个属性既能实现对象复用,又可以同时显示对应的文本信息
结果:
Android_Adapter,Spanner,listView及其优化
问题2:
如下图显示:只选中了第0个条目

Android_Adapter,Spanner,listView及其优化

但是在下拉时发现,复用第0个条目的条目也被选中:

Android_Adapter,Spanner,listView及其优化
原因在于:第8个条目的对象是复用第0个条目的对象,所以第8个条目拥有第0个条目所有的设置和值。TextView文本没有被替换是因为每次调用都会被重新赋值
Android_Adapter,Spanner,listView及其优化
类似的CheckBox也可以封装并且重新赋值:
①、定义Map集合用来存放每个条目的CheckBox的状态(用Map防止重复)
Android_Adapter,Spanner,listView及其优化
键是Integer为条目的位置,值是Boolean为CheckBox的状态

②、封装CheckBox
Android_Adapter,Spanner,listView及其优化

Android_Adapter,Spanner,listView及其优化
③、添加逻辑
每次显示条目时,应该把每个条目的选中状态存在Map集合中,如何存?每次显示时可以把当前的条目放在CheckBox的口袋中,点击时,若选中择存true否则存false
ps:之所以不能在第②部封装里面执行,是因为封装数据只是在初始化时执行,这样存入口袋的仅仅是初始化的几个条目
Android_Adapter,Spanner,listView及其优化
在触发监听前需要把当前的条目位置position放进当前条目中checkbox的口袋中,如果点击则触发监听,在判断是否选中的同时把存入口袋的位置拿出来put在Map集合中,这样每个checkbox的状态都会被记录下来
④、设置状态
在改变条目时除了重新设置TextView 还要设置CheckBox要不然就会出现第一个选中,复用次对象的其他条目也会被选中。
设置的状态由Map集合中取,为空则代表所有条目的第一次加载,默认都为false;若不为空则取Map集合中的值而不是直接true!!!,因为在触发监听时可能是选中时触发,也可能是取消选中时触发,设置时应该与Map集合中的值为准!!!

Android_Adapter,Spanner,listView及其优化