2010-08-18 28 views
5

Tôi vừa gặp phải tình huống sau. Tôi có một ứng dụng Android với một kịch bản mà tôi đoán có thể xảy ra trong nhiều ứng dụng. Đó là về gắn thẻ/ghi nhãn/phân loại, gọi nó như bạn muốn. Về cơ bản tôi có các mối quan hệ sau trong SQLite DBĐề xuất tối ưu hóa cho ListView với dữ liệu từ nhiều "bảng"

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

Mọi thứ được hiển thị trên Trình cung cấp nội dung tôi đã viết. Cho đến nay mọi thứ đều ổn. Trên phần giao diện người dùng, tôi có một ListActivity hiển thị tất cả các thiết bị được lưu trữ (từ bảng Devices) và để tùy chỉnh UI thêm một chút, tôi tạo các mục hàng tùy chỉnh hiển thị một hình ảnh nhỏ ở phía trước dựa trên loại thiết bị v.v.

Điều tôi muốn đạt được bây giờ là cũng hiển thị các thẻ được liên kết trên danh sách đó cho từng thiết bị. Bây giờ ở đây có vấn đề của tôi. Để xem danh sách thiết bị đơn giản, tôi đã tạo ra một ResourceCursorAdapter tùy chỉnh nơi tôi đặt thông tin theo trong phương pháp 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 
    ... 
} 

Như bạn có thể thấy, để có thể biết những loại thẻ điện thoại của tôi đã liên kết, tôi sẽ cần để truy vấn DeviceTags. Vì vậy, tôi đã làm:

@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; 
} 

Tôi đã thử nghiệm điều này và nó thực sự hoạt động tốt, nhưng phải trung thực tôi không cảm thấy tốt khi làm điều này. Bằng cách nào đó có vẻ lạ đối với tôi ...

Có giải pháp nào tốt hơn về cách giải quyết vấn đề này không ??

// Edit:

Sau phản hồi CommonsWare ở đây một chút làm rõ. Tôi kỳ lạ về việc thực hiện truy vấn thứ hai cho DB bên trong CursorAdapter, về cơ bản điều này sẽ dẫn đến một truy vấn mỗi hàng và tôi lo sợ điều này sẽ ảnh hưởng lớn đến hiệu suất của tôi (tôi vẫn thử nghiệm nó trên một thiết bị thực với một số lượng đáng kể dữ liệu để xem mức độ ảnh hưởng này).

Câu hỏi của tôi do đó là về việc liệu có một số chiến lược về cách tránh cho mô hình dữ liệu của tôi này hay tôi phải về cơ bản "sống" với điều đó :)

Trả lời

0

Tôi đã thử nghiệm giải pháp của mình (được đề xuất trong câu hỏi của tôi) với khoảng 100 mục nhập. Nó vẫn có thể sử dụng được, nhưng việc di chuyển không mượt mà như mong đợi. Như CommonsWare đã chỉ ra, tốt nhất là để thích ứng với mô hình dữ liệu hoặc ít nhất là để tổng hợp nó đúng cách s.t. cuối cùng con trỏ chứa tất cả dữ liệu cần thiết để hiển thị trên giao diện người dùng.

Tôi không muốn thay đổi hoàn toàn mô hình dữ liệu bên dưới. Những gì tôi đã làm là tổng hợp dữ liệu theo cách, s.t. con trỏ trả về bởi các ContentProvider chứa các dữ liệu cho các thiết bị bao gồm một chuỗi đã được chuẩn bị đại diện cho tên nhãn liên quan:

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

này khá nhiều giải quyết được vấn đề, vì không có nhu cầu để requery trong bindView của CursorAdapter (..) cho mỗi thiết bị, mà btw. khá tệ. Giải pháp này là có thể bằng cách sử dụng group_concat(X, Y) function của SQLite.

3

