[mobilesafe] 11_黑名单管理优化——ListView的优化

Android 4.0

黑名单管理优化

优化1:View view = View.inflate(getApplicationContext(), R.layout.listview_callsms_activity_itemnull);  
如果将xml布局转成view对象,让自己来写:
1)以流形式关联xml文件,将其加载到内存
2)将xml文件中的内容用pull解析出来,一个个对象
3)根据xml中的属性和内容给相应的对象设置
可以发现,如果ListView内容过多,拖动的数据过快,
也就是 public View getView(int position, View convertView, ViewGroup parent){}
方法不断地执行,此时耗费很大的内存,可能会出现dalvikvm虚拟机内存不足的异常。

解决:getView方法中,View convertView代表的就是用户不可见的view对象
convertView The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view. Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int)).
注意:复用该view,需要注意三点
1、检查该view是否为null,要不为null
2、检查该view的类型,instanceof
3、显示的数据要改变
核心代码:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    
    // 1、转换xml为view对象
    //进行复用以前创建的view对象
    View view = null;
    if (convertView!=null && convertView instanceof RelativeLayout) {//a) 如果不为null,且类型正确,那么复用该view对象
        view = convertView;
        Log.i(TAG"复用旧的view对象:" + position);
    }else { //b) 否则,就创建一个
        view = View.inflate(getApplicationContext(), R.layout.listview_callsms_activity_itemnull);
        Log.i(TAG"新创建的view对象:" + position);
    }
    
    // 2、通过view找到里面的组件
    TextView tv_number = (TextView) view.findViewById(R.id.tv_listitem_blacknumber);
    TextView tv_mode = (TextView) view.findViewById(R.id.tv_listitem_blackmode);
    
    // 3、获取对应位置的BlackNumberInfo对象
    BlackNumberInfo blackNumberInfo = numberInfos.get(position);
    
    // 4、设置数据
    tv_number.setText(blackNumberInfo.getNumber());
    String mode = blackNumberInfo.getMode();
    if ("1".equals(mode)) {
        mode = "电话拦截";
    } else if ("2".equals(mode)) {
        mode = "短信拦截";
    } else if ("3".equals(mode)) {
        mode = "全部拦截(电话拦截+短信拦截)";
    }
    tv_mode.setText(mode);
    // 5、返回view对象,一定要返回
    return view;
}
结果:
问题2:查找孩子也是很耗内存的,一颗树,通过算法来查找孩子,所以需要将其保存起来
TextView tv_number = (TextView) view.findViewById(R.id.tv_listitem_blacknumber);
TextView tv_mode = (TextView) view.findViewById(R.id.tv_listitem_blackmode);

1)定义一个ViewHolder来保存
/**
 * 3、保存着TextView对象的引用,优化,使用static效率更高些
 * @author hacket
 */
private static class ViewHolder {
    TextView tv_number;// 黑名单拦截号码
    TextView tv_mode;// 黑名单拦截模式
}

2)在新建View的时候,创建ViewHolder,并通过id查找对象的组件,
// ba)新建view对象时,new出来viewholder,保存引用  
viewHolder = new ViewHolder();
viewHolder.tv_number = (TextView) view.findViewById(R.id.tv_listitem_blacknumber);
viewHolder.tv_mode = (TextView) view.findViewById(R.id.tv_listitem_blackmode);
// bb) view关联起来viewholder
view.setTag(viewHolder); 
//A tag can be used to mark a view in its hierarchy and does not have to be unique within the hierarchy.

3) 在复用的时候,查找到view绑定的tag对应的对象ViewHolder
viewHolder = (ViewHolder) view.getTag();// 获取view关联的tag,viewholder,里面保存着,之间找到的组件,就不用再去find组件了
核心代码:
/**
 * 2、黑名单显示ListView的Adapter适配器
 * @author hacket
 */
private class CallSmsSafeAdapter extends BaseAdapter {
    private static final String TAG = "CallSmsSafeActivity";
    @Override
    public int getCount() {
        if (numberInfos!=null) {
            return numberInfos.size();
        }
        return 0;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        
        // 1、转换xml为view对象
        //进行复用以前创建的view对象
        View view = null;
        ViewHolder viewHolder = null;
        if (convertView!=null && convertView instanceof RelativeLayout) {
        //a) 如果不为null,且类型正确,那么复用该view对象
            Log.i(TAG"复用旧的view对象:" + position);
            view = convertView;
            
            // 在复用的时候,查找到view绑定的tag对应的对象ViewHolder
            viewHolder = (ViewHolder) view.getTag();
           // 获取view关联的tag,viewholder,里面保存着,之间找到的组件,就不用再去find组件了
        } else { // b) 否则,就新创建一个view对象
            Log.i(TAG"新创建的view对象:" + position);
            view = View.inflate(getApplicationContext(), R.layout.listview_callsms_activity_itemnull);
            // ba)新建view对象时,new出来viewholder,保存引用
            viewHolder = new ViewHolder();
            viewHolder.tv_number = (TextView) view.findViewById(R.id.tv_listitem_blacknumber);
            viewHolder.tv_mode = (TextView) view.findViewById(R.id.tv_listitem_blackmode);
            // bb) view关联起来viewholder
            view.setTag(viewHolder); 
            //A tag can be used to mark a view in its hierarchy and does not have to be unique within the hierarchy
        }
        
        // 2、通过view找到里面的组件,很耗内存和时间
        //TextView tv_number = (TextView) view.findViewById(R.id.tv_listitem_blacknumber);
        //TextView tv_mode = (TextView) view.findViewById(R.id.tv_listitem_blackmode);
        
        // 3、获取对应位置的BlackNumberInfo对象
        BlackNumberInfo blackNumberInfo = numberInfos.get(position);
        
        // 4、设置数据
        viewHolder.tv_number.setText(blackNumberInfo.getNumber());
        String mode = blackNumberInfo.getMode();
        if ("1".equals(mode)) {
            mode = "电话拦截";
        } else if ("2".equals(mode)) {
            mode = "短信拦截";
        } else if ("3".equals(mode)) {
            mode = "全部拦截(电话拦截+短信拦截)";
        }
        viewHolder.tv_mode.setText(mode);
        // 5、返回view对象,一定要返回
        return view;
    }
    @Override
    public Object getItem(int position) {
        return null;
    }
    @Override
    public long getItemId(int position) {
        return 0;
    }
}
结果:
优化3:允许ListView快速拖动android:fastScrollEnabled="true"
<ListView
    android:fastScrollEnabled="true"
    android:id="@+id/lv_callsmssafe_activity"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</ListView>
之后在右边就会有一个小方块,可以拖动的
总结:ListView的优化
1、 View convertView,view对象的复用

a) 判空
b) 判断类型
2、ViewHolder,用来保存已经通过id查找到的组件
3、ListView的 android:fastScrollEnabled = "true" 属性,允许快速滚动,默认为false