需求:在开发过程中,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