2012-06-08 2 views
2

У меня CursorAdapter который построен из базы данных Cursor и если View в первом пункте не заполняются, она заполнит первый элемент View сек с тем, что данные отображаются в что View в первом последующем элементе списка, где установлен View. Кроме того, я обнаружил, что onBindView трижды запускается для каждого элемента списка, что кажется странным. и он делает это только для первого элемента списка, а не для всех элементов с нулевым значением View. Вот как это выглядит:Android CursorAdapter рисунок вещи в неправильном списке пункт

example

Обратите внимание, что адрес «Юлиана» был скопирован в «Алисе». всякий раз, когда я устанавливаю точку останова в методе 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; 
    } 
} 

И я уверен, что у Алисы не должно быть адреса фактуры, связанного с этой записью. Вот запрос таблицы из sqlite3. Единственная запись с платежным адресом идентификатор записи Юлиана, как вы можете видеть:

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||||||| 
+0

Ваш вопрос неоднозначен, и я не думаю, что кто-то понял, что именно представляет собой проблема, поэтому постарайтесь лучше объяснить проблему. Некоторые вещи, которые я могу вам сказать, это: 'convertView' не может быть« null », поэтому проверка бесполезна, также вызываемая' bindView' вызывается для каждой строки в «ListView», и вы не должны запрашивать базу данных там , – Luksprog

+0

проблема была на самом деле довольно понятной. Я даже включил фотографии и весь соответствующий код. кроме того, в адаптере нет транзакций базы данных (которые вы бы знали, если бы посмотрели на код, который я опубликовал). единственная правильная часть вашего комментария заключалась в том, что convertView равен null. поэтому спасибо за это. я могу с радостью удалить две строки кода. потеря этого условного будет значительно ускорить мое приложение. – moonlightcheese

+0

Я думаю, что вы получаете нуль в конце Long billingAddressID для других записей, в 'final Long billingAddressID = cursor.getLong (cursor.getColumnIndex (CustomerSchema.BILLING_ADDRESS_ID));'. и строка 'String.valueOf (billingAddressID)' выдает ошибку. Это на первый взгляд. Позвольте мне попробовать это, и я могу вернуться, если он не был дан ответ уже. Но @stuckless ниже правильно о дизайне – Slartibartfast

ответ

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 для извлечения всей информации из обеих таблиц в одном запросе. По-прежнему возможно, что запрос займет слишком много времени, если выполняется в потоке пользовательского интерфейса. Предпочтительным решением является использование фонового Loader возможностей Android для этого, не связывая поток пользовательского интерфейса.

5

Глядя на код (который был немного запутанным на первый), кажется, что у вас есть Выборочная CursorAdapter для customer и внутри bindView() этого адаптера вы делаете вторичный запрос для location этого человека (т. е. a Присоединиться от customer до location). Это будет плохо работать, учитывая, что в потоке пользовательского интерфейса для каждого клиента вы выполняете вторичный запрос.

Это можно было бы решить с помощью SimpleCursorAdapter и запроса соединения между customer и location. (Мне кажется, единственной целью пользовательского адаптера сделать «присоединение» в customer и location, очень неэффективно)

Таким образом, запрос будет происходить в Backgound, используя Loaders возможно, и привязка будет достаточно эффективной, и вам не придется выполнять альтернативный запрос.

Первоначальный запрос может быть что-то вроде ..

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

(я не уверен, что ваши имена полей, но, мы надеемся, что передает базовая структура)

Смежные вопросы