Android 下分批加载数据以及listView使用过程中的优化

需求:在开发过程中,listview加载的数据如果比较大,这时为了提高用户体验感,我们应该事先分批加载以及下拉刷新功能

1、首先,数据访问层需要提供分批加载功能的接口,

代码如下:

package com.zaizai.safty.db.dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.zaizai.safty.db.BlackNumberDBOpenHelper;
import com.zaizai.safty.domain.BlackNumberInfo;

import java.util.ArrayList;
import java.util.List;

/**
 * 黑名单数据库的管理dao类
 * Created by zaizai on 2015/11/8.
 */
public class BlackNumberDao {
    private BlackNumberDBOpenHelper helper;


    /**
     * 构造方法
     *
     * @param context
     */
    public BlackNumberDao(Context context) {
        helper = new BlackNumberDBOpenHelper(context);

    }

    public boolean find(String number) {
        boolean result = false;
        SQLiteDatabase database = helper.getReadableDatabase();
        Cursor cursor = database.rawQuery("select * from blacknumber where number=?", new String[]{number});
        if (cursor.moveToNext()) {
            result = true;
        }
        return result;
    }

    /**
     * 查找部分数据
     *
     * @param offset    从offset开始获取数据
     * @param maxnumber 一次获取最大数据量
     * @return
     */
    public List<BlackNumberInfo> find(int offset, int maxnumber) {
        List<BlackNumberInfo> result = new ArrayList<BlackNumberInfo>();
        BlackNumberInfo info;
        SQLiteDatabase database = helper.getReadableDatabase();
        Cursor cursor = database.rawQuery("select number,mode from blacknumber order by _id desc limit ? offset ? ", new String[]{String.valueOf(maxnumber), String.valueOf(offset)});
        while (cursor.moveToNext()) {
            info = new BlackNumberInfo();
            String number = cursor.getString(0);
            String mode = cursor.getString(1);
            info.setNumber(number);
            info.setMode(mode);
            result.add(info);
        }
        cursor.close();
        database.close();
        return result;
    }

    /**
     * 查找全部数据
     *
     * @return
     */
    public List<BlackNumberInfo> findAll() {
        List<BlackNumberInfo> result = new ArrayList<BlackNumberInfo>();
        BlackNumberInfo info;
        SQLiteDatabase database = helper.getReadableDatabase();
        Cursor cursor = database.rawQuery("select number,mode from blacknumber order by _id desc", null);
        while (cursor.moveToNext()) {
            info = new BlackNumberInfo();
            String number = cursor.getString(0);
            String mode = cursor.getString(1);
            info.setNumber(number);
            info.setMode(mode);
            result.add(info);
        }
        cursor.close();
        database.close();
        return result;
    }

    /**
     * 增加黑名单号码
     *
     * @param number
     * @param mode   1 电话拦截 2 短信拦截 3 全部拦截
     */
    public void add(String number, String mode) {
        SQLiteDatabase database = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(BlackNumberDBOpenHelper.COLUMN_NUMBER, number);
        values.put(BlackNumberDBOpenHelper.COLUMN_MODE, mode);
        database.insert(BlackNumberDBOpenHelper.TABLE, null, values);
        database.close();
    }

    /**
     * 修改黑名单号码的拦截模式
     *
     * @param number 黑名单号码
     * @param mode   新的拦截模式
     */
    public void update(String number, String mode) {
        SQLiteDatabase database = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(BlackNumberDBOpenHelper.COLUMN_NUMBER, number);
        values.put(BlackNumberDBOpenHelper.COLUMN_MODE, mode);
        database.update(BlackNumberDBOpenHelper.TABLE, values, BlackNumberDBOpenHelper.COLUMN_NUMBER + "=?", new String[]{number});
        database.close();
    }

    /**
     * 删除黑名单号码的拦截模式
     *
     * @param number 黑名单号码
     */
    public void delete(String number) {
        SQLiteDatabase database = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        database.delete(BlackNumberDBOpenHelper.TABLE, BlackNumberDBOpenHelper.COLUMN_NUMBER + "=?", new String[]{number});
        database.close();
    }

