本文从头开始回顾我的项目,想要直接看题目解决方法的请跳到第三个问题。
最近打算做一个程序锁app练练手,在GitHub上找到了基础代码进行修改。
原代码https://github.com/caiwenshuo/asLockApp实现的listview是将锁定的app和未锁定的app混杂在一起显示,我希望实现的是锁定与未锁定的app分开显示,并且在listview有分类标签。
原效果图
改后效果图
于是我参考了https://blog.youkuaiyun.com/wellke666/article/details/50561770
他的原理是通过两个adapter嵌套,外层的adapter用于分类,判断要显示的item是属于分类标签还是内容,再分别交给不同的adapter去处理。
第一个问题:convertview == null
在应用这个分类listview后遇到的第一个问题就是在初始化界面时convertview == null 判断除了第一行外其他都不成立的。因为若convertview == null成立则为部件建立对象。这就导致其后生成的item因为没有初始化而报错。
后来发现原因出在了xml文件内,更改layout_height等属性后就可以做出正确判断了。
关于这个问题可参考: https://mp.youkuaiyun.com/postedit/82263578
第二个问题:不同类型item回收利用
我重写的ListViewAdapter在生成满屏的item后,滚动屏幕,新的item会利用旧的item的布局,免去了生成新布局的消耗。这在没有使用分类listview 之前是能很好运作的。但在使用后,由于回收的item有的是title类型,有的是content类型,他们的布局并不相同,没办法回收利用。这个问题我目前是粗暴地放弃回收利用,每次都产生新item。网上有说利用itemtype函数判断的。
第三个问题:notifyDataSetChanged执行而getview不执行
采用嵌套式adapter遇到的问题就是现在不能实时刷新页面了,比如我点击锁定,锁定按钮不会变成已锁定,也不会重新分类,但只要我滚动屏幕,再滚回去,就能看到变化。这说明我的数据源是有变化的。那肯定是就是我最内层的notifydatasetchanged函数没有执行力。(说明我的问题不是网上常说的list指针问题)
于是我重写了notifydatasetchanged输出日志,
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
System.out.println("log: notifydatasetchanged is executed"+getCount());
}
结果发现notifydatasetchanged在我每次点击锁定时都有执行,getcount也有变化。
那为什么视图没有变化呢?据我理解,notifydatasetchanged函数实际上判断数据有无变化,有则重新渲染,那么肯定得重新执行getview()了,于是我在getview中加入log。运行证明,在我点击锁定button后getview()并没有运行。难怪视图没有刷新。
经过查了stackover、csdn各种文章后,虽然并没有人能准确地解决我的问题,但还是给了我些启发。
我觉得我的问题是adapter嵌套引起的。我在activity中的Listview绑定的是最外层的adapter,而调用notifydatasetchanged则是由第二层adapter调用,这个adapter并没有与listview绑定,所以他即使调用了notifydatasetchanged可能也无法改变listview。
我尝试解决这个问题。我想到的方法是用回调函数,在按钮被点击时让第二层的adapter调用第一层的notifydatasetchanged,这样就可以完美解决adapter和listview的对应问题。
public interface Callback{
public abstract void callback();
}
cb=new Callback() {
@Override
public void callback() {
notifyDataSetChanged();
}
};
重写回调函数
if (listItems.get(position).get("flag").equals("已锁定")) {
listItems.get(position).put("flag", "锁定");
otherlistItems.add(listItems.get(position));
listItems.remove(position);
if(dataBaseUtil.delete(app) == 0){
Log.i(tableName, "delete failed! ");
}
else {
Log.i(tableName, "delete success! ");
}
System.out.println("delete");
}
else {
listItems.get(position).put("flag", "已锁定");
otherlistItems.add(listItems.get(position));
listItems.remove(position);
if(dataBaseUtil.insert(app) == -1){
Log.i(tableName, "insert failed! ");
}
else {
Log.i(tableName, "insert success! ");
}
System.out.println("lock");
}
//thread.start();
cbc.callback();
对回调函数不了解可参考: https://blog.youkuaiyun.com/Maoxf_Boss/article/details/51040072
在第二层adapter调用callback()
重新运行可以看到现在的界面就可以根据我们的点击实时刷新了~~~