前言:
对于RecyclerView,在Adapter中为item中的一个View设置点击事件,图方便的写法是使用OnClickListener的匿名内部类,因为有IDE的自动补全功能,啪的一下好几行代码就出来了
@Override
public void onBindViewHolder (ViewHolder holder,int position){
holder.testBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("onClick", "testBtn" + String.valueOf(position));
}
});
}
可是这样的方式就导致列表在滚动的时候,生成大量多余的OnClickListener对象,因为列表的循环机制虽然让item复用了,但是却每次都生成了新的OnClickListener对象。这会导致性能浪费甚至掉帧,GC频次增加甚至卡顿。
改进:
Google搜到的优化方式大多是这样的:
复用OnClickListener对象,然后在onBindViewHolder()方法中通过setTag(position) 和getTag() 的方式,来传递点击事件的position给listener。
public class TestAdapter extends RecyclerView.Adapter implements View.OnClickListener{
...
@Override
public void onBindViewHolder(final Holder holder, final int position) {
holder.itemView.setOnClickListener(this);
holder.itemView.setTag(position);
...
}
@Override
public void onClick(View v) {
int position = (Integer) v.getTag();
Log.d("onClick", "testBtn" + String.valueOf(position));
}
}
这样的方式其实也还可以,不过如果开发者在某个adapter中忘了执行setTag(position) ,AS是不会给任何提示的,而且也能正常展示界面,直到用户点击时才会出现顺序错乱。这种方式就要完全靠开发者记性和测试人员的严谨,不过这种靠人工的方法觉得不太工程化,于是琢磨用更科学的方式进行优化。
我的方式:
我觉得ViewHolder作为OnClickListener是比较合适的。然后ViewHolder是继承了RecyclerView.ViewHolder的,阅读该类源码,在里面搜搜position关键字,确实发现了几个现成的方法:
RecyclerView.ViewHolder的代码
有那么三个。getPosition(),在adapter的数据更新时,会因为异步处理而令返回的值有歧义,所以用@Deprecated注解标记为不推荐使用。剩下getLayoutPosition()和getAdapterPosition(),只看方法名的话感觉getAdapterPosition()方法靠谱,Google一下,的确如此,(参考:https://stackoverflow.com/questions/29684154/recyclerview-viewholder-getlayoutposition-vs-getadapterposition)
所以,我优化的方式整理如下,以比较复杂的item做例子:
public class TestAdapter extends RecyclerView.Adapter {
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.testBtn.setOnClickListener(holder);
holder.testTv.setOnClickListener(holder);
holder.testImage.setOnClickListener(holder);
holder.itemView.setOnClickListener(holder);
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@Override
public void onClick(View v) {
int wrapperPosition = getAdapterPosition();
//如果你项目中的RecyclerView是用常规的方式封装成可以添加header的话,那么要减去header的数量
//int wrapperPosition = getAdapterPosition() - headersCount;
switch (v.getId()) {
case R.id.test_btn:
//todo
break;
case R.id.test_tv:
//todo
break;
case R.id.test_image:
//todo
break;
}
if (itemView == v) {//itemView的判断
//todo
}
}
}
}
网友评论