    /**
     * 查询黑名单号码的拦截模式
     *
     * @param number
     * @return 返回号码的拦截模式,不是黑名单号码返回null
     */
    public String findMode(String number) {
        String result = null;
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select mode from blacknumber where number=?", new String[]{number});
        if (cursor.moveToNext()) {
            result = cursor.getString(0);
        }
        cursor.close();
        db.close();
        return result;
    }
}
可以看到方法public List<BlackNumberInfo> find(int offset, int maxnumber);
Cursor cursor = database.rawQuery("select number,mode from blacknumber order by _id desc limit ? offset ? ", new String[]{String.valueOf(maxnumber), String.valueOf(offset)});
我们根据offset 与maxnumber进行数据的定位访问
offset:从哪个位置开始查询,maxnumber 查询的最大数量
2、里面用到的domain 类如下
package com.zaizai.safty.domain;

/**
 * Created by zaizai on 2015/11/8.
 */
public class BlackNumberInfo {
    private String number;
    private String mode;

    @Override
    public String toString() {
        return "BlackNumberInfo{" +
                "number='" + number + '\'' +
                ", mode='" + mode + '\'' +
                '}';
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getMode() {
        return mode;
    }

    public void setMode(String mode) {
        this.mode = mode;
    }
}
3、完成了提供分批加载数据的到层后,由于数据访问经常都会用到,我们可将其封装成一个工具类代码如下:
提示:为了层与层之间的解耦,我们使用回调接口告知上一层,每次执行过程中的一些数据量信息,方便上一层获得该信息,
用来实现进度条提示功能
package com.zaizai.safty.utils;

import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 短信的工具类
 * Created by zaizai on 2015/11/9.
 */
public class SmsUtils {

    private static int max;

    /*备份短信的回调接口*/
    public interface BackUpCallBack {
        /**
         * 备份短信之前,设置进度的最大值
         *
         * @param max
         */
        public void beforeBackUp(int max);

        /**
         * 备份过程中,增加进度
         *
         * @param progress
         */
        public void onSmsBackUp(int progress);
    }

    /**
     * 恢复短信回调函数接口
     */
    public interface RestoreSmsBack {
        public void beforeRestore(int max);

        public void onRestore(int progress);
    }

    private static final String TAG = "zaizai";


    /**
     * 备份用户的短信
     *
     * @param context
     * @param backUpCallBack
     */
    public static void backUpSms(Context context, BackUpCallBack backUpCallBack) throws IOException, InterruptedException {
        ContentResolver resolver = context.getContentResolver();

        //在sd卡上创建文件
        File file = new File(Environment.getExternalStorageDirectory(), "backup.xml");
        Log.i(TAG, String.valueOf(Environment.getExternalStorageDirectory()));
        FileOutputStream fos = new FileOutputStream(file);
        //把用户的短信读出来,按一定格式写到文件里
        //1、获得序列化器
        XmlSerializer xmlSerializer = Xml.newSerializer();
        //2、初始化序列化器
        xmlSerializer.setOutput(fos, "utf-8");
        xmlSerializer.startDocument("utf-8", true);
        xmlSerializer.startTag(null, "smss");
        Uri uri = Uri.parse("content://sms/");
        Cursor cursor = resolver.query(uri, new String[]{"body", "address", "type", "date"}, null, null, null);
        //设置进度条最大值
        if (cursor != null) {
            max = cursor.getCount();
        }
        xmlSerializer.attribute(null, "max", max + "");
        backUpCallBack.beforeBackUp(max);
       /* progressDialog.setMax(cursor.getCount());
        */
        int process = 0;
        while (cursor != null && cursor.moveToNext()) {
            Thread.sleep(500);
            String body = cursor.getString(0);
            String address = cursor.getString(1);
            String type = cursor.getString(2);
            String date = cursor.getString(3);
            xmlSerializer.startTag(null, "sms");
            /*增加属性*/

            xmlSerializer.startTag(null, "body");
            xmlSerializer.text(body);
            xmlSerializer.endTag(null, "body");
            xmlSerializer.startTag(null, "address");
            xmlSerializer.text(address);
            xmlSerializer.endTag(null, "address");
            xmlSerializer.startTag(null, "type");
            xmlSerializer.text(type);
            xmlSerializer.endTag(null, "type");
            xmlSerializer.startTag(null, "date");
            xmlSerializer.text(date);
            xmlSerializer.endTag(null, "date");
            xmlSerializer.endTag(null, "sms");
            /*备份过程中增加进度*/
            process++;
          /*  progressDialog.setProgress(process);*/
            backUpCallBack.onSmsBackUp(process);
        }
        if (cursor != null) {
            cursor.close();
        }
        xmlSerializer.endTag(null, "smss");
        xmlSerializer.endDocument();
        fos.close();
    }

