一、ListView的基本用法
列表的显示需要三个元素:
1. ListView:用来展示列表的View。
2. Adapter适配器:是ListView界面与数据之间的桥梁;即用来把数据映射到ListView上的中介;当列表里的每一项显示到页面时,都会调用Adapter的getView方法返回一个View。
3. 数据:具体的将被映射的字符串、图片或基本组件等。
根据列表适配器的类型,可分为三种:ArrayAdapter,SimpleAdapter和CursorAdapter。其中ArrayAdapter最为简单,只能展示一行字。SimpleAdapter可用于显示自定义的复杂布局文件。SimpleCursorAdapter可以认为是SimpleAdapter对数据库的简单结合,可以方便的把数据库的内容以列表的形式展示出来。SimpleAdapter虽说已经可以满足大多数情况的需要,但还不够灵活,用SimpleAdapter显示的每一行布局都是相同的,不能有特殊情况。因此如果希望可以扩展ListView的显示特性,比如使一些行布局发生变化,或者添加Button控件的响应等,这时候就要自己去实现一个BaseAdapter,通过其getView方法为每一行返回特定的布局形式或者视图。例如
系统要绘制ListView,首先用getCount()函数得到要绘制的列表的长度,然后开始绘制第一行,如何绘制呢?调用getView()函数。在这个函数里首先获得一个View(如果是一个简单的显示则是View,如果是一个自定义的里面包含较多控件的时候它其实是一个ViewGroup),然后再实例化并设置各个组件及其数据内容并显示它。绘制完这行后,依此再绘制完。
二、ListView加载数据的基本原理
ListView针对每个item,要求Adapter返回一个视图(getView),即ListView在开始绘制时,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据该长度,调用getView()一行一行的绘制ListView的每一项。如果getCount()返回值是0的话,列表一行都不会显示,如果返回1,就只显示一行;返回几则显示几行。如果我们有成千上万的item要显示怎么办呢?为每个item创建一个新的View?这是不可能的!实际上Android早就缓存了这些视图;Android中有个Recycler的构件。只有可见的项目存在内存中,其他的在Recycler中。
三、ListView的优化
list.xml
1. 最简单的方法,最慢且最不实用。
1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) { 3 // TODO Auto-generated method stub 4 //找到R.layout.list布局文件 5 layout_Adapter = (LinearLayout)this.layoutInflater.inflate(R.layout.list, null); 6 imageView = (ImageView)layout_Adapter.findViewById(R.id.imageView1); 7 textView = (TextView)layout_Adapter.findViewById(R.id.textView1); 8 imageView.setImageResource(R.drawable.icon); 9 textView.setText(title[position]);10 return layout_Adapter;11 }
2. View的重用。View的每次创建都是比较耗时的,利用convertview回收视图;即对于getview方法传入的convertView做 != null的判断;这样系统将会少创建很多View,性能得到了很大的提升。
1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) { 3 // TODO Auto-generated method stub 4 if(convertView == null){ 5 convertView = (LinearLayout)this.layoutInflater.inflate(R.layout.list, null); 6 } 7 imageView = (ImageView)convertView.findViewById(R.id.imageView1); 8 textView = (TextView)convertView.findViewById(R.id.textView1); 9 imageView.setImageResource(R.drawable.icon);10 textView.setText(title[position]);11 return convertView;12 }
3. ViewHolder的应用。View的findViewById()方法也是比较耗时的,因此需要考虑只调用一次,之后就用View.getTag()方法来获得ViewHolder对象。
1 public View getView(int position, View convertView, ViewGroup parent) { 2 // TODO Auto-generated method stub 3 ViewHolder holder; 4 if(convertView == null){ 5 convertView = (LinearLayout)this.layoutInflater.inflate(R.layout.list, null); 6 holder = new ViewHolder(); 7 holder.tv = (TextView)convertView.findViewById(R.id.textView1); 8 holder.im = (ImageView)convertView.findViewById(R.id.imageView1); 9 convertView.setTag(holder);10 }else{11 holder = (ViewHolder)convertView.getTag();12 Log.d("list", "convertView....");13 }14 holder.im.setImageResource(R.drawable.icon);15 holder.tv.setText(title[position]);16 return convertView;17 }18 19 static class ViewHolder{20 TextView tv;21 ImageView im;22 }
这种方法为View设置了Tag属性,通过setTag()方法将View的ImageView、TextView组件的对象添加到Tag 中,当回收利用时直接用getTag()方法取出,不需要重新获取组件。
ListView的优化三原则
1.复用convertView。
在getItemView中,判断convertView是否为空,如果不为空,可复用。
2.异步加载图片
item中如果包含有webimage,那么最好异步加载
3.快速滑动时不显示图片
当快速滑动列表时(SCROLL_STATE_FLING),item中的图片或获取需要消耗资源的view,可以不显示出来;而处于其他两种状态(SCROLL_STATE_IDLE 和SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来;在做ListView加载数据时如果数据量大的话会造成加载时间过长而卡屏,所以为了解决这个问题,查看了SDK,在OnScrollListener中有两个方法只要重写这两个方法就可以实现滚动加载,例如:
1 public void onScroll(AbsListView v, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 2 3 lastItem = firstVisibleItem + visibleItemCount - 1; 4 5 if (adapter.count == lastItem) { 6 7 adapter.count += 10; adapter.notifyDataSetChanged(); 8 9 }10 }11 public void onScrollStateChanged(AbsListView view, int scrollState) { 12 13 // TODO Auto-generated method stub14 Log.i("onScrollStateChanged", "onScrollStateChanged"); 15 16 }