针对包含多个元素的View,如ListView,GridView,ExpandableListview,的时候我们是给其设置一个Adapter。Adapter是父View给子View提供数据的桥梁,也是提供每个Item的视图桥梁。由于View滚动的时候涉及到资源和View的回收过程,这其中也有一些坑,处理不当容易造成OOM,这里列举了一些优化的办法。
设计模式
1.桥接模式
Adapter使用了桥接模式(Bridge Mode),将抽象部分与实现部分分离,使它们都可以独立的变化,即具有无数个子View的ViewGroup(ListView,GridView)以及各种Adapter(CursorAdapter,ArrayAdapter)都可独立的变化。Adapter是与View之间提供数据的桥梁,也是提供每个Item的视图桥梁。
2.适配器模式
适配器模式可以让两个没有任何关系的类在一起运行,我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。
AbsListView内部的RecycleBin
- fillActiveViews() 调用这个方法后就会根据传入的参数来将ListView中的指定元素存储到mActiveViews数组当中。
- getActiveView() 获取ListView某一position处的View(获取之后即从mActiveViews中移除)
- addScrapView() 当有某个View确定要废弃掉的时候(比如滚动出了屏幕)缓存这个View
RecycleBin中有一个ArrayList来存储废弃的View,convertView被移出屏幕后进入到了废弃缓存中,item再次滑动到屏幕上时拿过来复用即可。
可查看ListView工作原理完全解析。
1.避免重复inflate view造成资源浪费
ListView等内部有Recycler的实现,无论数据中是多少个item,在显示上Recycler只存储其中可见的View在内存中。当向下滑动时,顶部不可见Item直接回移动到下方再次填充数据变为新增项,不用再inflate产生新的对象。
1 2 3 4 5 6 7
| public View getView(int position, View convertView, ViewGroupparent){ if (convertView ==null) { convertView =LayoutInflater.from(mContext).inflate(R.layout.item_view, null); } return converView; }
|
2.使用一个静态类(ViewHolder)保存子View和xml的引用关系,避免多余的findViewById
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| static class ViewHolder { TextView tv; ImageView iv; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.item_view, null); holder = new ViewHolder(); holder.tv = (TextView) convertView.findViewById(R.id.text); holder.iv = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.tv.setText(DATA[pos].title); holder.iv.setImageBitmap(DATA[pos].bitmap); return convertView; }
|
3.图片加载优化
- 使用线程池进行图片请求并使用LRUCache做图片缓存处理
- 图片根据边界,option进行缩放处理
- 滑动时暂停请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| listView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { loadImage(startPos, endPos); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { startPos = firstVisibleItem; endPos = firstVisibleItem + visibleItemCount; if (endPos >= totalItemCount) { endPos = totalItemCount - 1; } } });
|
4.设置点击事件
设置点击事件的通常写法是在getView方法中一个个设置,每次调用getView时都设置了一个新的onClick事件,可以直接在ViewHolder中设置一个position,然后实现OnClickListenr接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class ViewHolder implements OnClickListener{ int position; TextView name; public void setPosition(int position){ this.position = position; } @Override public void onClick(View v) { switch (v.getId()){ } } } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if(convertView==null){ convertView = inflater.inflate(R.layout.list_item, parent, false); holder = new ViewHolder(); holder.name = (TextView) convertView.findViewById(R.id.name); holder.name.setOnClickListener(this); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.name.setText(list.get(position).partname); holder.setPosition(position); return convertView; }
|
5.getView中减少重复逻辑,避免耗时操作,避免创建大量对象
6.RecyclerView的优化主要在代码量上