    /**
     * 还原短信
     *
     * @param context
     * @param flag    true is delete all sms
     */
    public static void restoreSms(Context context, boolean flag, RestoreSmsBack restoreSmsBack) throws Exception {
        Uri uri = Uri.parse("content://sms/");
        if (flag) {

            context.getContentResolver().delete(uri, null, null);
        }
        /*1、读取sd卡沙漠化的xml文件*/
        XmlPullParser parser = Xml.newPullParser();
        File file = new File(Environment.getExternalStorageDirectory(), "backup.xml");
        FileInputStream fileInputStream = new FileInputStream(file);

        parser.setInput(fileInputStream, "utf-8");
        /*2、读取max*/
        //得到当前节点的类型
        int eventype = parser.getEventType();
        String body = null;
        String date = null;
        String type = null;
        String address = null;
        String max = null;
        ContentValues values = null;
        /*一直解析到结束文档标签*/
        int process = 0;
        while (eventype != XmlPullParser.END_DOCUMENT) {
            String tagName = parser.getName();
            //jdk1.7开始switch支持string类型
            switch (eventype) {
                case XmlPullParser.START_TAG: {
                    /*开始标签*/
                    if ("smss".equals(tagName)) {
                        max = parser.getAttributeValue(null, "max");
                        Log.i(TAG, "max:" + max);
                        restoreSmsBack.beforeRestore(Integer.parseInt(max));
                    } else if ("body".equals(tagName)) {
                        body = parser.nextText();
                    } else if ("date".equals(tagName)) {
                        date = parser.nextText();
                    } else if ("type".equals(tagName)) {
                        type = parser.nextText();

                    } else if ("address".equals(tagName)) {
                        address = parser.nextText();
                    }
                    break;
                }
                case XmlPullParser.END_TAG: {
                    /*结束标签*/
                    if ("sms".equals(tagName)) {
                           /*3、读取每一条短信信息 body date type address*/
                            /*4、把短信插入到系统信息应用*/
                        values = new ContentValues();
                        values.put("body", body);
                        values.put("date", date);
                        values.put("type", type);
                        values.put("address", address);
                        context.getContentResolver().insert(uri, values);
                        process++;
                        restoreSmsBack.onRestore(process);
                        Thread.sleep(500);
                    }
                    break;
                }
                default: {
                    break;
                }
            }
            //得到下一个标签类型
            eventype = parser.next();
        }
        fileInputStream.close();
    }
}
4、工具类写好之后,我们便需要为listView对象添加滚动监听事件
4.1、listView 布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView

            android:id="@+id/textView1"
            android:layout_width="fill_parent"
            android:layout_height="55dip"
            android:background="@android:color/holo_green_dark"
            android:gravity="center"
            android:text="黑名单拦截"
            android:textColor="@android:color/black"
            android:textSize="22sp" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:onClick="addBlackNumber"
            android:text="添加" />
    </RelativeLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout

            android:id="@+id/ll_loading"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:visibility="invisible">

            <ProgressBar
                android:layout_width="match_parent"
                android:layout_height="60dip" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="正在加载数据......" />
        </LinearLayout>

        <ListView
            android:id="@+id/lv_call_sms_safe"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </FrameLayout>


</LinearLayout>
listView子视图的布局文件代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_black_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dip"
        android:layout_marginTop="3dip"
        android:text="黑名单号码"
        android:textColor="#000000"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/tv_black_mode"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_black_number"
        android:layout_marginLeft="10dip"
        android:layout_marginTop="3dip"
        android:text="拦截模式"
        android:textColor="#88000000"
        android:textSize="15sp" />

    <ImageView
        android:id="@+id/iv_delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="3dip"
        android:src="@drawable/delete_selector" />

</RelativeLayout>
4.2、activity代码如下:
package com.zaizai.safty;

import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.zaizai.safty.db.dao.BlackNumberDao;
import com.zaizai.safty.domain.BlackNumberInfo;

import java.util.List;

/**
 * Created by zaizai on 2015/11/8.
 */
public class CallSmsSafeActivity extends AppCompatActivity {
    private static final String TAG = "zaizai";
    private ListView lvCallSmsSafe;
    private List<BlackNumberInfo> infos;
    private BlackNumberDao dao;
    private CallSmsSafeAdapter adapter;
    private LinearLayout llLoading;
    private int offset;
    private int maxnumber = 20;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_call_sms_safe);
        lvCallSmsSafe = (ListView) findViewById(R.id.lv_call_sms_safe);
        llLoading = (LinearLayout) findViewById(R.id.ll_loading);
        dao = new BlackNumberDao(this);
        fillData();


