Android CursorAdapter在错误的列表项中绘制内容
我有一个Android CursorAdapter在错误的列表项中绘制内容,android,android-listview,android-adapter,Android,Android Listview,Android Adapter,我有一个CursorAdapter,它是从数据库Cursor构建的,如果第一个项目中的视图没有填写,它会用该视图中显示的任何数据填充第一个项目的视图中设置了视图的第一个后续列表项目。此外,我发现每个列表项都会触发三次onBindView,这似乎很奇怪。它只对第一个列表项执行此操作,而不是对所有具有nullViews的项执行此操作。下面是它的样子: 注意,“朱利安”的地址被复制到了“爱丽丝”。每当我在onBindView方法中设置断点时,我可以看到当customerName为“Alice Mar
CursorAdapter
,它是从数据库Cursor
构建的,如果第一个项目中的视图
没有填写,它会用该视图
中显示的任何数据填充第一个项目的视图
中设置了视图
的第一个后续列表项目。此外,我发现每个列表项都会触发三次onBindView
,这似乎很奇怪。它只对第一个列表项执行此操作,而不是对所有具有nullView
s的项执行此操作。下面是它的样子:
注意,“朱利安”的地址被复制到了“爱丽丝”。每当我在onBindView方法中设置断点时,我可以看到当customerName为“Alice Martin”时,如果(billingAddressCursor!=null&&billingAddressCursor.getCount()>0)
对onBindView
的所有三个调用的计算结果总是为false,因此我知道它永远不会进入计费条件
为什么它在第一个位置绘制错误的值,我如何才能阻止它?代码:
private class CustomerAdapter extends CursorAdapter {
/*** DEBUG CODE, TO BE REMOVED ***/
int aliceBindCount = 0;
int blakeBindCount = 0;
int cscBindCount = 0;
int julianBindCount = 0;
/*** ^^^^^^^^^^^^^^^^^^^^^^^^^ ***/
public CustomerAdapter(Context context, Cursor cursor) {
super(context, cursor);
}
public void bindView(View convertView, Context context, Cursor cursor) {
if(convertView!=null) {
String customerName = cursor.getString(cursor.getColumnIndex(CustomerSchema.NAME));
((TextView)convertView.findViewById(R.id.cli_customer_name)).setText(customerName);
/*** DEBUG CODE, TO BE REMOVED ***/
if(customerName.equals("Alice Martin")) {
aliceBindCount += 1;
} else if(customerName.equals("Blake Slappey")) {
blakeBindCount += 1;
} else if(customerName.equals("Conceptual Systems")) {
cscBindCount += 1;
} else if(customerName.equals("Julian")) {
julianBindCount += 1;
}
/*** ^^^^^^^^^^^^^^^^^^^^^^^^^ ***/
}
final Long billingAddressID = cursor.getLong(cursor.getColumnIndex(CustomerSchema.BILLING_ADDRESS_ID));
Cursor billingAddressCursor = DbDesc.getInstance().getDatabase(getActivity()).query(
LocationSchema.TABLE_NAME,
null,
LocationSchema._ID+"=?",
new String[]{ String.valueOf(billingAddressID) },
null,
null,
null
);
if(billingAddressCursor!=null && billingAddressCursor.getCount()>0) {
billingAddressCursor.moveToFirst();
String street = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.STREET));
String city = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.CITY));
String state = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.STATE));
String zip = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.ZIP));
if(zip==null || zip.equals("")) {
zip = "[NO ZIP]";
}
if(street==null || street.equals("")) {
street = "[NO STREET]";
}
if(city==null || city.equals("")) {
city = "[NO CITY]";
}
if(state==null || state.equals("")) {
state = "[NO STATE]";
}
((TextView)convertView.findViewById(R.id.cli_street)).setText(street);
((TextView)convertView.findViewById(R.id.cli_city_state_zip)).setText(city+", "+state+" "+zip);
}
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.customer_list_item, parent, false);
return v;
}
}
我确信Alice不应该有与该记录相关联的账单地址。下面是来自sqlite3的表查询。唯一具有账单地址id的记录是Julian的记录,如您所见:
sqlite> .tables
.tables
android_metadata contract location
contact customer phone
sqlite> select * from customer;
select * from customer;
2|Conceptual Systems|||||||
3|Blake Slappey|||||||
4|Julian|1||||||
5|Alice Martin|||||||
通过查看代码(一开始有点混乱),您似乎有一个针对customer
的自定义CursorAdapter
,并且在该适配器的bindView()
中,您正在对此人的位置进行二次查询(即,从customer
连接到location
)。鉴于在UI线程上,对于每个客户,您都在进行二次查询,这将执行得很糟糕
使用和客户
和位置
之间的联接查询是否可以更轻松地解决此问题(在我看来,自定义适配器的唯一目的似乎是以非常低效的方式“联接”客户
和位置
)
这样,查询将在后台进行,可能使用加载程序
,并且绑定将相当有效,无需执行其他查询
初始查询可能类似于
select customer.name, location.address1, location.address2 from customer, location where customer.location_id = location._id
(我不确定您的字段名是什么,但希望这能传达基本结构)
ListView
回收其行视图,因此未明确设置的旧值将在后续使用中挂起。在您的代码中,如果活页夹中的查询未返回任何行,则视图中以前使用的值将保留。这不是您想要的
下面是一个重新编码,可以解决此问题:
String street = "[NO STREET]";
String city = "[NO CITY]";
String state = "[NO STATE]";
String zip = "[NO ZIP]";
if(billingAddressCursor!=null && billingAddressCursor.getCount()>0) {
billingAddressCursor.moveToFirst();
String tmp = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.STREET));
if (tmp != null) street = tmp;
tmp = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.CITY));
if (tmp != null) city = tmp;
tmp = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.STATE));
if (tmp != null) state = tmp;
tmp = billingAddressCursor.getString(billingAddressCursor.getColumnIndex(LocationSchema.ZIP));
if (tmp != null) zip = tmp;
}
((TextView)convertView.findViewById(R.id.cli_street)).setText(street);
((TextView)convertView.findViewById(R.id.cli_city_state_zip)).setText(city+", "+state+" "+zip);
话虽如此,但这并不是填写此列表的正确方法。每个列表项一个DB查询将产生一个性能狗。最简单的修复方法是使用一个
内部联接
在一个查询中检索两个表中的所有信息。如果在UI线程上运行,查询可能会花费太长时间。首选修复方法是s使用Android的后台加载程序
功能,无需占用UI线程。您的问题模棱两可,我认为没有人理解到底是什么问题,所以请尝试更好地解释问题。我可以告诉您的是:convertView
不能为null
,这样检查就没用了,另外,对列表视图中的每一行调用bindView
,您不应该在其中查询数据库。问题实际上非常清楚。我甚至包括了图片和所有相关代码。此外,适配器中没有发生数据库事务(如果你看过我发布的代码,你就会知道)。您的评论中唯一正确的部分是关于convertView为null的。谢谢。我可以很高兴地删除两行代码。丢失该条件将大大加快我的应用程序。我认为您在其他记录的final Long billingAddressID处获得null,位于final Long billingAddressID=cursor.getLong(cursor.getColumnIndex(CustomerSchema.BILLING_ADDRESS_ID))
。字符串string.valueOf(billingAddressID)
抛出一个错误。这是第一次查看。让我尝试一下,如果还没有回答,我可以返回。但是下面@stuckless的设计是正确的