Страницы

Поиск по вопросам

вторник, 27 ноября 2018 г.

SimpleCursorAdapter + ListView: Чекбоксы теряются при скроллинге

Чекбоксы при уходе за экран, теряют свои положения при возвращении. Вот тут - SimpleCursorAdapter - фильтр для курсора есть весь код (мой вопрос). Как реализовать метод для сохранения булевых значений? Неужели вместо курсора в адаптер мне нужно отправлять массив булевых значений? А как взятие данных через курсор в методе bindView()? Ведь курсор я уберу, а он нужен.
Mетод для получения данных с БД
private void getProductList() { String filter = "list=" + intValue; final Cursor cursor = mSqLiteDatabase.query("products", new String[] {DatabaseProductHelper.PRODUCT_ID, DatabaseProductHelper.PRODUCT_NAME, DatabaseProductHelper.PRODUCT_COUNT, DatabaseProductHelper.PRODUCT_LIST, DatabaseProductHelper.PRODUCT_TYPE, DatabaseProductHelper.PRODUCT_COMPLETE}, null, null, null, null, null) ; final Cursor cursorc = mSqLiteDatabase.query("products", new String[] {DatabaseProductHelper.PRODUCT_ID, DatabaseProductHelper.PRODUCT_NAME, DatabaseProductHelper.PRODUCT_COUNT, DatabaseProductHelper.PRODUCT_LIST, DatabaseProductHelper.PRODUCT_TYPE, DatabaseProductHelper.PRODUCT_COMPLETE}, filter, null, null, null, null) ; final ArrayList arrTblNames = new ArrayList();
if (cursor.moveToFirst()) { while ( !cursor.isAfterLast() ) { if(cursor.getInt(cursor.getColumnIndex("list"))==intValue) { arrTblNames.add(cursor.getString(cursor.getColumnIndex("name"))); } cursor.moveToNext(); }
String[] from = {DatabaseProductHelper.PRODUCT_NAME, DatabaseProductHelper.PRODUCT_COUNT, DatabaseProductHelper.PRODUCT_TYPE, DatabaseProductHelper.PRODUCT_COMPLETE}; int[] to = {R.id.ColMemberID, R.id.ColName, R.id.count_tv, R.id.chb_products}; adapter = new InteractiveArrayAdapter(ListBuilder.this, R.layout.row, cursorc, from, to ); //lv_products.setAdapter(adapter);
}

if(!arrTblNames.isEmpty()){ empty_bd_layout.setAlpha(0); lv_products.setAdapter(adapter); } else{ empty_bd_layout.setAlpha(255); } }
И сам адаптер
public class InteractiveArrayAdapter extends SimpleCursorAdapter { private int layout; private boolean checked[]; public InteractiveArrayAdapter(Context _context, int _layout, Cursor _cursor, String[] _from, int[] _to) { super(_context, _layout, _cursor, _from, _to); layout = _layout; int counts = _cursor.getCount(); checked = new boolean[counts];
for (int i = 0; i < counts; i++){
int checkMarker = _cursor.getInt(_cursor.getColumnIndex("complete")); checked[i] = checkMarker == 1; }
}
@Override public void bindView(View view, Context _context, Cursor _cursor) {

String prod_name = _cursor.getString(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_NAME)); String prod_count = _cursor.getString(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_COUNT)); String prod_type = _cursor.getString(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_TYPE)); // int prod_complete = _cursor.getInt(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_COMPLETE)); TextView name_prod_tv = (TextView)view.findViewById(R.id.ColMemberID); TextView count_prod_tv = (TextView)view.findViewById(R.id.ColName); TextView type_prod_tv = (TextView)view.findViewById(R.id.count_tv); CheckBox chb = (CheckBox)view.findViewById(R.id.chb_products);
chb.setFocusable(false);
chb.setClickable(false);
name_prod_tv.setText(prod_name);
count_prod_tv.setText(prod_count);
type_prod_tv.setText(prod_type);
chb.setChecked(checked[_cursor.getPosition()]);
} @Override public View newView(Context _context, Cursor _cursor, ViewGroup _parent) { LayoutInflater inflater = (LayoutInflater)_context.getSystemService(_context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(layout, _parent, false); return view; } public void setChecked(int position){ // инвертируем значение при обработке клика этим методом checked[position]=!checked[position]; } }
LogCat FATAL EXCEPTION: main 05-09 07:18:29.529 1468 1468 E AndroidRuntime Process: ru.diskrim.listbuy, PID: 1468 05-09 07:18:29.529 1468 1468 E AndroidRuntime java.lang.RuntimeException: Unable to start activity ComponentInfo{ru.diskrim.listbuy/ru.diskrim.listbuy.ListBuilder}: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 4 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.ActivityThread.access$800(ActivityThread.java:151) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.os.Handler.dispatchMessage(Handler.java:102) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.os.Looper.loop(Looper.java:135) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.ActivityThread.main(ActivityThread.java:5254) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at java.lang.reflect.Method.invoke(Native Method) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at java.lang.reflect.Method.invoke(Method.java:372) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:117) 05-09 07:18:29.529 1468 1468 E AndroidRuntime Caused by: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 4 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.database.AbstractCursor.checkPosition(AbstractCursor.java:426) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.database.AbstractWindowedCursor.getInt(AbstractWindowedCursor.java:68) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at ru.diskrim.listbuy.ListBuilder$InteractiveArrayAdapter.(ListBuilder.java:245) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at ru.diskrim.listbuy.ListBuilder.getProductList(ListBuilder.java:209) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at ru.diskrim.listbuy.ListBuilder.onCreate(ListBuilder.java:184) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.Activity.performCreate(Activity.java:6033) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106) 05-09 07:18:29.529 1468 1468 E AndroidRuntime at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278) 05-09 07:18:29.529 1468 1468 E AndroidRuntime ... 11 more