Bạn phàn nàn trên Twitter rằng bạn chưa làm' không nhận được bất kỳ phản hồi nào. Tôi không khủng khiếp ngạc nhiên, bởi vì trong khi câu hỏi của bạn có một mức độ gương mẫu của từng chi tiết, nó bị hai lỗ hổng lớn:

  1. Câu hỏi của bạn, ở cuối, là mơ hồ
  2. Câu hỏi của bạn dựa trên giải thích của riêng bạn về "điều này" là gì, mà chúng tôi không thuộc quyền riêng tư

Do đó, tôi không có hình ảnh rõ ràng nào bạn nghĩ là "lạ". do...while() vòng? :-)

Tôi sẽ đi ra ngoài một chi và đoán rằng mối quan tâm của bạn là làm một truy vấn thứ hai và vòng chuỗi nối cho mỗi hàng trong ListView của bạn. Tất cả phụ thuộc vào việc bạn có nghĩ rằng "danh sách các thẻ được phân tách bằng dấu phẩy" hay không là một phần của mô hình dữ liệu hoặc một phần của bản trình bày. Nếu nó là một phần của bài thuyết trình, tôi không thấy làm thế nào bạn có thể tránh những gì bạn đang làm ở đây.

Nếu bạn thực sự nghĩ rằng đó là một phần của mô hình dữ liệu, hãy điều chỉnh ContentProvider để cung cấp danh sách thẻ phân cách bằng dấu phẩy như một phần của bất kỳ điều gì bạn đang truy vấn ngay từ đầu (không được hiển thị).

Trong cả hai trường hợp, hiệu suất là một vấn đề có thể xảy ra. Thực hiện một truy vấn trên mỗi hàng, đặc biệt là tại thời điểm di chuyển, có thể là xấu. Sau khi tất cả, Romain Guy nhấn mạnh nhiều lần rằng bạn cần phải tái chế các hàng trong bộ điều hợp của bạn, và tôi sẽ phải tưởng tượng rằng một truy vấn cơ sở dữ liệu là một chút wee đắt hơn thổi phồng một bố trí. Thật không may, bạn đã đi với một mô hình dữ liệu nhấn mạnh không gian theo thời gian, và vì vậy bạn sẽ phải trả piper một nơi nào đó dọc theo dòng.

+0

Xin lỗi vì câu hỏi mơ hồ của tôi. Không nhận ra điều đó và sửa nó ngay bây giờ. Nhưng bạn hoàn toàn phù hợp với các vấn đề của tôi (+1 cho điều đó) :) Hiệu suất là chính xác điều. Nhưng với yêu cầu rằng 1 thiết bị có thể có nhiều thẻ, tôi sẽ phải đi với thiết kế DB được hiển thị ở trên ... Tất nhiên những gì tôi có thể làm là bằng cách nào đó "hợp nhất" DeviceTags và Tags nhưng tôi đoán điều này sẽ trở nên khá xấu xí ... Có lẽ tôi nên tham gia mọi thứ trên cấp độ ContentProvider Tôi đã có sẵn dữ liệu ... Tôi sẽ thử rằng nếu tôi thấy các giải pháp hiện tại tác động mạnh đến hiệu suất. – Juri

+0

Tôi cũng sẽ cố gắng tránh truy vấn thứ hai trong bộ điều hợp. Chỉ cần xây dựng nhà cung cấp nội dung của bạn theo cách mà tất cả các dữ liệu cần thiết trong danh sách có mặt tại thời điểm nó được hiển thị cho người dùng. – Janusz

+0

@Juri: "Nhưng với yêu cầu 1 thiết bị có thể có nhiều thẻ, tôi sẽ phải đi với thiết kế DB được hiển thị ở trên" - không, bạn không cần. Bạn chỉ cần rằng nếu bạn cũng đang đi vào dữ liệu từ phía thẻ, và thường xuyên, đủ để làm cho nó đáng giá. Cách thay thế của bạn là để chuẩn hóa dữ liệu, có danh sách các thẻ được phân tách bằng dấu phẩy là một cột trong bảng thiết bị. – CommonsWare

Các vấn đề liên quan