ListView 的优化(原)
随着ListView的不断深入使用,对于其的优化是必不可免的一个过程,现把其常见的优化步骤分享下,一些粗浅见识。。。
优化分四步走:
第一,复用convertView对象,如果之前有条目对象,就复用,否则就去创建
第二,为了减少findViewById次数,将findViewById已经找到的控件,做一个存储,存储到ViewHolder中,viewHolder存储到复用的convertView中
第三,将ViewHolder定义成静态,字节码加载一次
第四,通过分页算法,进一步优化用户体验(每一次加载20条数据,下一次加载的数据要添加到上一次数据后面)
详细步骤截图如下:
package cn.itae.listview_optimize; import java.util.List; import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Contacts.Intents.Insert;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.TextView;
import cn.itae.listview_optimize.db.dao.BlackNumberDao;
import cn.itae.listview_optimize.db.domain.BlackNumberInfo; public class MainActivity extends Activity { private ListView lv_blackNumber;
private Button bt_add;
private BlackNumberDao mDao;
private List<BlackNumberInfo> mBlackNumberList;
private int mTotalCount;
private MyAdapter mAdapter;
protected boolean isload = false;
private int mode = 1;
private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) {
// 给listView添加数据
if (mAdapter == null) {
mAdapter = new MyAdapter();
lv_blackNumber.setAdapter(mAdapter);
} else {
// /加载更多的时候,因为已经有数据适配,只需要做刷新即可
mAdapter.notifyDataSetChanged();
// 设置判断是否加载下一次数据的标识
// false允许开启下一次的加载过程
isload = false;
}
};
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); initUI(); initData();
} class MyAdapter extends BaseAdapter { public int getCount() { return mBlackNumberList.size();
} public BlackNumberInfo getItem(int position) { return mBlackNumberList.get(position);
} public long getItemId(int position) { return position;
} public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
// 1 、复用convertView
if (convertView == null) {
holder = new ViewHolder();
convertView = View.inflate(getApplicationContext(),
R.layout.list_item_view, null); // 2、通过ViewHolder类,减少findViewById的次数
// 获取条目上的控件
holder.tv_phone = (TextView) convertView
.findViewById(R.id.tv_phone);
holder.tv_mode = (TextView) convertView
.findViewById(R.id.tv_mode);
holder.iv_delete = (ImageView) convertView
.findViewById(R.id.iv_delete);
// 给convertView设置一个tag,tag就是要存储的holder对象
convertView.setTag(holder);
} else {
// 将convertView中的holder对象取出来,给复用的条目去做控件中的赋值操作
holder = (ViewHolder) convertView.getTag();
} // 给控件赋值
// 从集合中获取数据 给控件赋值
final BlackNumberInfo blackNumberInfo = mBlackNumberList
.get(position); holder.tv_phone.setText(blackNumberInfo.getPhone()); // 给图片按钮设置删除点击事件
holder.iv_delete.setOnClickListener(new OnClickListener() { public void onClick(View v) {
// 1从数据库中删除一条数据
mDao.delete(blackNumberInfo.getPhone());
// 2在集合中删除一条数据
mBlackNumberList.remove(blackNumberInfo); // 3.告知适配器刷新
mAdapter.notifyDataSetChanged();
}
}); // 显示模式
switch (blackNumberInfo.getMode()) {
case 1:
// 短信
holder.tv_mode.setText("拦截短信");
break;
case 2:
// 电话
holder.tv_mode.setText("拦截电话");
break;
case 3:
// 所有
holder.tv_mode.setText("拦截所有");
break;
} return convertView;
}
} // 4,分页算法(每一次加载20条数据,下一次加载的数据要添加到上一次数据后面),数据库,网络请求
// 3、将ViewHolder定义成静态,字节码文件只加载一次
static class ViewHolder {
TextView tv_phone;
TextView tv_mode;
ImageView iv_delete;
} /**
* 初始化数据,将之前加入黑名单的号码从数据库中获取出来
*/
private void initData() {
// 查询数据库中数据是耗时操作,需要放到子线程中去执行
new Thread() { public void run() {
mBlackNumberList = mDao.find(0);
mTotalCount = mDao.findTotalCount(); // 通过消息处理机制告知主线程可以使用获得的数据了
mHandler.sendEmptyMessage(0);
};
}.start();
} private void initUI() { mDao = BlackNumberDao.getInstance(this); lv_blackNumber = (ListView) findViewById(R.id.lv_blackNumber);
bt_add = (Button) findViewById(R.id.bt_add); bt_add.setOnClickListener(new OnClickListener() { public void onClick(View v) {
// 弹出对话框
showDialog();
}
}); lv_blackNumber.setOnScrollListener(new OnScrollListener() { public void onScrollStateChanged(AbsListView view, int scrollState) {
// 滚动状态发生改变方法
// OnScrollListener.SCROLL_STATE_FLING;正在飞速的滚动
// OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;用户触摸滚动
// OnScrollListener.SCROLL_STATE_IDLE;滚动状态有滚动--->空闲
// 如果由滚动变成空闲,并且滚动到最后一个条目
// (最后一个可见条目的索引>=集合大小-1)则需要加载更多数据 if (mBlackNumberList != null) {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE && lv_blackNumber.getLastVisiblePosition() >= mBlackNumberList.size() - 1 && !isload) { if (mBlackNumberList.size() < mTotalCount) {
//加载下一页数据
new Thread(){
public void run() {
//下一页查询到的数据
List<BlackNumberInfo> moreData = mDao.find(mBlackNumberList.size());
//添加到上一页数据集合后面
mBlackNumberList.addAll(moreData);
//本次在添加的时候,不能进行下一次加载,下一次加载必须受到上一次加载完成的
isload = true;
//告知主线程,listV可以使用查询到的数据
mHandler.sendEmptyMessage(0);
};
}.start();
}
}
}
} public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) { }
});
} private void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
final AlertDialog dialog = builder.create(); View view = View.inflate(this, R.layout.dialog_add_blacknumber, null); final EditText et_phone = (EditText) view.findViewById(R.id.et_phone); Button bt_submit = (Button) view.findViewById(R.id.bt_submit);
Button bt_cancel = (Button) view.findViewById(R.id.bt_cancel); RadioGroup rg_group = (RadioGroup) view.findViewById(R.id.rg_group); // 监听单选框的改变 (短信--电话--所有) rg_group.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(RadioGroup group, int checkedId) {
// 3,获取按钮组中选中的拦截类型
switch (checkedId) {
case R.id.rb_sms:
mode = 1;
break;
case R.id.rb_call:
mode = 2;
break;
case R.id.rb_all:
mode = 3;
break;
}
}
}); bt_submit.setOnClickListener(new OnClickListener() { public void onClick(View v) {
// 2,获取拦截电话电话号码
String phone = et_phone.getText().toString().trim();
// 4,插入数据库
mDao.insert(phone, mode);
// 5,创建一个BlacknumberInfo对象,将此对象更新到填充数据适配器的集合中去
BlackNumberInfo blackNumberInfo = new BlackNumberInfo();
blackNumberInfo.setMode(mode);
blackNumberInfo.setPhone(phone);
// 将提交的数据放在最上面
mBlackNumberList.add(0, blackNumberInfo); // 6,通知数据适配器刷新,刷新更改后的数据
mAdapter.notifyDataSetChanged(); dialog.dismiss();
}
}); bt_cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
}); // 为了兼容低版本
dialog.setView(view, 0, 0, 0, 0); dialog.show();
}
} //附上数据库操作部分代码
继承SqliteOpenHelper类的帮助类此处省略
package cn.itae.listview_optimize.db.dao; import java.util.ArrayList;
import java.util.List; import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import cn.itae.listview_optimize.db.BlackNumberOpenHelper;
import cn.itae.listview_optimize.db.domain.BlackNumberInfo; /**
* @author advance
* @E-mail:18943704697@163.com
* @qq:974467160 创建操作数据库中数据的一个单例对象
*/
public class BlackNumberDao {
private BlackNumberOpenHelper blackNumberOpenHelper; // 私有化构造方法
private BlackNumberDao(Context ctx) {
blackNumberOpenHelper = new BlackNumberOpenHelper(ctx);
} // 创建返回该类的对象
private static BlackNumberDao blackNumberDao = null; // 对外提供访问方法
public static BlackNumberDao getInstance(Context ctx) { if (blackNumberDao == null) {
blackNumberDao = new BlackNumberDao(ctx);
}
return blackNumberDao;
} // 提供访问数据库中数据的CRUD方法 /**
* @param phone
* 要添加黑名单的号码
* @param mode
* 选择黑名单的模式
*/
public void insert(String phone, int mode) {
// 获取数据操作对象
SQLiteDatabase db = blackNumberOpenHelper.getWritableDatabase(); ContentValues values = new ContentValues();
values.put("phone", phone);
values.put("mode", mode); // 由于要让后添加的黑名单号码显示在最前端,故按照id逆序排序
db.insert("blacknumber", null, values);
} /**
* @param phone
* 通过提供的号码去删除该黑名单
*/
public void delete(String phone) {
SQLiteDatabase db = blackNumberOpenHelper.getWritableDatabase(); db.delete("blacknumber", "phone = ?", new String[] { phone });
} /**
* @param phone
* 号码
* @param mode
* 黑名单类型 将指定的号码的黑名单类型改变
*/
public void update(String phone, int mode) {
SQLiteDatabase db = blackNumberOpenHelper.getWritableDatabase(); ContentValues values = new ContentValues();
values.put("mode", mode); db.update("blacknumber", values, "phone = ?", new String[] { phone });
} /**
* 一次查询20条
*
* @param index
* 查询的起始位置
*/
public List<BlackNumberInfo> find(int index) {
SQLiteDatabase db = blackNumberOpenHelper.getWritableDatabase();
List<BlackNumberInfo> blackNumberList = new ArrayList<BlackNumberInfo>(); // 依然要逆序查询,最后添加的数据,显示在最前面
String sql = "select phone,mode from blacknumber order by _id desc limit ?,20;";
Cursor cursor = db.rawQuery(sql, new String[] { index + "" }); // 将查询到的数据封装到javabean,再将javabean封装到集合中,方便去调用其中的数据
while (cursor.moveToNext()) {
BlackNumberInfo blackNumberInfo = new BlackNumberInfo(); blackNumberInfo.setPhone(cursor.getString(0));
blackNumberInfo.setMode(cursor.getInt(1)); blackNumberList.add(blackNumberInfo);
}
cursor.close();
db.close();
return blackNumberList;
} /**
* @return 返回数据库中的数据的总条数
*/
public int findTotalCount() {
int count = 0;
SQLiteDatabase db = blackNumberOpenHelper.getWritableDatabase(); String sql = "select count(*) from blacknumber;";
Cursor cursor = db.rawQuery(sql, null);
if (cursor.moveToNext()) {
// 获取总条目数
count = cursor.getInt(0);
}
cursor.close();
db.close();
return count;
}
}
//其他的selector选择器和布局文件此处省略....
结果图显示
ListView 的优化(原)的更多相关文章
- Android进阶笔记14:ListView篇之ListView性能优化
1. 首先思考一个问题ListView如何才能提高效率 ? 当convertView为空时候,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象.当convertView不 ...
- Android进阶笔记11:ListView篇之ListView性能优化
1. 首先思考一个问题ListView如何才能提高效率 ? 当convertView为空时候,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象.当convertView不 ...
- 【腾讯Bugly干货分享】跨平台 ListView 性能优化
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/FbiSLPxFdGqJ00WgpJ94yw 导语 精 ...
- Android之ListView性能优化——一行代码绑定数据——万能适配器
如下图,加入现在有一个这样的需求图,你会怎么做?作为一个初学者,之前我都是直接用SimpleAdapter结合一个Item的布局来实现的,感觉这样实现起来很方便(基本上一行代码就可以实现),而且也没有 ...
- android技巧(二)listview的优化
对于listview的优化有以下三个措施: 1.原有listview每一个item显示时都会调用一次getView()方法,实际上对于ListView而言,只需要保留能够显示的最大个数的view即可, ...
- Android Listview 性能优化
首先我一般使用的适配器是BaseAdapter,其中有两个方法最主要,分别是: getCount,getView, 在对Listview 进行优化的时候,首先使用 convertview 和viewH ...
- BaseAdapter以及对ListView的优化(转)
背景 对于ListView.GridView.Gallery.Spinner等等,它是它们的适配器,直接继承自接口类Adapter的,使用BaseAdapter时需要重写很多方法,其中最重要的当属ge ...
- ym——Android之ListView性能优化
转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103),谢谢支持! Android之ListView性能优化 假设有看过我写过的15k面试题的朋友们一定知 ...
- Android实训案例(五)——四大组件之一ContentProvider的使用,通讯录的实现以及ListView的优化
Android实训案例(五)--四大组件之一ContentProvider的使用,通讯录的实现 Android四大组件是啥这里就不用多说了,看图吧,他们之间通过intent通讯 我们后续也会一一的为大 ...
随机推荐
- iOS语音
<span style="white-space:pre"> </span>语音技术近来可是出遍了风头,从iphone4s的siri,到微信的语音聊天 ...
- 发布自己的pods到CocoaPods trunk 及问题记录
这两天准备把之前写的一些小玩意添加到pods库中去,参考了一些资料后进行操作,实际中也遇到了一些问题,记录下来,问题及解决方式在后面. 参考内容转载如下: 首先更新了用trunk之后,CocoaPod ...
- apache中虚拟主机的配置
一.两种方式:基于域名的虚拟主机和基于IP地址的的虚拟主机 (这里基于前者) 二.作用:实现在同一个web服务器下,同时运行很多个站点(项目) 三.虚拟主机的配置 1.在核心配置文件中加载虚拟主机配置 ...
- MFC-01-Chapter01:Hello,MFC---1.3 第一个MFC程序(01)
#include <afxwin.h> class CMyApp : public CWinApp { public: virtual BOOL InitInstance(); }; cl ...
- xcode意外退出
完全不明所以的频繁退出 第一种 排除SVN冲突 在团队开发中,SVN冲突是最常见的了,程序异常时查看SVN文件冲突基本上成了本能. 排除SVN冲突 首先,右键主项目文件即xcodeproj文件,显示包 ...
- Android 振动器
今天介绍一下Android的振动器Vibrator,有三个方法来控制手机振动: 1.void vibrate(long milliseconds):控制手机振动milliseconds毫秒. 2.vo ...
- doctype声明的重要性-------这绝对是ie的坑, 与angular无关, 我错怪你啦
今天开发一个页面, 自己写页面, 自己实现功能. 因为以往需求都没有要求兼容ie9, 所以并未发现此坑. 今天就记录下来. 贴图对比 ie9 chrome 如图, ie9界面显示错误. 起初以为是a ...
- UliPad ----python 开发利器
安装wxPython ...
- Git忽略规则及.gitignore规则不生效的解决办法
在git中如果想忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改根目录中 .gitignore 文件的方法(如无,则需自己手工建立此文件).这个文件每一行保存了一个匹配的规则例如: # 此为注 ...
- MVC中使用js拼接的元素验证问题
MVC使用的验证方式本质上是jquery.validation js 拼接的格式如下: <input class='required' title='请输入项目名称!' id='Project ...