不断扩展的ListView
技术点:1、sqlite分页查询: select * from table limit ? offset ?; limit:一页显示的最大数目 offset:从哪里开始查询,偏移量 2、 onScrollStateChanged()中 获取最后一个位置 int lastVisiblePosition = view.getLastVisiblePosition();// 获取最后一个可以的item的位置,从0开始 3、查询数据库,获取所有的值 count = cursor.getCount(); 4、数据适配器,用以前的 // 复用旧的数据适配器,通知数据适配器数据更新了
callSmsSafeAdapter.notifyDataSetChanged();
|
核心代码:
|
问题1:黑名单界面获取数据时,要等3秒钟才能进入 分析:由于getBlackNumberInfos()获取所有信息睡眠的3秒钟,是在主线程,相当于主线程睡眠3秒钟,setAdapter()也是更新UI操作,所以需要放在子线程中操作。 numberInfos = blackNumberDao.getBlackNumberInfos();
lv_callsmssafe.setAdapter(new CallSmsSafeAdapter());问题1解决:开启子线程操作 new Thread() {
@Override
public void run() {
numberInfos = blackNumberDao.getBlackNumberInfos();
lv_callsmssafe.setAdapter(new CallSmsSafeAdapter());
由于getBlackNumberInfos()获取所有信息睡眠的3秒钟,是在主线程,相当于主线程睡眠3秒钟,所以需要放在子线程中操作.
setAdapter()也是更新UI操作,不能放在子线程中,要发送消息。 }
}.start(); 但又出现问题2:子线程中不能更新UI 11-10 16:27:51.460: E/AndroidRuntime(645): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 问题2解决:通过消息机制来更新UI private Handler handler = new Handler() {// 消息机制,
@Override
public void handleMessage(Message msg) {
lv_callsmssafe.setAdapter(new CallSmsSafeAdapter());// 消息机制来更新UI
}
}; handler.sendEmptyMessage(0); //没有数据携带,可以发送空消息 问题3:但又有一个用户体验不好,界面不需要等3秒,但用户进来,需要等3秒钟白板才能看到数据,体验不好。 解决:使用分页查询 select * from table limit ? offset ?; 问题4:只能查询前20个, 解决:监听拖动的状态,在加载到最后一个item时, offset += limit; // b、为ListView设置滚动事件
lv_callsmssafe.setOnScrollListener(new OnScrollListener() {
// b-1)在滚动状态发生改变的时候调用的方法
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.i(TAG, "滚动状态发生变化~~~");
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:// a) listview静止时,这个重要
Log.i(TAG, "滚动状态变化到:" + "idle");
int lastVisiblePosition = view.getLastVisiblePosition();// 获取最后一个可以的item的位置,从0开始
int size = blackNumberInfos.size();// 获取的黑名单数据的大小,从1开始
if (lastVisiblePosition == (size - 1)) {
Log.i(TAG, "滚动到了最后一个了~~~,加载更多的数据~~~");
offset += limit;
fillBlackNameListView();// 到了最后一个,重新设置黑名单listview的数据
}
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:// b) 手指触摸滚动的时候
Log.i(TAG, "滚动状态变化到:" + "touch scroll");
break;
case OnScrollListener.SCROLL_STATE_FLING:// c) 手指已经离开,惯性滑行状态
Log.i(TAG, "滚动状态变化到:" + "fling");
break;
}
}问题5:拖动时,之间换了一页的内容,前面的内容就没有了,丢失了 解决:进行获取数据的时候,进行判断,如果黑名单集合为null,那么覆盖,如果不为null, 那么进行addAll()操作,将后面查找的数据加载到集合后面,防止前面的数据丢失 /**
* 4、填充黑名单中ListView的内容
*/
private void fillBlackNameListView() {
new Thread() {
@Override
public void run() {
if (blackNumberInfos==null) {//a)如果保存黑名单的集合为null,那么进行新的赋值
blackNumberInfos = blackNumberDao.getPartBlackNumberInfos(limit, offset);
} else {// b)如果不为null,那么将后面的数据加载到黑名单集合中去,防止前面的丢失,导致界面显示不出来
blackNumberInfos.addAll(blackNumberDao.getPartBlackNumberInfos(limit, offset));
}
handler.sendEmptyMessage(0);// 没有数据携带,可以发送空消息
}
}.start();
}问题6:但每一页数据加载完毕的时候,又回到了最前面 分析:由于消息处理机制里面的数据适配器,每次都是new出来的 @SuppressLint("HandlerLeak")
private Handler handler = new Handler() {// 消息机制,
@Override
public void handleMessage(Message msg) {
lv_callsmssafe.setAdapter(new CallSmsSafeAdapter());// 消息机制来更新UI
ll_blacknumber.setVisibility(ProgressBar.INVISIBLE);// 数据加载完毕时,将ll_blacknumber中的pb和textview提示给隐藏
}
};解决:将数据适配器定义成类成员变量,如果以前存在,那么复用callSmsSafeAdapter.notifyDataSetChanged(); @SuppressLint("HandlerLeak")
private Handler handler = new Handler() {// 消息机制,
@Override
public void handleMessage(Message msg) {
ll_blacknumber.setVisibility(ProgressBar.INVISIBLE);// 数据加载完毕时,将ll_blacknumber中的pb和textview提示给隐藏
if (callSmsSafeAdapter == null) {
callSmsSafeAdapter = new CallSmsSafeAdapter();
lv_callsmssafe.setAdapter(callSmsSafeAdapter);// 消息机制来更新UI
} else {
// 复用旧的数据适配器,通知数据适配器数据更新了
callSmsSafeAdapter.notifyDataSetChanged();
}
}
}; 优化7:在加载到最后,提示给用户,已经加载到最后 // b、为ListView设置滚动事件
lv_callsmssafe.setOnScrollListener(new OnScrollListener() {
// b-1)在滚动状态发生改变的时候调用的方法
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.i(TAG, "滚动状态发生变化~~~");
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:// a) listview静止时,这个重要
Log.i(TAG, "滚动状态变化到:" + "idle");
int lastVisiblePosition = view.getLastVisiblePosition();// 获取最后一个可以的item的位置,从0开始
int size = blackNumberInfos.size();// 获取的黑名单数据的大小,从1开始
if (lastVisiblePosition == (size - 1)) {
Log.i(TAG, "滚动到了一页的末尾~~~,加载更多的数据~~~");
offset += limit;
if (offset > count) {
ToastUtils.showToastInThread(CallSmsSafeActivity.this, "已经到了末尾,不能加载更多的数据了~~~");
return;
}
fillBlackNameListView();// 到了最后一个,重新设置黑名单listview的数据
}
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:// b) 手指触摸滚动的时候
Log.i(TAG, "滚动状态变化到:" + "touch scroll");
break;
case OnScrollListener.SCROLL_STATE_FLING:// c) 手指已经离开,惯性滑行状态
Log.i(TAG, "滚动状态变化到:" + "fling");
break;
}
}Bug8:因为是开启新的线程,如果前面一个线程数据还没有加载完毕,后面一个线程数据先加载了,那么就会出现数据顺序紊乱 解决:定义一个boolean的值,来记住是否正在加载数据 在加载数据前设置为true,在 case OnScrollListener.SCROLL_STATE_IDLE:的时候进行判断,如果为true,那么不能进行下次加载 在更新UI后,设置为false,下次可以继续加载数据 注意9:有的真实手机可以用音量按键来控制ListView的上下,以及Htc的一些手机,有一个滚动球,额可以控制,这些不响应 lv_callsmssafe.setOnScrollListener(new OnScrollListener() {},这个只有触摸才会响应 解决:在Activity中重写onKeyDown()方法 // 5、物理音量键拖动ListView
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:// 音量键+被按下
break;
case KeyEvent.ACTION_DOWN:// 音量键-被按下
break;
}
return super.onKeyDown(keyCode, event);
}
|