控件ListView讲解

747 阅读5分钟

1.listView是如何提高其效率的?

其实当converView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。当convertView不为空,重复利用已经创建的view的时候,使用getTag()方法获取绑定的ViewHolder对象,这样就避免了findViewById对控件的层层查询,而是快速定位到控件。

1)复用ConvertView,使用历史的view,提升效率200%,

2)自定义静态类ViewHolder,减少findViewById的次数。提升效率50%

3)异步加载数据,分页加载数据。

4)使用WeakRefrence引用ImageView对象

2.当ListView数据集改变后,如何更新ListView?

使用该ListView的adapter的notifyDataSetChanger()方法。该方法会使ListView重新绘制。

3.ListView如何实现分页加载?

1)设置ListView的滚动监听器:setOnScrollListener(new OnScrollListener{})

在监听器中有两个方法:滚动状态发生变化的方法(onScrollStateChanged)和listView被滚动时调用的方法onScroll

2)在滚动状态发生改变的方法中,有三种状态:手指按下移动的状态:SCROLL_STATE_TOUCH_SCROLL://触摸滑动 惯性滚动(滑翔(flgin)状态):SCROLL_STATEFLING//滑翔状态

静止状态:SCROLL_STATE_IDLE:静止

分批加载数据,只关心静止状态;关心最后一个可见的条目,如果最后一个可见条目就是数据适配器(集合)里的最后一个,此时可加载更多的数据。在每次加载的时候,计算出滚动的数量,当滚动的数量大于等于总数量的时候,可以提示用户无更多数据了。

4.ListView可以显示多种类型的条目吗

这个当天可以的,listView显示的每个条目都是通过baseAdapter的getView(int position,View convertView ,ViewGroup parent)来展示的,理论上我们完全可以让每个条目都是不同类型的view。

比如:从服务器拿回一个标识为id=1,那么当id=1的时候,我们就加载类型一的条目,当id=2的时候,加载类型二的条目,常见布局在资讯类客户端中可以经常看到。

除此之外adapter还提供了getViewTypeCount()和getItemViewType(int position)两个方法。在getView方法中我们可以根据不同viewtype加载不同的布局文件。

5.listView 如何定位到指定位置

可以通过listview提供的lv.setSelection(View.getPosition());方法。

6.如何在ScrollView中如何嵌入ListView

通常情况下我们不会在ScrollView中嵌套ListView,在ScrollView添加一个Listview会导致listview控件显示不全,通常只会显示一条,这是因为两个控件的滚动事件冲突导致。所以需要通过listview中的item数量去计算listview的显示高度,从而使其完整展示,如下提供一个方法供大家参考:

     lv =(ListView)findViewById(R.id.lv);
     adapter =new MyAdapter();
     lv.setAdapter(adapter);
     setListViewHeightBasedOnChildren(lv);
     
     public  void  setListViewHeightBasedOnChildren(ListView listview){
     ListAdapter listAdapter =listView.getAdapter();
     if(listAdapter ==null){
          return;
     }
       int  totalHeight =0;
       for (int i=0;i<listAdapter.getCount();i++){
       View  listItem =listAdapter.getView(i,null,listView);
       listItem.measure(0,0);
       totalHeight+=listItem.getMeasuredHeight();
       
       }
       ViewGroup.LayoutParams params =listView.getLayoutParams();
       params.height =totalHeight+(listView.getDividerHeight())*(listAdapter.getCount()-1);
       params.height+=5;
       
       listView.setLayoutParams(params);
       
       
       }
       
     
     
     }
     

现阶段最好的处理的方式是:自定义ListView,重载onMeasure()方法,设置全部显示。

 package com.meiya.ui;

 import.android.widget.ListView;

 /**
*scrollview中嵌套listview的简单实现
*   ScrolViewwithListView.java
 */

public class  ScrollViewwithListView  extends ListView{
   
   public ScrollViewWithListView(android.content.Context context, 
  android.util.AttributeSet attrs) {
 super(context, attrs);
 }
 
 /**
 **  Integer.MAX_VALUE>>2.如果不设置,系统默认设置是显示两条
 */

  public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
  int  expandSpec =MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
  super.onMeasure(widthMeasureSpec,expandSpec);
  
  
  }

 }
 
 

7.ListView 中如何优化图片

图片的优化策略比较多。

1).处理图片的方式:

如果ListView中自定义的Item中有涉及到大量图片的,一定要对图片进行细心的处理,因为图片占的内存是ListView项中最头疼的,处理图片的方法大致有以下几种:

(1.不要直接拿路径就去循环BitmapFactory.decodeFile;使用Options保存图片大小,不要加载图片到内存去。

(2.对图片一定要经过边界压缩尤其是比较大的图片,如果你的图片是后台服务器处理好的那就不需要了

(3.在ListView中取图片时也不要直接拿个路径去取图片,而是以WeakReference(使用WeakReference代替强引用。比如可以使用WeakReference mContextRef),SoftReference,WeakHashMap等的来存储图片信息。

(4.在getView中做图片转换时,产生的中间变量一定及时释放

2.异步加载图片基本思想:

(1.先从内存缓存中获取图片显示(内存缓冲)

(2.获取不到的话从sd卡里获取(sd卡缓冲)

(3.都获取不到的话从网络下载图片并保存到sd卡同时加入内存并显示(视情况看是否要显示)

原理:

优化一:先从内存中加载,没有则开启线程从sd卡或网络中获取,这里注意从sd卡获取图片是放在子线程里执行的,否则快速滑屏的话会不够流畅。

优化二:于此同时,在adapter里有个busy变量,表示listview是否处于滑动状态,如果是滑动状态则仅从内存中获取图片,没有的话无需再开启线程去外存或网络获取图片。

优化三:ImageLoader里的线程使用了线程池,从而避免了过多的线程频繁创建和销毁,如果每次总是new 一个线程去执行这是非常不可取的,好一点的用AsyncTask类,其实内部也是用到了线程池。在从网络获取图片时,先是将其保存到sd卡,然后在加载到内存,这么做的好处是在加载到内存时可以做个压缩处理,以减少图片所占内存。

8listview中图片错位的问题是如何产生的

图片错位问题的本质源于我们的listview使用了缓存convertview,假设一种场景,一个listview一屏显示九个item,那么在拉出第十个item的时候,事实上该item是重复使用了第一个item,也就是说在第一个item从网络中下载图片并最终要在显示的时候,其实该item已经不再当前显示区域内了,此时显示的后果将可能在第十个item上输出图像,这就导致了图片错位的问题。所以解决之道在于可见则显示,不可见则不显示。

9.如何刷新listview中单个item的数据,不刷新整个listview的数据?

修改单个item的数据,然后调用适配器的notifyDataSetChanged()方法