ListView的优化建议,包含来自多个“表”的数据

时间:2010-08-18 11:13:27

标签: android optimization listview

我刚遇到以下情况。我有一个Android应用程序,我猜可能会在多个应用程序中发生。它是关于标记/标记/分类,根据需要调用它。我在SQLite DB中基本上有以下关系

 --------                 --------------              ---------
|  Tags  |               |  DeviceTags  |            | Devices |
|--------|               |--------------|            |---------|
| ID     | 1 ------ *    | ID           | * ------ 1 | ID      |
| NAME   |               | TAGS_ID      |            | NAME    |
 --------                | DEVICE_ID    |            | ...     |
                          --------------              ---------

我写过的ContentProvider上暴露了一切。到目前为止一切都很好。

在UI部分,我有一个ListActivity显示所有存储的设备(来自Devices表),并且为了进一步自定义UI,我创建了自定义行项目,根据设备类型等显示前面的小图像

我现在想要实现的是在每个设备的列表中显示相关标签。现在来了我的问题。对于简单的设备列表,我创建了一个自定义的ResourceCursorAdapter,我在bindView方法中设置相应的信息

@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
  final int objectId = cursor.getInt(cursor.getColumnIndex(Devices._ID));

  TextView deviceName = (TextView) view.findViewById(R.id.deviceName);
  deviceName.setText(...); //set it from the cursor
  ...
  TextView associatedTagsView = (TextView)...;
  associatedTagsView.setText(...); //<<<???? This would need a call to a different table
  ...
}

如您所见,为了能够知道我的设备与哪种标签相关联,我需要查询DeviceTags。所以我做了:

@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
   ...
   TextView associatedTagsView = (TextView)view.findViewById(R.id.deviceTags);
   String tagsString = retrieveTagsString(view.getContext().getContentResolver(), objectId);
   ...
}

private String retrieveTagsString(ContentResolver contentResolver, int objectId) {
    Cursor tagForDeviceCursor =  contentResolver.query(DroidSenseProviderMetaData.TABLE_JOINS.TAG_DEVICETAG,...);
    if(tagForDeviceCursor != null && tagForDeviceCursor.moveToFirst()){
        StringBuffer result = new StringBuffer();

        boolean isFirst = true;
        do{
            if(!isFirst)
                result.append(", ");
            else
                isFirst = false;

            result.append(retrieve name from cursor column...);
        }while(tagForDeviceCursor.moveToNext());

        return result.toString();
    }       
    return null;
}

我对此进行了测试,它实际上运行得很好,但老实说我做得不好。不知怎的,对我来说似乎很奇怪......

有没有更好的解决方案来解决这个问题?

//编辑:

在CommonsWare的反馈之后,这里有一点澄清。我对在CursorAdapter中对数据库进行第二次查询很奇怪,基本上这会导致每行一次查询,我担心这会严重影响我的性能(我还是要在真实的设备上测试它的数量很大数据,以了解这会产生多大影响)。

我的问题因此是关于如何在给定我的数据模型的情况下避免这种情况的策略,或者我是否必须基本上“活”起来:)

2 个答案:

答案 0 :(得分:3)

您在Twitter上抱怨说您没有得到任何反馈。我并不十分惊讶,因为虽然你的问题具有典型的细节,但它存在两个主要缺陷:

  1. 你的问题最后是模糊的
  2. 你的问题取决于你自己对“这个”是什么的解释,而我们并不知情
  3. 因此,我没有清楚的图片你认为“怪异”。 do...while()循环? : - )

    我想出一个问题,猜测你关注的是为ListView中的每一行做第二次查询和一串字符串连接。这完全取决于您是否认为“以逗号分隔的标签列表”是数据模型的一部分或演示文稿的一部分。如果它是演示文稿的一部分,我看不出你怎么能避免你在这里做的事情。

    如果您确实认为它是数据模型的一部分,请调整ContentProvider以提供以逗号分隔的标记列表,作为您首先查询的内容的一部分(未显示)。< / p>

    在任何一种情况下,表现都是可能的问题。每行执行一个查询,特别是在滚动时,可能会很糟糕。毕竟,Romain Guy反复强调你需要在你的适配器中回收行,我不得不想象数据库查询比夸大布局要贵一些。不幸的是,你使用的数据模型随着时间的推移强调了空间,因此你将不得不在某个地方向吹笛者支付费用。

答案 1 :(得分:0)

我测试了我的解决方案(在我的问题中提出),大约有100个条目。它仍然可用,但滚动并不像人们期望的那样顺畅。正如CommonsWare已经指出的那样,最好是调整数据模型,或者至少将其正确地聚合到一起。最后,光标包含在UI上显示所需的所有数据

我不想完全更改基础数据模型。我所做的是以某种方式聚合数据,s.t。 ContentProvider返回的游标包含设备的数据,包括已经准备好的字符串,表示相关标签的名称:

| ID | NAME             | ... | LABELSSTRING   |
------------------------------------------------
| 1  | "My Device name" | ... | "Work, Friend" |
| 1  | "Some other"     | ... |                |
| 1  | "Yet another"    | ... | "Work"         |

这几乎解决了这个问题,因为没有必要在每个设备的CursorAdapter的bindView(..)中重新查询,顺便说一句。非常糟糕。使用SQLite的group_concat(X, Y) function

可以实现此解决方案