Чекбоксы при уходе за экран, теряют свои положения при возвращении. Вот тут - 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
Ответы
Ответ 1
Решение мало чем отличается от этого ответа, с той разницей, что вам нужно обрабатывать
клики из активити, а не слушателем в адаптере.
Поскольку мы не можем изменять значения в курсоре, а только читать их оттуда, для
запоминания текущего состояния чекбоксов требуется создать локальное хранилище.
В адаптер вам нужно передавать данные, которые должны быть отображены в виджете,
в вашем случае это курсор с выборкой из БД. Вспомогательный массив булевых значений
никуда передавать не нужно, он создается внутри адаптера.
Примерная реализация может выглядеть так:
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];
}
}
запись изменений в БД по отметкам чекбоксов у вас, насколько я помню, осуществляется
при обработке клика в слушателе списка, поэтому в адаптер это действие не включено,
но оно должно обязательно присутствовать, иначе при выходе из списка отметки будут утеряны.