需求:在开发过程中,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对象内部的控件即可,完整代码上面已贴出
 
 
知识点记录:listview的优化。
1.复用历史缓存的view对象 converview
2.减少子孩子id查询的次数
3.分批的加载数据
4.分页的加载数据
优化的原则: 拆东墙补西墙。
1.时间换时间(listview的分批加载数据)
2.时间换空间  文件拷贝
3.空间换时间  文件快速查找的索引。
4.空间换空间  虚拟内存  RAMdisk