ListView的内存优化机制
第一:RecycleBin机制
在ListView里有一个内部类RecycleBin ,RecycleBin 里有一个存储View对象的ArrayList
private ArrayList<View> mCurrentScrap;当有item滑出了屏幕,itemView会被回收到这个数组里,而有新的item滑入了屏幕时,会从数组里拿到view,这个view做为第二个参数传到了Adapter的getView()方法当中,这样就不用每一次都inflate view了。
@Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(resourceId, null); } else { view = convertView; } return view; }第二:ViewHolder
ListView内部的重用是防止你重复inflate布局,ViewHolder的使用是防止你重复findViewById。
例:
public class MyAdapter extends BaseAdapter { @Override public int getCount() { return 0; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { //分离绑定职责 ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(resourceId, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.bind(getItem(position)); return convertView; } class ViewHolder { private TextView subView1; private TextView subView2; ViewHolder(View root){//通过构造方法将子view的查找逻辑搬移到这里 subView1 = (TextView)root.findViewById(R.id1); subView2 = (TextView)root.findViewById(R.id2); } void bind(ItemData item){ //绑定逻辑搬移到这里将更加简洁 subView1.setText(item.text1); subView2.setText(item.text2); } } }ViewHolder是用来保存视图引用的类,无论是ListView亦或是RecyclerView。只不过在ListView中,ViewHolder需要自己来定义,且这只是一种推荐的使用方式,不使用当然也可以,这不是必须的。只不过不使用ViewHolder的话,ListView每次getView的时候都会调用findViewById(int),这将导致ListView性能展示迟缓。而在RecyclerView中使用RecyclerView.ViewHolder则变成了必须,尽管实现起来稍显复杂,但它却解决了ListView面临的上述不使用自定义ViewHolder时所面临的问题。
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>{ @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){ MyViewHolder holder = new MyViewHolder(LayoutInflater.from( HomeActivity.this).inflate(R.layout.item_home, parent, false)); return holder; } @Override public void onBindViewHolder(MyViewHolder holder, int position) holder.tv.setText(mDatas.get(position)); } @Override public int getItemCount(){ return mDatas.size(); } class MyViewHolder extends ViewHolder{ TextView tv; public MyViewHolder(View view){ super(view); tv = (TextView) view.findViewById(R.id.id_num); } } }在ListView中如果我们想要在item之间添加间隔符,我们只需要在布局文件中对ListView添加如下属性即可:
android:divider="@android:color/transparent" android:dividerHeight="5dp"而RecyclerView并没有支持divider这样的属性,但是可以自己定制: 我们可以通过该方法添加分割线:
mRecyclerView.addItemDecoration();该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类。
我们知道ListView只能在垂直方向上滚动,Android API没有提供ListView在水平方向上面滚动的支持。或许有多种方式实现水平滑动,但是请相信我,ListView并不是设计来做这件事情的。但是RecyclerView相较于ListView,在滚动上面的功能扩展了许多。它可以支持多种类型列表的展示要求,主要如下:
LinearLayoutManager,可以支持水平和竖直方向上滚动的列表。 StaggeredGridLayoutManager,可以支持交叉网格风格的列表,类似于瀑布流或者Pinterest。 GridLayoutManager,支持网格展示,可以水平或者竖直滚动,如展示图片的画廊。
使用如下:
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));RecyclerView 的item增加、删除的动画是可配置的:RecyclerView.ItemAnimator。
ItemAnimator是一个抽象类,系统为我们提供了一种默认的实现类DefaultItemAnimator。
mRecyclerView.setItemAnimator(new DefaultItemAnimator());ListView通过AdapterView.OnItemClickListener接口来探测点击事件。而RecyclerView则通过RecyclerView.OnItemTouchListener接口来探测触摸事件。它虽然增加了实现的难度,但是却给予开发人员拦截触摸事件更多的控制权限。
除了这种方式也可以通过adapter中自己去提供回调,如:
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>{ //... public interface OnItemClickLitener{ void onItemClick(View view, int position); void onItemLongClick(View view , int position); } private OnItemClickLitener mOnItemClickLitener; public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener){ this.mOnItemClickLitener = mOnItemClickLitener; } @Override public void onBindViewHolder(final MyViewHolder holder, final int position){ holder.tv.setText(mDatas.get(position)); // 如果设置了回调,则设置点击事件 if (mOnItemClickLitener != null){ holder.itemView.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v){ int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemClick(holder.itemView, pos); } }); holder.itemView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v){ int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemLongClick(holder.itemView, pos); return false; } }); } } //... }别忘了给item添加一个drawable:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" > <item android:state_pressed="true" android:drawable="@color/color_item_press"></item> <item android:drawable="@color/color_item_normal"></item> </selector>Activity中去设置监听:
mAdapter.setOnItemClickLitener(new OnItemClickLitener() { @Override public void onItemClick(View view, int position){ Toast.makeText(HomeActivity.this, position + " click", Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position){ Toast.makeText(HomeActivity.this, position + " long click", Toast.LENGTH_SHORT).show(); mAdapter.removeData(position); } });ListView可以设置选择模式,并添加MultiChoiceModeListener,如下所示:
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); listView.setMultiChoiceModeListener(new MultiChoiceModeListener() { public boolean onCreateActionMode(ActionMode mode, Menu menu) { ... } public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { ... } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.menu_item_delete_crime: CrimeAdapter adapter = (CrimeAdapter)getListAdapter(); CrimeLab crimeLab = CrimeLab.get(getActivity()); for (int i = adapter.getCount() - 1; i >= 0; i--) { if (getListView().isItemChecked(i)) { crimeLab.deleteCrime(adapter.getItem(i)); } } mode.finish(); adapter.notifyDataSetChanged(); return true; default: return false; } public boolean onPrepareActionMode(ActionMode mode, Menu menu) { ... } public void onDestroyActionMode(ActionMode mode) { ... } });而RecyclerView则没有此功能。