ListView加载性能优化---ViewHolder---分页
ListView是Android中一个重要的组件,可以使用它加列表数据,用户可以自己定义列表数据,同时ListView的数据加载要借助Adapter,一般情况下要在Adapter类中重写getCount(),getItem(),getItemId(),getView()四个方法。自定义列表项,以及数据的加载在getView()中处理。当ListView加载的列表项数据过多时,会占用大量的内存,影响性能,因此要对ListView进行优化。
getView的加载方法有3种形式,如下:
1.每次创建一个view,然后加载数据,加载速度慢
/** * 1.每次都创建一个View */ @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(context); View view = inflater.inflate(R.layout.listview_item_two,null); ImageView iv = (ImageView) view.findViewById(R.id.lv_listview_adapter_img); TextView tv = (TextView) view.findViewById(R.id.tv_listview_adapter_name); iv.setImageResource(icons[position]); tv.setText(titles[position]); return view; }
2.当convertView不为空的时候直接重新使用convertView,为空时新建view,这样可以减少了很多不必要的View的创建,然后加载数据
/** * 2.复用convertView,减少不必要的创建 * 当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据 * @param position * @param convertView * @param parent * @return */ @Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView==null){ LayoutInflater inflater = LayoutInflater.from(context); convertView = inflater.inflate(R.layout.listview_item_two,null); } ImageView iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img); TextView tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name); iv.setImageResource(icons[position]); tv.setText(titles[position]); return convertView; }
3.定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可
ViewHolder将需要缓存的view封装好,convertView的setTag才是将这些缓存起来供下次调用。 当你的listview里布局多样化的时候 viewholder的作用体现明显,效率再一次提高。 View的findViewById()方法也是比较耗时的,因此需要考虑只调用一次,之后就用View.getTag()方法来获得ViewHolder对象。
当加载100条数据时,采用分页,每页加载20条,相当于创建了20个convertview。再加载21-40这二十条时,不需要重新创建20个convertview。第21条复用第1条的convertView,第22条复用第2条的convertView.......
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder vh; if(convertView==null){ LayoutInflater inflater = LayoutInflater.from(context); convertView = inflater.inflate(R.layout.listview_item_two,null); vh = new ViewHolder(); vh.iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img); vh.tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name); convertView.setTag(vh); }else{ vh = (ViewHolder)convertView.getTag(); } vh.iv.setImageResource(icons[position]); vh.tv.setText(titles[position]); return convertView; } static class ViewHolder{ ImageView iv; TextView tv; }
以下是一个亲测的Demo案例,代码及运行结果如下:
1.ListViewActivity.class
public class ListViewActivity extends AppCompatActivity { private ListView listView_five; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list_view_adapter); listView_five = (ListView) findViewById(R.id.lv_listview_adater); listView_five.setAdapter(new MyAdapter(this)); listView_five.setOnItemClickListener(new AdapterView.OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(ListViewActivity.this,"position="+position,Toast.LENGTH_LONG).show(); } }); } static class MyAdapter extends BaseAdapter{ private String[] titles = {"title-1","title-2","title-3","title-4"}; private int[] icons = {android.R.drawable.ic_lock_idle_alarm,android.R.drawable.ic_lock_idle_alarm, android.R.drawable.ic_lock_idle_alarm,android.R.drawable.ic_lock_idle_alarm}; private Context context; public MyAdapter(Context context){ this.context = context; } @Override public int getCount() { return titles.length; } @Override public Object getItem(int position) { return titles[position]; } @Override public long getItemId(int position) { return position; } /** * 1.每次都创建一个View */ /*@Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(context); View view = inflater.inflate(R.layout.listview_item_two,null); ImageView iv = (ImageView) view.findViewById(R.id.lv_listview_adapter_img); TextView tv = (TextView) view.findViewById(R.id.tv_listview_adapter_name); iv.setImageResource(icons[position]); tv.setText(titles[position]); return view; }*/ /** * 2.复用convertView,减少不必要的创建 * 当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据 * @param position * @param convertView * @param parent * @return */ /* @Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView==null){ LayoutInflater inflater = LayoutInflater.from(context); convertView = inflater.inflate(R.layout.listview_item_two,null); } ImageView iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img); TextView tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name); iv.setImageResource(icons[position]); tv.setText(titles[position]); return convertView; }*/ /** * 3.定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可 * @param position * @param convertView * @param parent * @return */ @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder vh; if(convertView==null){ LayoutInflater inflater = LayoutInflater.from(context); convertView = inflater.inflate(R.layout.listview_item_two,null); vh = new ViewHolder(); vh.iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img); vh.tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name); convertView.setTag(vh); }else{ vh = (ViewHolder)convertView.getTag(); } vh.iv.setImageResource(icons[position]); vh.tv.setText(titles[position]); return convertView; } static class ViewHolder{ ImageView iv; TextView tv; } } }
2.activity_list_view_adapter.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lv_listview_adater" android:layout_width="match_parent" android:layout_height="match_parent" android:dividerHeight="10dp" android:listSelector="#00ff00" android:scrollbars="vertical|horizontal" /> </RelativeLayout>
3.listview_item_two.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/lv_listview_adapter_img" android:src="@mipmap/gradview_dog_one" android:layout_width="wrap_content" android:layout_gravity="center" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_listview_adapter_name" android:text="拉布拉多" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
三种方法的运行结果均相同,如下所示:
参考网址:http://blog.csdn.net/jacman/article/details/7087995
http://blog.csdn.net/pkxiuluo01/article/details/7380874
ListView分页的实现:
1.PageActivity.class
/** * ListView分页 * 实现OnScrollListener监听 */ public class PageActivity extends AppCompatActivity implements AbsListView.OnScrollListener{ private ListView listView_six; private int index = 1; private MyAdapter myAdapter; private Vector<News> news = new Vector<>();//线程安全的容器 private static final int DATA_UPDATE = 0x1;//数据更新完成后的标记 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_page); listView_six = (ListView) findViewById(R.id.lv_listview_page); listView_six.setOnScrollListener(this); //引入加载缓冲布局,并添加到ListView的底部 View footView = getLayoutInflater().inflate(R.layout.load_item,null); listView_six.addFooterView(footView); initData(); myAdapter = new MyAdapter(this); listView_six.setAdapter(myAdapter); } //加载数据,每次加载10条 private void initData() { for(int i = 0;i < 10;i++){ News n = new News(); n.setTitle("title"+index); n.setContent("content"+index); index++; news.add(n); } } private int visibleLastIndex;//用来可显示的最后一条数据的索引 /** * * @param view * @param scrollState 滚动状态 * SCROLL_STATE_IDLE:不再滚动时(停止) */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if(myAdapter.getCount() == visibleLastIndex && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){ new loadDataThread().start(); } } /** * * @param view * @param firstVisibleItem 第一次显示的 * @param visibleItemCount 可显示的总量 * @param totalItemCount */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { visibleLastIndex = firstVisibleItem + visibleItemCount -1; } //线程之间通讯机制 private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case DATA_UPDATE: //告知Adapter刷新数据 myAdapter.notifyDataSetChanged(); break; default: break; } } }; //子线程通知UI线程 class loadDataThread extends Thread{ @Override public void run() { super.run(); //前10条数据加载完成时,再加载另外10条 initData(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //通过Handler向主线程发送一个标记 handler.sendEmptyMessage(DATA_UPDATE); } } //自定义Adapter class MyAdapter extends BaseAdapter{ private Context context; public MyAdapter(Context context){ this.context = context; } @Override public int getCount() { return news!=null?news.size():0; } @Override public Object getItem(int position) { return news.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder vh; if(convertView==null){ LayoutInflater inflater = LayoutInflater.from(context); convertView = inflater.inflate(R.layout.listview_item_three,null); vh = new ViewHolder(); vh.tv_one = (TextView) convertView.findViewById(R.id.tv_page_title); vh.tv_two = (TextView) convertView.findViewById(R.id.tv_page_content); convertView.setTag(vh); }else{ vh = (ViewHolder)convertView.getTag(); } News n = news.get(position); vh.tv_one.setText(n.getTitle()+"---"); vh.tv_two.setText(n.getContent()); return convertView; } class ViewHolder{ TextView tv_one; TextView tv_two; } } }
2.activity_page.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.langdon.taiyang.androidtest.listview.PageActivity"> <ListView android:id="@+id/lv_listview_page" android:layout_width="match_parent" android:layout_height="match_parent" android:dividerHeight="10dp" android:listSelector="#00ff00" android:scrollbars="vertical|horizontal" /> </LinearLayout>
3.load_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.langdon.taiyang.androidtest.listview.PageActivity"> <ListView android:id="@+id/lv_listview_page" android:layout_width="match_parent" android:layout_height="match_parent" android:dividerHeight="10dp" android:listSelector="#00ff00" android:scrollbars="vertical|horizontal" /> </LinearLayout>
4.listview_item_three.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <TextView android:id="@+id/tv_page_title" android:text="标题" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_page_content" android:text="内容" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
运行效果如下: