事情源于我们正在写的一个app,效果图:
整个界面搭建的就是一个listview,内部的item有gridview 有viewpager等等。当小伙伴写好的时候就出现了listview滑动卡顿的情况。
网上百度了解决办法:
1..Adapter的getView方法里面convertView没有使用setTag和getTag方式;
2.在getView方法里面ViewHolder初始化后的赋值或者是多个控件的显示状态和背景的显示没有优化好,抑或是里面含有复杂的计算和耗时操作;
3.在getView方法里面 inflate的row 嵌套太深(布局过于复杂)或者是布局里面有大图片或者背景所致;
4.Adapter多余或者不合理的notifySetDataChanged;
5.listview 被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为fill_parent. 如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都是fill_parent的;
但是这并没有完全的解决我的办法。所以就从以下分析了;
1.数据解析:
把getview中所有要进行json解析的数据都解析好了再放进去。
2.Viewholder优化:
出于偷懒,我们之前用的万能viewholder
public class ViewHolder {
@SuppressWarnings("unchecked")
public static <T extends View> T get(View convertView, int id) {
SparseArray<View> viewHolder = (SparseArray<View>) convertView.getTag();
if (viewHolder == null) {
viewHolder = new SparseArray<View>();
convertView.setTag(viewHolder);
}
View childView = viewHolder.get(id);
if (childView == null) {
childView = convertView.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}
}
当我再viewholder==null 下面打断点的时候,发现界面加载后都会过这个断点,故有的viewholder没有真正的settag和gettag;所有就对viewholder进行优化,老办法,listview 里面多创建几个holder 我们是十多个不同的viewholder;
int templantCode = getItemViewType(i);
Holder1 holder1 = null;
Holder2 holder2 = null;
Holder3 holder3 = null;
Holder4 holder4 = null;
Holder5 holder5 = null;
Holder6 holder6 = null;
Holder7 holder7 = null;
Holder8 holder8 = null;
Holder9 holder9 = null;
Holder10 holder10 = null;
3.重写以下方法
getViewTypeCount
getItemViewType
4.getview中settag的优化(真正卡顿的所在):
以往的规矩是我们在
if(view == null){这儿里面进行控件的初始化而在最外面进行控件的赋值。比如:
if(view == null){ view = LayoutInflater.from(context).inflate(R.layout.viewholder1, null); holder4 = new Holder4(view); view.setTag(holder4); }else{ holder4 = (Holder4) view.getTag(); } holder4.tvType3.setText(videoInfoBeen.get(i).getName());当我们复用的时候 直接走的else和下面的方法,所以这儿我们会每次都绑定控件和new adapter;viewHolder3Adapter = new ViewHolder3Adapter(context, appPecialColumnResourcesArrayList3); viewHolder3Adapter.setCountNum(8); holder4.gridView3.setAdapter(viewHolder3Adapter);
因此我把初始化和绑定控件的方法放在了view==null里面
if(view == null){ view = LayoutInflater.from(context).inflate(R.layout.viewholder1, null); holder4 = new Holder4(view); view.setTag(holder4); viewHolder3Adapter = new ViewHolder3Adapter(context, appPecialColumnResourcesArrayList3); viewHolder3Adapter.setCountNum(8); holder4.gridView3.setAdapter(viewHolder3Adapter); }else{ holder4 = (Holder4) view.getTag(); }这样就完美的解决了卡顿了现象。
总结:初始化的方法和绑定adapter都放在view==null 的判定中,这样才能真正的形成布局复用。多item时多要重写getviewtype和getviewtypecount方法。深度嵌套时内部的listview或者gridview记得测量高度比如:
public class NonScrollGridView extends GridView { public NonScrollGridView(Context context, AttributeSet attrs){ super(context, attrs); } public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, mExpandSpec); } }小窍门:getviewtypecount 返回个100W(10W好像也行) 运行程序直接会内存溢出,并不会报错。需要整蛊别人的朋友可以试试。
/********************************华丽翻身******************************/
鉴于评论的朋友说了不能刷新和数据重复。
我有对这个布局做了点小优化。就可以刷新和不存在数据重复了。
思路:将adapter 和内部列表控件都封装在viewholder。
比如这个项目ViewHolder:
class UserCommentHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private OnItemClickListener onItemClick;
private CircleImageView img;
private TextView tv_nick_name,tv_commend_time,tv_commend_reply,tv_commend_content,tv_shop_name,tv_shop_address,tv_shop_tags;
private AppCompatRatingBar rating_bar;
private NonScrollGridView gv_img;
private ArrayList<ImgListEntity> result=new ArrayList<>();
private ImageAdapter imageAdapter;
private View layout_reply;
private ImageView img_shop;
public UserCommentHolder(View itemView,OnItemClickListener onItemClickListener) {
super(itemView);
img= (CircleImageView) itemView.findViewById(R.id.img);
tv_nick_name= (TextView) itemView.findViewById(R.id.tv_nick_name);
tv_shop_name= (TextView) itemView.findViewById(R.id.tv_shop_name);
tv_shop_address= (TextView) itemView.findViewById(R.id.tv_shop_address);
tv_shop_tags= (TextView) itemView.findViewById(R.id.tv_shop_tags);
tv_commend_time= (TextView) itemView.findViewById(R.id.tv_commend_time);
tv_commend_content= (TextView) itemView.findViewById(R.id.tv_commend_content);
tv_commend_reply= (TextView) itemView.findViewById(R.id.tv_commend_reply);
layout_reply= (View) itemView.findViewById(R.id.layout_reply);
img_shop= (ImageView) itemView.findViewById(R.id.img_shop);
gv_img= (NonScrollGridView) itemView.findViewById(R.id.gv_img);
rating_bar= (AppCompatRatingBar) itemView.findViewById(R.id.rating_bar);
imageAdapter=new ImageAdapter(result);
gv_img.setAdapter(imageAdapter);
this.onItemClick = onItemClickListener;
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (onItemClick != null)
onItemClick.onItemClick(view, getPosition()-1);
}
}
然后在绑定数据的onBindViewHolder/getview 方法里:
holder.result.clear();
holder.result.addAll(“数据源”);
holder.imageAdapter.notifyDataSetChanged();
这样就能跟随主布局一起刷新了。当item复用的时候就不会出现重复的view了。