在逛知乎的时候看见了这个提问,里面大神的回答给了我很多很有用的帮助,所以转来博客里,以防以后找不到。如有侵权,请通知我删除。
原文章地址:http://www.zhihu.com/question/19703384
/*********************************SamuraiSong,阿里巴巴移动安全Android开发的回答start*****************************************************************/
有一个小细节,很多开发人员都没有注意过
比如你的Item中有三个按钮,你要为三个按钮分别定义点击事件,如何定义?
也许你会在getView中这样做
button1.setOnclickListener(new View.OnClickListener() {
@override
public void onClick(View v) {
//balabalabala...
}
});
button2.setOnclickListener(new View.OnClickListener() {
@override
public void onClick(View v) {
//balabalabala...
}
});
button3.setOnclickListener(new View.OnClickListener() {
@override
public void onClick(View v) {
//balabalabala...
}
});
如果你每屏显示7个Item,你一共创建了21个listener对象在内存中,如果View回收不畅,会更多,这样,在滚动的时候频繁GC 就会导致卡顿(这里描述有误,请看7月25日更新)
如果你在Adapter初始化的时候创建一个Listener
public MyAdapter () {
myListener = new View.OnClickListener() {
@override
public void onClick(View v) {
v.getTag()
v.getId()
//balabalabala...
}
});
}
通过传入的View v这个参数判断是哪一个button被点击,这样,无论View如何创建,你只创建了1个Listener对象
这只是一个小细节,优化的方式要综合使用,才会事半功倍
------------------7月24日更-----------------
v.getTag() 这个tag本不是用来存数据的,通俗点讲它和view 的Id是同一个东西,只不过tag的类型是Object。实际上在tag中存储数据是不符合规范的方式
但其实View类有两种tag,
setTag(Object tag) 方法将tag保存在一个成员变量中,findViewWithTag正是遍历此tag
setTag(int key, Object tag) 方法是最终是调用View类中的setKeyedTag(int key, Object tag)
这是一个私有方法
他是用 SparseArray 实现的,我们可以把需要的东西存到这里面(其实观察源码可以发现,系统很多时候都是这样做的)
这里要注意一点,参数key必须是唯一的,那么我们可以这样做
那么需要先在res/values/strings.xml中添加
<resources>
<item type="id" name="tag_first"></item>
<item type="id" name="tag_second"></item>
</resources>
使用的时候写成
view.setTag(R.id.tag_first, obj1);
view.setTag(R.id.tag_second, obj2);
-------------------------关于如何绑定数据的问题-----------------
有人问,如果这样写,所有button只能通过id区分逻辑,无法传入每个item的数据
我们可以将数据通过view 的tag带进来
public View getView(.....) {
....
v.setTag(key, getItem(position));
....
}
然后在listener中通过v.getTag()将数据取出
-----------------------7月25日update-------------------
这里我有一个失误
如果listener里面的逻辑与当前的item有关,那么其实并不只是创建了21个listener对象
public void getView (View convertView ,final int position ....) {
if (convertView == null) {
View v = LayoutInflater.from(mContext).inflate(...);
v.setOnclickListener(new View.OnClickListener () {
@override
public void onClick(View v) {
getItem(position);
}
});
} else {
}
}
你看下,如果绑定数据在convertView为空的情况下确实只创建了有限个listener,
但是在这种情况下绑定上的数据只有View创建时的7个,之后不为空的情况下没有更换listener导致重用的view数据是新的,listener里面的position依然是过去创建view时的7个之一,不会变化(注意参数上的final)
若在else里面再重新setListener,view是有重用,listener被换成新的,并与新的position绑定,老的listener就会变成一个废对象,等待gc回收,随着list滚动,越来越多
关键是我们的业务与position这个参数有关
不知道这回我表述清楚了没有
/**************************************SamuraiSong,阿里巴巴移动安全Android开发的回答end**************************************************************************/
/**************************************吴晶,Android软件工程师+图像处理的回答start*************************************************************************************/
为了避免大家误会这个回答只是老生常谈 ListView 的重用机制,编辑一下。
我这里说一下我用 ListView 的一些经验,为了尽量说的全面一些,这里列一些 Tips,具体的代码可以找相关的文章,或者一起交流:
写的有点散,有些也是相互穿插的。这里面提到的都是一些原则,详细的解决方案,每个都能在网上找到相关的文章。
/**************************************吴晶,Android软件工程师+图像处理的回答end*************************************************************************************/