ListView的用途
当你希望展示的内容很大量的时候,这个时候你应该使用ListView,它能够使你展示的内容随着屏幕的滑动进而得到展示。
使用方法
- 在布局文件中添加ListView的控件
| 1 | <ListView | 
- 作为ListView本身是不知道要展示什么类型的数据,也不知道要展示数据的内容,这个时候你需要创建一个ListAdapter(提供数据来源和展示的规则)并与ListView进行关联。
| 1 | package com.example.bibingwei.listview; | 
ListView加载大量数据为什么没有出现OOM
RecycleBin机制
第一次Layout
- 数据还在Adapter那里,这个时候不断循环从屏幕的顶端开始进行绘制子元素,但绘制完整个屏幕的时候也就是最后一个元素超出屏幕的时候,停止绘制,然后将这一屏的子布局进行填充数据,至此第一次layout就结束了。
第二次Layout
- 这次将之前加载好的一屏的数据都清除掉,并重新进行加载。因为之前加载的数据已经被缓存起来了,这次加载不会消耗太多的时间
滑动加载更多数据
- 经过之前的两次Layout过程,现在我们可以在ListView中看到已经加载的数据了。这个时候ListView只加载了第一屏的数据。当我们滑动屏幕的时候,系统会判断我们是向上滑动还是向下滑动
- 如果向上滑动,那么就会将之前的View加入到废弃的缓存中去并计数加1,并进行detach操作。如果是向下滑动的话就继续加载View

ListView优化
Use a Background Thread
使用Background线程可以是主线程专心的绘制UI以便提高效率。常常会使用AsyncTask来进行耗时的操作。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// Using an AsyncTask to load the slow images in a background thread
new AsyncTask<ViewHolder, Void, Bitmap>() {
    private ViewHolder v;
    
    protected Bitmap doInBackground(ViewHolder... params) {
        v = params[0];
        return mFakeImageLoader.getImage();
    }
    
    protected void onPostExecute(Bitmap result) {
        super.onPostExecute(result);
        if (v.position == position) {
            // If this item hasn't been recycled already, hide the
            // progress and set and show the image
            v.progress.setVisibility(View.GONE);
            v.icon.setVisibility(View.VISIBLE);
            v.icon.setImageBitmap(result);
        }
    }
}.execute(holder);
使用ViewHolder
当你滑动ListView的时候你可能会不断的findById,这样就可能造成OOM
在ViewHolder中会存储布局中需要用来展示内容的控件,所以这个时候不用每次都去查询并新建
| 1 | static class ViewHolder { | 
填充ViewHolder并存储在布局中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69package com.example.bbw.listviewtest;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
 * @author bbw
 * @date 2017/6/23
 */
public class FruitAdapter extends ArrayAdapter<Fruit> {
    private int resourceId;
    /**
     * @param context
     * @param textViewResourceId 子项布局的id
     * @param objects 数据源
     */
    FruitAdapter( Context context, int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId,objects);
        resourceId = textViewResourceId;
    }
    /**
     * @param position 位置信息
     * @param convertView 缓存view
     * @param parent 父布局
     * @return
     */
    
    
    public View getView(int position,  View convertView,  ViewGroup parent) {
        Fruit fruit = getItem(position);
        View view ;
        ViewHolder viewHolder;
        if (convertView == null){
            //记住
            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
            //提高效率,不必每次获取控件的id
            viewHolder = new ViewHolder();
            viewHolder.fruitImage = view.findViewById(R.id.fruitImage);
            viewHolder.fruitName = view.findViewById(R.id.fruitName);
            view.setTag(viewHolder);
        }else{
            //是否有缓存,convertView就是用于将之前加载好的布局进行缓存以便重用
            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.fruitImage.setImageResource(fruit.getImageId());
        viewHolder.fruitName.setText(fruit.getName());
        return view;
    }
    private class ViewHolder {
        ImageView fruitImage;
        TextView fruitName;
    }
}