        /*给listView注册滚动事件监听器*/
        lvCallSmsSafe.setOnScrollListener(new AbsListView.OnScrollListener() {
            /*滚动事件状态变化时调用*/
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                /*AbsListView当前滚动的view scrollState滚动状态*/
                switch (scrollState) {
                    case SCROLL_STATE_IDLE: {
                        /*闲置状态*/
                        Log.i(TAG, "闲置状态");
                        //1、判断当前listView滚动的位置
                        int firstPosition = lvCallSmsSafe.getFirstVisiblePosition();
                        Log.i(TAG, "第一个条目的位置" + firstPosition);
                        int lastPosition = lvCallSmsSafe.getLastVisiblePosition();
                        Log.i(TAG, "最后一个条目的位置" + lastPosition);
                        /*判断集合被移动到最后一个位置*/
                        if (lastPosition == (infos.size() - 1)) {
                            Log.i(TAG, "列表被移动到最后一个位置,加载更多数据");
                            offset = offset + maxnumber;
                            fillData();
                        }
                        break;
                    }
                    case SCROLL_STATE_TOUCH_SCROLL: {
                        /*手指触摸滚动*/
                        Log.i(TAG, "手指触摸滚动");
                        break;
                    }
                    case SCROLL_STATE_FLING: {
                        /*惯性滑行状态*/
                        Log.i(TAG, "惯性滑行状态");
                        break;

                    }

                }

            }

            /*滚动调用时调用*/
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });

    }

    private void fillData() {
    /*开启线程之前,设置进度条控件可见*/
        llLoading.setVisibility(View.VISIBLE);

        /*获取数据耗时,放入线程中*/
        new Thread(new Runnable() {
            @Override
            public void run() {
                //  infos = dao.findAll();
                if (infos == null) {
                    infos = dao.find(offset, maxnumber);
                } else {
                    infos.addAll(dao.find(offset, maxnumber));
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //llLoading.setVisibility(View.INVISIBLE);
                        llLoading.setVisibility(View.INVISIBLE);
                        if (adapter == null) {
                            adapter = new CallSmsSafeAdapter();
                            lvCallSmsSafe.setAdapter(adapter);
                        } else {
                            adapter.notifyDataSetChanged();
                        }

                    }
                });
            }
        }).start();
    }

    private class CallSmsSafeAdapter extends BaseAdapter {

        private View view;

        @Override
        public int getCount() {
            return infos.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        /**
         * 没显示出一个条目,调用一次getView()
         *
         * @param position
         * @param convertView
         * @param parent
         * @return
         */
        @Override
        public View getView(final int position, View convertView, final ViewGroup parent) {
            ViewHoder viewHoder;
            /*每移除一个条目,就会将条目赋值给convertView*/
            // Log.i(TAG, "position:" + position + "concertView" + convertView);
            //优化操作1、减少内存中view对象创建的个数
            if (convertView == null) {
                view = View.inflate(getApplicationContext(), R.layout.list_item_call_sms, null);
                viewHoder = new ViewHoder();
                viewHoder.tvBlackNumber = (TextView) view.findViewById(R.id.tv_black_number);
                viewHoder.tvBlackMode = (TextView) view.findViewById(R.id.tv_black_mode);
                viewHoder.ivDelete = (ImageView) view.findViewById(R.id.iv_delete);
                //找到id对应的空间后,把他保存大view中
                view.setTag(viewHoder);
            } else {
                view = convertView;
                viewHoder = (ViewHoder) view.getTag();
            }
            //减少Id查询的次数,大概提高百分之五

            viewHoder.tvBlackNumber.setText(infos.get(position).getNumber());
            String mode = infos.get(position).getMode();
            if ("1".equals(mode)) {
                viewHoder.tvBlackMode.setText("电话拦截");
            } else if ("2".equals(mode)) {
                viewHoder.tvBlackMode.setText("短信拦截");
            } else if ("3".equals(mode)) {
                viewHoder.tvBlackMode.setText("全部拦截");
            }
            //删除BlackNumber
            viewHoder.ivDelete.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(CallSmsSafeActivity.this);
                    builder.setTitle("警告");
                    builder.setMessage("确定要删除这条记录么?");
                    builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //1、删除数据库数据
                            dao.delete(infos.get(position).getNumber());
                            infos.remove(position);
                            /*通知listView数据适配器更新*/
                            adapter.notifyDataSetChanged();
                        }
                    });
                    builder.setNegativeButton("取消", null);
                    builder.create().show();
                }
            });
            return view;
        }
    }

    //记录内存地址
    static class ViewHoder {
        TextView tvBlackNumber;
        TextView tvBlackMode;
        ImageView ivDelete;
    }

    /**
     * onclick to add blacknumber method
     *
     * @param view
     */
    private EditText etBlackNumber;
    private CheckBox cbPhone;
    private CheckBox cbSms;
    private Button btConfim;
    private Button btCancel;

    public void addBlackNumber(View view) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        final AlertDialog dialog = builder.create();
        View contentView = View.inflate(this, R.layout.dialog_add_blacknumber, null);
        dialog.setView(contentView, 0, 0, 0, 0);
        dialog.show();
        /*空间初始化*/
        etBlackNumber = (EditText) contentView.findViewById(R.id.et_blacknumber);
        cbPhone = (CheckBox) contentView.findViewById(R.id.cb_phone);
        cbSms = (CheckBox) contentView.findViewById(R.id.cb_sms);
        btConfim = (Button) contentView.findViewById(R.id.bt_confim);
        btCancel = (Button) contentView.findViewById(R.id.bt_cancel);

        /*按钮设置监听事件*/
        btCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
        btConfim.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String blacknumber = etBlackNumber.getText().toString().trim();
                if (TextUtils.isEmpty(blacknumber)) {
                    Toast.makeText(getApplicationContext(), "黑名单号码不能为空", Toast.LENGTH_LONG).show();
                    return;
                }
                String mode;
                if (cbPhone.isChecked() && cbSms.isChecked()) {
                    //全部拦截
                    mode = "3";
                } else if (cbPhone.isChecked()) {
                    //电话拦截
                    mode = "1";
                } else if (cbSms.isChecked()) {
                    //短信拦截
                    mode = "2";
                } else {
                    Toast.makeText(getApplicationContext(), "请选择拦截模式", Toast.LENGTH_LONG).show();
                    return;
                }
                //数据被加到数据库
                dao.add(blacknumber, mode);
                //更新listview集合里面的内容。
                BlackNumberInfo info = new BlackNumberInfo();
                info.setMode(mode);
                info.setNumber(blacknumber);
                infos.add(0, info);
                //通知listview数据适配器数据更新了。
                adapter.notifyDataSetChanged();
                dialog.dismiss();

            }
        });


    }

}
从上面大家可以看到,我们添加监听事件的代码如下:
/*给listView注册滚动事件监听器*/
lvCallSmsSafe.setOnScrollListener(new AbsListView.OnScrollListener() {
    /*滚动事件状态变化时调用*/
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        /*AbsListView当前滚动的view scrollState滚动状态*/
        switch (scrollState) {
            case SCROLL_STATE_IDLE: {
                /*闲置状态*/
                Log.i(TAG, "闲置状态");
                //1、判断当前listView滚动的位置
                int firstPosition = lvCallSmsSafe.getFirstVisiblePosition();
                Log.i(TAG, "第一个条目的位置" + firstPosition);
                int lastPosition = lvCallSmsSafe.getLastVisiblePosition();
                Log.i(TAG, "最后一个条目的位置" + lastPosition);
                /*判断集合被移动到最后一个位置*/
                if (lastPosition == (infos.size() - 1)) {
                    Log.i(TAG, "列表被移动到最后一个位置,加载更多数据");
                    offset = offset + maxnumber;
                    fillData();
                }
                break;
            }
            case SCROLL_STATE_TOUCH_SCROLL: {
                /*手指触摸滚动*/
                Log.i(TAG, "手指触摸滚动");
                break;
            }
            case SCROLL_STATE_FLING: {
                /*惯性滑行状态*/
                Log.i(TAG, "惯性滑行状态");
                break;

            }

        }

    }

    /*滚动调用时调用*/
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }
});
以上为listView加载数据的优化,提高用户的体验感接下来我们将在程序运行过程中消耗资源的优化
二、listView本身的优化
在listView的使用过程中,每一个Item项,都是一个view对象,如果你有1000条数据,如果你不做处理的话,那么系统将会
创建1000个view对象用来展示,但是可能我们手机界面一次只能看到其中的一部分,比如20条,那么是否我们只需要创建20个
可以重复使用的对象便可以完成以上功能呢?这样的话,将大大降低资源的消耗
在上面贴出的代码中,
private class CallSmsSafeAdapter extends BaseAdapter {

