2

我有一个CursorAdapter从数据库构建的Cursor,如果View第一个项目中的 a 未填写,它会使用设置了的第一个后续列表项中View出现的任何数据填充第一个项目的 s 。此外,我发现每个列表项都会触发 3 次,这似乎很奇怪。它只对第一个列表项执行此操作,而不是所有带有 null的项目。这是它的样子:ViewViewonBindViewView

例子

请注意,“Julian”的地址已复制到“Alice”。每当我在 onBindView 方法中设置断点时,我可以看到当 customerName 为“Alice Martin”时,if(billingAddressCursor!=null && billingAddressCursor.getCount()>0)对于所有三个调用,总是评估为 false onBindView,所以我知道它永远不会进入计费条件。

为什么它在第一个位置绘制错误的值,我该如何阻止它?代码:

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|||||||
4

2 回答 2

5

通过查看代码(起初有点混乱),您似乎在该适配器的内部有一个 Custom CursorAdaptercustomerbindView()正在location对该人进行二次查询(即 Join from customerto location)。这将执行得很糟糕,因为在 UI 线程上,对于每个客户,您都在执行辅助查询。

使用SimpleCursorAdaptercustomer和and之间的连接查询可以更轻松地解决这个问题吗location?(在我看来,自定义适配器的唯一目的是以非常低效的方式“加入” customerand location

这样,查询将在后台发生,使用Loaders可能,并且绑定将相当有效,永远不需要进行备用查询。

初始查询可能类似于..

select customer.name, location.address1, location.address2 from customer, location where customer.location_id = location._id

(我不确定您的字段名称是什么,但希望能传达基本结构)

于 2012-06-22T19:00:49.367 回答
3

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);

话虽如此,这不是填写此列表的正确方法。每个列表项一个数据库查询将产生一个性能狗。最简单的解决方法是使用单个INNER JOIN查询从两个表中检索所有信息。如果在 UI 线程上运行,查询仍然可能需要很长时间。首选的解决方法是使用 LoaderAndroid 的后台功能来执行此操作,而不会占用 UI 线程。

于 2012-06-28T12:18:41.593 回答