随着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选择器和布局文件此处省略....
结果图显示