    private View view;

    @Override
    public int getCount() {
        return infos.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    /**
     * 没显示出一个条目,调用一次getView()
     *
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getView(final int position, View convertView, final ViewGroup parent) {
        ViewHoder viewHoder;
        /*每移除一个条目,就会将条目赋值给convertView*/
        // Log.i(TAG, "position:" + position + "concertView" + convertView);
        //优化操作1、减少内存中view对象创建的个数
        if (convertView == null) {
            view = View.inflate(getApplicationContext(), R.layout.list_item_call_sms, null);
            viewHoder = new ViewHoder();
            viewHoder.tvBlackNumber = (TextView) view.findViewById(R.id.tv_black_number);
            viewHoder.tvBlackMode = (TextView) view.findViewById(R.id.tv_black_mode);
            viewHoder.ivDelete = (ImageView) view.findViewById(R.id.iv_delete);
            //找到id对应的空间后,把他保存大view中
            view.setTag(viewHoder);
        } else {
            view = convertView;
            viewHoder = (ViewHoder) view.getTag();
        }
        //减少Id查询的次数,大概提高百分之五

        viewHoder.tvBlackNumber.setText(infos.get(position).getNumber());
        String mode = infos.get(position).getMode();
        if ("1".equals(mode)) {
            viewHoder.tvBlackMode.setText("电话拦截");
        } else if ("2".equals(mode)) {
            viewHoder.tvBlackMode.setText("短信拦截");
        } else if ("3".equals(mode)) {
            viewHoder.tvBlackMode.setText("全部拦截");
        }
        //删除BlackNumber
        viewHoder.ivDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder = new AlertDialog.Builder(CallSmsSafeActivity.this);
                builder.setTitle("警告");
                builder.setMessage("确定要删除这条记录么?");
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //1、删除数据库数据
                        dao.delete(infos.get(position).getNumber());
                        infos.remove(position);
                        /*通知listView数据适配器更新*/
                        adapter.notifyDataSetChanged();
                    }
                });
                builder.setNegativeButton("取消", null);
                builder.create().show();
            }
        });
        return view;
    }
}