Ответ

Решение мало чем отличается от этого ответа, с той разницей, что вам нужно обрабатывать клики из активити, а не слушателем в адаптере.
Поскольку мы не можем изменять значения в курсоре, а только читать их оттуда, для запоминания текущего состояния чекбоксов требуется создать локальное хранилище.
В адаптер вам нужно передавать данные, которые должны быть отображены в виджете, в вашем случае это курсор с выборкой из БД. Вспомогательный массив булевых значений никуда передавать не нужно, он создается внутри адаптера.
Примерная реализация может выглядеть так:
private void getProductList() {
// Если требуется только имя, следует и запрашивать из БД только //колонку с именем, а не все в кучу // Если требуется только записи с каким то условием, то их и надо запрашивать, // а не все в подряд, а потом выбирать нужные каким то циклом // так же эти данные включены и в курсор для адаптера и нет нужды // получать то же самое дважды // final Cursor cursor = mSqLiteDatabase.query("products", new String[] {DatabaseProductHelper.PRODUCT_ID, DatabaseProductHelper.PRODUCT_NAME,"list = ?", new String[] {""+ intValue}, null, null, null) ;
//то же самое, только нужные в адаптере колонки final Cursor cursorc = mSqLiteDatabase.query("products", new String[] {DatabaseProductHelper.PRODUCT_ID,DatabaseProductHelper.PRODUCT_NAME, DatabaseProductHelper.PRODUCT_COUNT, DatabaseProductHelper.PRODUCT_TYPE, DatabaseProductHelper.PRODUCT_COMPLETE}, "list = ?", new String[] {""+ intValue}, null, null, null);
final ArrayList arrTblNames = new ArrayList();
// копирование данных из курсора в массив сильно упрощается, // так как у нас только нужные данные
while ( cursorc.moveToNext() ) { arrTblNames.add(cursorc.getString(cursorc.getColumnIndex("name"))); }
String[] from = {DatabaseProductHelper.PRODUCT_NAME, DatabaseProductHelper.PRODUCT_COUNT, DatabaseProductHelper.PRODUCT_TYPE, DatabaseProductHelper.PRODUCT_COMPLETE}; int[] to = {R.id.ColMemberID, R.id.ColName, R.id.count_tv, R.id.chb_products}; adapter = new InteractiveArrayAdapter(ListBuilder.this, R.layout.row, cursorc, from, to ); //lv_products.setAdapter(adapter);
}
// у виджетов есть свойство Visiblity, которое управляет видимостью на // экране, зачем тут использовать прозрачность ? // я бы поменял всю логику вообще, исходя из условия ниже, // зачем получаnь и формировать данные, если они могут не понадобится
if(!arrTblNames.isEmpty()){ empty_bd_layout.setAlpha(0); lv_products.setAdapter(adapter); } else{ empty_bd_layout.setAlpha(255); } }
адаптер:
public class InteractiveArrayAdapter extends SimpleCursorAdapter { private int layout; private boolean checked[];
public InteractiveArrayAdapter(Context _context, int _layout, Cursor _cursor, String[] _from, int[] _to) { super(_context, _layout, _cursor, _from, _to); layout = _layout; // инициализируем вспомогательный массив начальными отметками чекбоксов из курсора
checked = new boolean[_cursor.getCount()]; int i = 0; while ( _cursor.moveToNext() ) { int checkMarker = _cursor.getInt(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_COMPLETE)); checked[i] = (checkMarker == 1) ? true: false; i = i + 1; }
// нужно вернуть указатель курсора в первоначальное положение // перед первой записью, для корректной работы адаптера _cursor.moveToPosition(-1); }
@Override public void bindView(View view, Context _context, Cursor _cursor) {
String prod_name = _cursor.getString(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_NAME)); String prod_count = _cursor.getString(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_COUNT)); String prod_type = _cursor.getString(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_TYPE));
// эта строка здесь теперь по видимому не нужна, поскольку значения отметок // из курсора мы уже извлекли //int prod_complete = _cursor.getInt(_cursor.getColumnIndex(DatabaseProductHelper.PRODUCT_COMPLETE));
TextView name_prod_tv = (TextView)view.findViewById(R.id.ColMemberID); TextView count_prod_tv = (TextView)view.findViewById(R.id.ColName); TextView type_prod_tv = (TextView)view.findViewById(R.id.count_tv); CheckBox chb = (CheckBox)view.findViewById(R.id.chb_products); chb.setFocusable(false); chb.setClickable(false); name_prod_tv.setText(prod_name); count_prod_tv.setText(prod_count); type_prod_tv.setText(prod_type);
// устанавливаем чекбоксы по вспомогательному массиву, а не по курсору // получаем текущую позицию курсора (и соответственно адаптера) через его метод chb.setChecked(checked[_cursor.getPosition()]); }

@Override public View newView(Context _context, Cursor _cursor, ViewGroup _parent) {
LayoutInflater inflater = (LayoutInflater)_context.getSystemService(_context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(layout, _parent, false);
return view; }
// метод для фиксации изменений отметок чекбоксов из активити public void setChecked(int position){
// инвертируем значение при обработке клика этим методом checked[position] = !checked[position]; }
}
запись изменений в БД по отметкам чекбоксов у вас, насколько я помню, осуществляется при обработке клика в слушателе списка, поэтому в адаптер это действие не включено, но оно должно обязательно присутствовать, иначе при выходе из списка отметки будут утеряны.

Комментариев нет:

Отправить комментарий