zoukankan      html  css  js  c++  java
  • Android之ListView优化

    关于ListView几个方面的优化:

    1. ListView的大小设定固定值;
    2. 复用convertView, 使用ViewHolder提高在容器中查找组件的效率;
    3. 使用分页加载;
    4. 快速滚动时, item不显示耗时加载的图片(或其他资源)

    Android中有一个反复循环构件(Recycler),它的工作原理如下:(参考:http://www.cnblogs.com/xiaowenji/archive/2010/12/08/1900579.html)

    1. 如果有很多的item时,那么可见的条目存在内存中,其他的在Recycler中。
    2. ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的。
    3. 当item1滚出屏幕,并且一个新的项目从屏幕底端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图。

    代码示例:

     1 public class ListViewActivity extends Activity {
     2     private ArrayList<String> list = new ArrayList<>();
     3     private ListView listView;
     4     private ItemAdapter itemAdapter;
     5     private int index = 0;
     6 
     7     @Override
     8     protected void onCreate(Bundle savedInstanceState) {
     9         super.onCreate(savedInstanceState);
    10         setContentView(R.layout.activity_list_view);
    11         listView = (ListView) findViewById(R.id.listView);
    12         initData();
    13         itemAdapter = new ItemAdapter();
    14         listView.setAdapter(itemAdapter);
    15 
    16     }
    17 
    18     public void initData(){
    19         for(int i = 0; i < 50; i++){
    20             list.add("item--" + index++);
    21         }
    22     }
    23     class ItemAdapter extends BaseAdapter {
    24 
    25         @Override
    26         public int getCount() {
    27             return list.size();
    28         }
    29 
    30         @Override
    31         public Object getItem(int position) {
    32             if(position >= 0 && position < list.size()) return list.get(position);
    33             return null;
    34         }
    35 
    36         @Override
    37         public long getItemId(int position) {
    38             return position;
    39         }
    40 
    41         @Override
    42         public View getView(int position, View convertView, ViewGroup parent) {
    43             Log.e("getView:--" + position, convertView + "");
    44             ViewHolder vh;
    45             if(convertView == null){
    46                 convertView  = getLayoutInflater().inflate(R.layout.list_item, null);
    47                 vh = new ViewHolder();
    48                 vh.textView = (TextView) convertView.findViewById(R.id.textView);
    49                 convertView.setTag(vh);
    50             }else{
    51                 vh = (ViewHolder) convertView.getTag();
    52             }
    53             vh.textView.setText("item--" + position);
    54             return convertView;
    55         }
    56 
    57          class ViewHolder{
    58             TextView textView;
    59         }
    60     }
    61 }

    一、ListView的大小设定固定值

    首先需要了解Adapter的使用,里面有一个getCount()是返回ListView要显示的item个数,而手机界面可以显示的个数是通过: ListView的高度/item的高度 。getView()方法是需要返回一个视图,如果ListView的高度不确定,而是使用wrap_content属性,那么ListView在匹配的时候,就会在一直在尝试可以显示多少个item在ListView上,那么会影响到性能问题。

     <ListView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/listView"
            android:layout_alignParentTop="true"
            android:layout_alignParentStart="true" />

    1. 如果在layout里面没有设置ListView的高度为定值时,观察如下现象:

    10-20 17:01:59.857 21761-21761/com.example.lenovo.listviewtest E/getView:--0﹕ null
    10-20 17:01:59.870 21761-21761/com.example.lenovo.listviewtest E/getView:--1﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.870 21761-21761/com.example.lenovo.listviewtest E/getView:--2﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.870 21761-21761/com.example.lenovo.listviewtest E/getView:--3﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.870 21761-21761/com.example.lenovo.listviewtest E/getView:--4﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.874 21761-21761/com.example.lenovo.listviewtest E/getView:--5﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.874 21761-21761/com.example.lenovo.listviewtest E/getView:--6﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.874 21761-21761/com.example.lenovo.listviewtest E/getView:--7﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.934 21761-21761/com.example.lenovo.listviewtest E/getView:--0﹕ android.widget.LinearLayout{42994980 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.937 21761-21761/com.example.lenovo.listviewtest E/getView:--1﹕ null
    10-20 17:01:59.940 21761-21761/com.example.lenovo.listviewtest E/getView:--2﹕ null
    10-20 17:01:59.944 21761-21761/com.example.lenovo.listviewtest E/getView:--3﹕ null
    10-20 17:01:59.947 21761-21761/com.example.lenovo.listviewtest E/getView:--4﹕ null
    10-20 17:01:59.947 21761-21761/com.example.lenovo.listviewtest E/getView:--5﹕ null
    10-20 17:01:59.950 21761-21761/com.example.lenovo.listviewtest E/getView:--6﹕ null
    10-20 17:01:59.950 21761-21761/com.example.lenovo.listviewtest E/getView:--7﹕ null
    10-20 17:01:59.957 21761-21761/com.example.lenovo.listviewtest E/getView:--0﹕ null
    10-20 17:01:59.957 21761-21761/com.example.lenovo.listviewtest E/getView:--1﹕ android.widget.LinearLayout{429a2fa0 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.957 21761-21761/com.example.lenovo.listviewtest E/getView:--2﹕ android.widget.LinearLayout{429a2fa0 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.957 21761-21761/com.example.lenovo.listviewtest E/getView:--3﹕ android.widget.LinearLayout{429a2fa0 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.957 21761-21761/com.example.lenovo.listviewtest E/getView:--4﹕ android.widget.LinearLayout{429a2fa0 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.960 21761-21761/com.example.lenovo.listviewtest E/getView:--5﹕ android.widget.LinearLayout{429a2fa0 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.960 21761-21761/com.example.lenovo.listviewtest E/getView:--6﹕ android.widget.LinearLayout{429a2fa0 V.E..... ......I. 0,0-0,0}
    10-20 17:01:59.960 21761-21761/com.example.lenovo.listviewtest E/getView:--7﹕ android.widget.LinearLayout{429a2fa0 V.E..... ......I. 0,0-0,0}
    .....

    2. 如果设置ListView的高度为定值时,观察如下现象:

    10-20 17:13:31.624 29386-29386/com.example.lenovo.listviewtest E/getView:--0﹕ null
    10-20 17:13:31.631 29386-29386/com.example.lenovo.listviewtest E/getView:--1﹕ null
    10-20 17:13:31.634 29386-29386/com.example.lenovo.listviewtest E/getView:--2﹕ null
    10-20 17:13:31.638 29386-29386/com.example.lenovo.listviewtest E/getView:--3﹕ null
    10-20 17:13:31.641 29386-29386/com.example.lenovo.listviewtest E/getView:--4﹕ null
    10-20 17:13:31.644 29386-29386/com.example.lenovo.listviewtest E/getView:--5﹕ null
    10-20 17:13:31.644 29386-29386/com.example.lenovo.listviewtest E/getView:--6﹕ null
    10-20 17:13:31.651 29386-29386/com.example.lenovo.listviewtest E/getView:--7﹕ null
    10-20 17:20:42.501 29386-29386/com.example.lenovo.listviewtest E/getView:--8﹕ null
    10-20 17:20:42.614 29386-29386/com.example.lenovo.listviewtest E/getView:--9﹕ android.widget.LinearLayout{4299bd90 V.E..... ........ 0,-133-656,7}
    10-20 17:20:42.698 29386-29386/com.example.lenovo.listviewtest E/getView:--10﹕ android.widget.LinearLayout{4299e9b8 V.E..... ........ 0,-128-656,12}
    10-20 17:20:42.751 29386-29386/com.example.lenovo.listviewtest E/getView:--11﹕ android.widget.LinearLayout{4299fda0 V.E..... ........ 0,-138-656,2}
    10-20 17:20:42.851 29386-29386/com.example.lenovo.listviewtest E/getView:--12﹕ android.widget.LinearLayout{429a1188 V.E..... ........ 0,-129-656,11}
    10-20 17:20:42.998 29386-29386/com.example.lenovo.listviewtest E/getView:--13﹕ android.widget.LinearLayout{429a2570 V.E..... ........ 0,-137-656,3}
    10-20 17:20:50.464 29386-29386/com.example.lenovo.listviewtest E/getView:--14﹕ android.widget.LinearLayout{429a3958 V.E..... ........ 0,-138-656,2}
    10-20 17:20:58.315 29386-29386/com.example.lenovo.listviewtest E/getView:--15﹕ android.widget.LinearLayout{429a4d40 V.E..... ........ 0,-140-656,0}

    结论:

    由以上比较可以知道,设定ListView的属性时,为了提高匹配的效率,可以把ListView的高度设为确定值。

    二、复用convertView, 使用ViewHolder提高在容器中查找组件的效率

    1.  上面的代码已经重复使用了convertView了,如果每次都是重新对item中的布局实例化成View对象,view = getLayoutInflater().inflate(R.layout.list_item, null); 这是比较耗时的。

    
    
    view  = getLayoutInflater().inflate(R.layout.list_item, null); 

    Log.e("getView:--" + position, view + "");

    10-20 19:35:03.723 18046-18046/com.example.lenovo.listviewtest E/getView:--0﹕ android.widget.LinearLayout{42998e50 V.E..... ......I. 0,0-0,0}
    10-20 19:35:03.730 18046-18046/com.example.lenovo.listviewtest E/getView:--1﹕ android.widget.LinearLayout{4299bec0 V.E..... ......I. 0,0-0,0}
    10-20 19:35:03.733 18046-18046/com.example.lenovo.listviewtest E/getView:--2﹕ android.widget.LinearLayout{4299d570 V.E..... ......I. 0,0-0,0}
    10-20 19:35:03.737 18046-18046/com.example.lenovo.listviewtest E/getView:--3﹕ android.widget.LinearLayout{4299ec20 V.E..... ......I. 0,0-0,0}
    10-20 19:35:03.740 18046-18046/com.example.lenovo.listviewtest E/getView:--4﹕ android.widget.LinearLayout{429a02d0 V.E..... ......I. 0,0-0,0}
    10-20 19:35:03.743 18046-18046/com.example.lenovo.listviewtest E/getView:--5﹕ android.widget.LinearLayout{429a1980 V.E..... ......I. 0,0-0,0}
    10-20 19:35:03.747 18046-18046/com.example.lenovo.listviewtest E/getView:--6﹕ android.widget.LinearLayout{429a3030 V.E..... ......I. 0,0-0,0}
    10-20 19:35:03.750 18046-18046/com.example.lenovo.listviewtest E/getView:--7﹕ android.widget.LinearLayout{429a46e0 V.E..... ......I. 0,0-0,0}
    10-20 19:35:11.330 18046-18046/com.example.lenovo.listviewtest E/getView:--8﹕ android.widget.LinearLayout{429a9638 V.E..... ......I. 0,0-0,0}
    10-20 19:35:13.503 18046-18046/com.example.lenovo.listviewtest E/getView:--0﹕ android.widget.LinearLayout{429ab200 V.E..... ......I. 0,0-0,0}
    10-20 19:35:15.697 18046-18046/com.example.lenovo.listviewtest E/getView:--8﹕ android.widget.LinearLayout{429ad050 V.E..... ......I. 0,0-0,0}
    10-20 19:35:18.773 18046-18046/com.example.lenovo.listviewtest E/getView:--8﹕ android.widget.LinearLayout{429aed90 V.E..... ......I. 0,0-0,0}
    10-20 19:35:20.477 18046-18046/com.example.lenovo.listviewtest E/getView:--9﹕ android.widget.LinearLayout{429b0888 V.E..... ......I. 0,0-0,0}
    10-20 19:35:20.634 18046-18046/com.example.lenovo.listviewtest E/getView:--10﹕ android.widget.LinearLayout{429b21c0 V.E..... ......I. 0,0-0,0}
    10-20 19:35:21.484 18046-18046/com.example.lenovo.listviewtest E/getView:--2﹕ android.widget.LinearLayout{429b3ba8 V.E..... ......I. 0,0-0,0}
    10-20 19:35:21.604 18046-18046/com.example.lenovo.listviewtest E/getView:--1﹕ android.widget.LinearLayout{429b54e0 V.E..... ......I. 0,0-0,0}
    10-20 19:35:21.680 18046-18046/com.example.lenovo.listviewtest E/getView:--0﹕ android.widget.LinearLayout{429b6d90 V.E..... ......I. 0,0-0,0}

    2.  上面的例子是一个view就一个TextView组件,但是如果当item的结构比较复杂时,通过convertView.findById()来查找每个组件id就会很费时,如果建立一个ViewHold的话,就避免了在view容器中查找组件的耗时工作了。

    10-20 20:08:49.140 3604-3604/com.example.lenovo.listviewtest E/getView:--0﹕ null
    10-20 20:08:49.146 3604-3604/com.example.lenovo.listviewtest E/getView:--1﹕ null
    10-20 20:08:49.150 3604-3604/com.example.lenovo.listviewtest E/getView:--2﹕ null
    10-20 20:08:49.153 3604-3604/com.example.lenovo.listviewtest E/getView:--3﹕ null
    10-20 20:08:49.156 3604-3604/com.example.lenovo.listviewtest E/getView:--4﹕ null
    10-20 20:08:49.160 3604-3604/com.example.lenovo.listviewtest E/getView:--5﹕ null
    10-20 20:08:49.160 3604-3604/com.example.lenovo.listviewtest E/getView:--6﹕ null
    10-20 20:08:49.163 3604-3604/com.example.lenovo.listviewtest E/getView:--7﹕ null
    10-20 20:08:53.760 3604-3604/com.example.lenovo.listviewtest E/getView:--8﹕ null
    10-20 20:08:55.023 3604-3604/com.example.lenovo.listviewtest E/getView:--9﹕ android.widget.LinearLayout{42999078 V.E..... ........ 0,-137-656,3}    //item--0   convertView = Recycler中的type1
    10-20 20:08:56.040 3604-3604/com.example.lenovo.listviewtest E/getView:--9﹕ android.widget.LinearLayout{42999078 V.E..... ........ 0,1072-656,1212}
    10-20 20:08:56.190 3604-3604/com.example.lenovo.listviewtest E/getView:--10﹕ android.widget.LinearLayout{4299c098 V.E..... ........ 0,-132-656,8}  //item--1   convertView = Recycler中的type2
    10-20 20:08:57.153 3604-3604/com.example.lenovo.listviewtest E/getView:--11﹕ android.widget.LinearLayout{4299d498 V.E..... ........ 0,-139-656,1}
    10-20 20:08:58.500 3604-3604/com.example.lenovo.listviewtest E/getView:--12﹕ android.widget.LinearLayout{4299e898 V.E..... ........ 0,-132-656,8}
    10-20 20:09:00.160 3604-3604/com.example.lenovo.listviewtest E/getView:--13﹕ android.widget.LinearLayout{4299fc98 V.E..... ........ 0,-134-656,6}
    10-20 20:09:00.343 3604-3604/com.example.lenovo.listviewtest E/getView:--14﹕ android.widget.LinearLayout{429a1098 V.E..... ........ 0,-130-656,10}
    10-20 20:09:02.023 3604-3604/com.example.lenovo.listviewtest E/getView:--15﹕ android.widget.LinearLayout{429a2498 V.E..... ........ 0,-139-656,1}
    10-20 20:09:03.957 3604-3604/com.example.lenovo.listviewtest E/getView:--16﹕ android.widget.LinearLayout{429a3898 V.E..... ........ 0,-135-656,5}
    10-20 20:09:06.000 3604-3604/com.example.lenovo.listviewtest E/getView:--7﹕ android.widget.LinearLayout{429a3898 V.E..... ........ 0,1053-656,1193}
    10-20 20:09:06.137 3604-3604/com.example.lenovo.listviewtest E/getView:--6﹕ android.widget.LinearLayout{429a2498 V.E..... ........ 0,1043-656,1183} //item--6  从convertView中取得
    10-20 20:09:06.270 3604-3604/com.example.lenovo.listviewtest E/getView:--5﹕ android.widget.LinearLayout{429a1098 V.E..... ........ 0,1063-656,1203}

    在滑动ListView的时候,当最下面显示item--9的时候,item--0就存在Recycler的type1中,当convertView就不会为null了,同时通过观察以上的getView--15可以知道,此时item--6的视图在Recycler中,当向上滑动ListView显示到getView--6的时候,是从convertView中获取的,而不是新实例化layout得到的。

    总结: 通过对比,显然复用convertView可以提高ListView的显示效略,起到优化作用。同时如果item内部的组件多的时候,建议使用ViewHold, 可以提高在view容器中查找组件的效率。

    三、使用分页加载数据

     当客户端从服务端获取数据量过大,一般为用户考虑流量,我们都需要对数据进行分页加载(特别是图片的加载),就上面的例子,模拟ListView的分页显示和下拉刷新功能。需要用到ListView的滚动监听。

     1 public class ListViewActivity extends Activity implements AbsListView.OnScrollListener{
     2     private Vector<String> list = new Vector<>(); //Vector保证线程安全
     3     private ListView listView;
     4     private ItemAdapter itemAdapter;
     5     private int index = 0;
     6     private int count = 0;
     7 
     8     @Override
     9     protected void onCreate(Bundle savedInstanceState) {
    10         super.onCreate(savedInstanceState);
    11         setContentView(R.layout.activity_list_view);
    12         listView = (ListView) findViewById(R.id.listView);
    13         listView.addFooterView(getLayoutInflater().inflate(R.layout.footer_view, null));
    14         initData();
    15         itemAdapter = new ItemAdapter();
    16         listView.setAdapter(itemAdapter);
    17         listView.setOnScrollListener(this);
    18     }
    19 
    20     public void initData(){
    21         for(int i = 0; i < 20; i++){
    22             list.add("item--" + index++);
    23         }
    24     }
    25 
    26     private Handler handler = new Handler(){
    27         @Override
    28         public void handleMessage(Message msg) {
    29             if(msg.what == 100){
    30                 itemAdapter.notifyDataSetChanged(); //adapter更新数据,listView变化
    31             }
    32         }
    33     };
    34 
    35     Runnable r = new Runnable() {
    36         @Override
    37         public void run() {
    38             initData();
    39             try {
    40                 Thread.sleep(2000); //休眠2秒
    41             } catch (InterruptedException e) {
    42                 e.printStackTrace();
    43             }
    44 
    45             handler.sendEmptyMessage(100);
    46         }
    47     };
    48 
    49     @Override
    50     public void onScrollStateChanged(AbsListView view, int scrollState) {
    51         if(itemAdapter.getCount() == count && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
    52             new Thread(r).start();  //子线程运行
    53         }
    54     }
    55 
    56     @Override
    57     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    58         count = firstVisibleItem + visibleItemCount - 1; //减1是减去listView的footerView
    59     }
    60 
    61     class ItemAdapter extends BaseAdapter {
    62 
    63         @Override
    64         public int getCount() {
    65             return list.size();
    66         }
    67 
    68         @Override
    69         public Object getItem(int position) {
    70             if(position >= 0 && position < list.size()) return list.get(position);
    71             return null;
    72         }
    73 
    74         @Override
    75         public long getItemId(int position) {
    76             return position;
    77         }
    78 
    79         @Override
    80         public View getView(int position, View convertView, ViewGroup parent) {
    81             Log.e("getView:--" + position, convertView + "");  //观察convertView值
    82             ViewHolder vh;
    83             if(convertView == null){
    84                 convertView  = getLayoutInflater().inflate(R.layout.list_item, null);
    85                 vh = new ViewHolder();
    86                 vh.textView = (TextView) convertView.findViewById(R.id.textView);
    87                 convertView.setTag(vh);
    88             }else{
    89                 vh = (ViewHolder) convertView.getTag();
    90             }
    91             vh.textView.setText("item--" + position);
    92             return convertView;
    93         }
    94 
    95          class ViewHolder{
    96             TextView textView;
    97         }
    98     }
    99 }
    View Code

    listView页脚:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="horizontal" android:layout_width="match_parent"
     4     android:layout_height="wrap_content" android:gravity="center_horizontal">
     5 
     6     <ProgressBar
     7         style="?android:attr/progressBarStyleSmall"
     8         android:layout_width="wrap_content"
     9         android:layout_height="wrap_content"
    10         android:id="@+id/progressBar" />
    11 
    12     <TextView
    13         android:layout_width="wrap_content"
    14         android:layout_height="wrap_content"
    15         android:textAppearance="?android:attr/textAppearanceSmall"
    16         android:text="正在努力加载..."
    17         android:id="@+id/textView" />
    18 </LinearLayout>
    View Code

    四、快速滚动时, item中可以不显示耗时加载的图片或其他资源

    一般情况下需要从网络获取图片显示需要做缓存处理,那么此时可以实现ListView的滚动监听OnScrollListener接口中的方法:

     1   private int startPosition;
     2   private int endPosition;
     3     @Override
     4     public void onScrollStateChanged(AbsListView view, int scrollState) {
     5         switch (scrollState){
     6             case SCROLL_STATE_IDLE:{
     7                 if(itemAdapter.getCount() == count){
     8                     new Thread(r).start();  //子线程运行
     9                 }
    10                
    11                 for(int i = startPosition; i < endPosition; i++){
    12                     //可以设置图片的加载显示
    13                 }
    14                 break;
    15             }
    16             case SCROLL_STATE_TOUCH_SCROLL:
    17                 //可以设置图片的加载显示
    18                 //...
    19                 break;
    20             case SCROLL_STATE_FLING: //快速滚动
    21                 break;
    22             
    23         }
    24     }
    25 
    26     @Override
    27     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    28         count = firstVisibleItem + visibleItemCount - 1; //减1是减去listView的footerView
    29         startPosition = firstVisibleItem; //可视起始的item位置
    30         endPosition = count; //可视最后的item位置
    31     }
  • 相关阅读:
    机器学习西瓜书笔记---1、绪论
    机器学习西瓜书白话解读笔记---0101-0102、绪论基本概念
    机器学习西瓜书白话解读笔记---0000、资料相关
    心得体悟帖---201123(游戏心得)
    PCA数学原理
    如何通俗易懂地解释卷积
    git操作中出现Unlink of file '......' failed. Should I try again?
    checker jenkins 启动配置
    Linux下Shell脚本运行程序不输出日志到终端
    Spring boot 配置文件位置
  • 原文地址:https://www.cnblogs.com/denluoyia/p/5980167.html
Copyright © 2011-2022 走看看