//记录内存地址
static class ViewHoder {
    TextView tvBlackNumber;
    TextView tvBlackMode;
    ImageView ivDelete;
}
原理:1、listView在使用时,已经创建的view对象从可视区域进入不可视区域时,系统会将其view对象的内存地址放入
convertView对象中,通过回调数据适配器的
@Override
public View getView(final int position, View convertView, final ViewGroup parent)
方法告知用户,那么我们便可通过检测convertView对象是都为Null便可获得进入不可视区域的view对象的内存地址
再次使用该对象。
2、那么view对象里面的展示基本信息的控件对象我们又该如何重复使用呢。
我们可以通过定义一个类来记录已经创建的view里面的控件对象的内存地址,
如下
//记录内存地址
static class ViewHoder {
    TextView tvBlackNumber;
    TextView tvBlackMode;
    ImageView ivDelete;
}
然后在创建view对象时将其内部的控件对象的地址复制给它
view = View.inflate(getApplicationContext(), R.layout.list_item_call_sms, null);
viewHoder = new ViewHoder();
viewHoder.tvBlackNumber = (TextView) view.findViewById(R.id.tv_black_number);
viewHoder.tvBlackMode = (TextView) view.findViewById(R.id.tv_black_mode);
viewHoder.ivDelete = (ImageView) view.findViewById(R.id.iv_delete);
通过view.setTag(viewHoder);使view对象记录内存地址的对象。
使用时,直接使用viewHoder对象来操作view对象内部的控件即可,完整代码上面已贴出

&#160;

&#160;

知识点记录:listview的优化。

1.复用历史缓存的view对象 converview  
2.减少子孩子id查询的次数  
3.分批的加载数据  
4.分页的加载数据


优化的原则: 拆东墙补西墙。

1.时间换时间(listview的分批加载数据)

2.时间换空间&#160; 文件拷贝

3.空间换时间&#160; 文件快速查找的索引。

4.空间换空间&#160; 虚拟内存&#160; RAMdisk

你可能感兴趣的:(android,ListView,xmlpullparser,sqlitedatabase,XMLSerializer)