前言
前段时间做了客户列表功能,侧边加了数字导航栏,当时在网上随便找了一个侧边类库加上了,过了一段时间,用户报这个功能加载缓慢的越来越多,于是登录客户真机看了一下,发现是用户数据太多的原因
问题描述
为什么要加载数据缓慢呢?没有做上拉刷新吗?
1,因为要一次将几千条数据加载全
2,因为点击侧边字母后listview需要滚动到指定位置,不好做分页
所以如果有大量数据加上侧边导航看上去是无解
误区
网上很多侧边字母导航Demo 都是直接在代码中用一个集合加载一堆数据,然后一个一个的解析首字母,如果直接这样实现而且数据量大的话会导致页面卡顿
android官方实现方式
于是我查阅了android官方通讯的代码的实现方式,结果发现官方实现方式是实现利用LoaderManager 异步加载cursor数据集,然后将cursor传递给cursorAdapter数据
为什么要用cursor 呢?
cursor 加载数据是不用时间的,所以即使刚记入页面的时候加载几千数据大约也只用了一秒,而如果让Sqlite直接返回数据集的话,虽然放在异步不会导致主线程卡顿,但是也会消耗一定时间
使用了cursor后和即使将所有数据存在内存中这两种加载方式对比,明显感觉cursor要快,看了cursorAdapter 代码知道当item要展示的时候才去加载数据,加载的粒度更细
整个实现过程
1,将数据从服务器拉取下来存到数据库时,事先提取和缓存首字母
2,首次加载时将字母排序
3,在加载同时查询侧边栏字母(去重复,非26个)
4,更新数据时,只要重复2,3 ,效果和调用listView的notifyDateChange 一样,不会重新刷新listview
加载所要展示的字母索引
select sortLetters,COUNT(*) as count from member GROUP BY sortLetters order by upper(sortLetters) asc
提取要展示的字母
labels = new String[numLabels];counts = new int[numLabels];
for (int i = 0; i < numLabels; i++) {
cursor.moveToNext();
labels[i] = cursor.getString(0);
counts[i] = cursor.getInt(1);
}
官方的有点复杂,这个索引还加了缓存,查询所有数据的代码省略了,只要返回一个排序后的cursor就行了
cursorAdapter中的部分方法
/** * @see android.widget.ListAdapter#getView(int, View, ViewGroup) */
public View getView(int position, View convertView, ViewGroup parent) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
View v;
if (convertView == null) {
v = newView(mContext, mCursor, parent);
} else {
v = convertView;
}
bindView(v, mContext, mCursor);
return v;
}
根据字母和数量得到字母position
mSections = sections;
mPositions = new int[counts.length];
int size = counts.length;
int position = 0;
for (int i = 0; i < size; i++) {
if (TextUtils.isEmpty(mSections[i])) {
mSections[i] = "#";
}
mPositions[i] = position;
position += counts[i];}mCount = position;
根据sectionIndex 获取postion
public int getPositionForSection(int sectionIndex) {
if (mSections == null || mPositions == null) {
return -1;
}
if (sectionIndex < 0 || sectionIndex >= mSections.length) {
return -1;
}
return mPositions[sectionIndex];
}
根据字母获取Position
public int getPositionForTitle(String title){
for (int i = 0; i < mSections.length; i++) {
if(mSections[i].equals(title)){
int position = getPositionForSection(i);
MLog.e("test","getPositionForTitle" + position);
return position;
}
}
return -1;
}
根据postion获取字母所在Section
@Override
public int getSectionForPosition(int position) {
if (mSections == null || mPositions == null) {
return -1;
}
if (position < 0 || position >= mCount) {
return -1;
}
int index = Arrays.binarySearch(mPositions, position);
return index;
}