Урок 34. Хранение данных. SQLite

Обсуждение уроков
dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 15 окт 2012, 17:06

А, действительно, все равно без помощи не обойтись

Код: Выделить всё

public class MainActivity extends Activity {

    myDataSource datasource;
    String names;
    String name;
    ContentValues cv;
    Cursor c;
    long rowID; //ID строки имени позиции
    int idColIndex; // номер столбца по ID
    int nameColIndex; // номер столбца по имени
    int peremennaya;
    private static final int CM_DELETE_ALL = 1;
    private static final int CM_DELETE_ID = 2;
    private static final int CM_ADD_ID = 3;
    EditText position_name;
    ArrayList<Bazar> bazar = new ArrayList<Bazar>();
    MyAdapter myAdapter;
    private String LOG_TAG;

    /**
     * Called when the activity is first created.
     */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        myAdapter = new MyAdapter(this, bazar);

        // настраиваем список
        ListView lv_main = (ListView) findViewById(R.id.lv_main);

        // выставляем адаптер
        lv_main.setAdapter(myAdapter);

        // регистратор контекстного меню
        registerForContextMenu(lv_main);

        // вызов ChildActivity        
        lv_main.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {

                peremennaya = position;

                Intent intent = new Intent(getApplicationContext(), ChildActivity.class);
                intent.putExtra("value", peremennaya);
                startActivity(intent);
            }
        });


        cv = new ContentValues();

        datasource = new myDataSource(this);
        
        bazar.add(new Bazar("Product_0", R.drawable.unread));

        dbShowAll();

    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.add(0, CM_DELETE_ALL, 0, "Удалить все позиции");
        menu.add(0, CM_DELETE_ID, 0, "Удалить позицию");
        menu.add(0, CM_ADD_ID, 0, "Добавить позицию");
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {

        if (item.getItemId() == CM_DELETE_ALL) {

            this.allProductsDeleteDialog();

            return true;

        } else {

            if (item.getItemId() == CM_DELETE_ID) {

                // получаем инфу о пункте списка                    
                AdapterView.AdapterContextMenuInfo acmi = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();

                //получаем значение текущей id позиции в листе
                rowID = acmi.id;
                
                // подключаемся к базе данных             
                datasource.open();

                if (c != null) {

                    //удаление текущей позиции из базы данных
                    Log.d(LOG_TAG, "--- Delete from mytabe: ---");

                    // удаляем из ? по значению id
                    int delCount = datasource.db.delete("mytable", "id = " + rowID, null);
                    
                    Log.d(LOG_TAG, "row of List ID deleted, ID = " + rowID);
                    Log.d(LOG_TAG, "deleted rows count = " + delCount);

                    
                    // физическое удаление из листа ?
                    bazar.remove(acmi.position);
                    
                    // уведомляем, что данные изменились
                    myAdapter.notifyDataSetChanged();

                } else {
                    Log.d(LOG_TAG, "Cursor is null");
                    c.close();
                }
                datasource.db.close();

                return true;

            } else {

                if (item.getItemId() == CM_ADD_ID) {

                    // вызываем метод создания диалога для ввода данных 
                    productInputDialog();
                }

            }
        }

        return super.onContextItemSelected(item);
    }

    void allProductsDeleteDialog() {

        AlertDialog.Builder adb_main = new AlertDialog.Builder(this);
        adb_main.setTitle("???");
        adb_main.setMessage("Действительно удалить все содержимое");
        adb_main.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {

                // удаляем с листа все содержимое по позициям

                bazar.clear();

                // подключаемся к базе данных             
                datasource.open();

                if (c != null) {
                    // удаляем все значения из базы данных
                    Log.d(LOG_TAG, "--- Clear mytable: ---");

                    int clearCount = datasource.db.delete("mytable", null, null);

                    Log.d(LOG_TAG, "deleted rows count = " + clearCount);
                    
                    // уведомляем, что данные изменились
                    myAdapter.notifyDataSetChanged();

                } else {
                    Log.d(LOG_TAG, "Cursor is null");
                    c.close();
                }
                datasource.close();
            }
        });
        adb_main.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {

                dialog.cancel();

                return;
            }
        });

        adb_main.setIcon(R.drawable.unread);
        adb_main.show();
    }

    void productInputDialog() {

        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        LayoutInflater inflater = this.getLayoutInflater();

        final View view = inflater.inflate(R.layout.dialog_signin_main, null);

        builder.setView(view);
        builder.setTitle("Добавить");
        builder.setMessage("к содержимому");

        builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int whichButton) {

                position_name = (EditText) view.findViewById(R.id.position_name);

                // получаем имя из строки ввода 
                name = position_name.getText().toString();

                // передаем значение методу dbReader()
                dbReader(name);

                // получаем значение names
                bazar.add(new Bazar(names, R.drawable.unread));

                myAdapter.notifyDataSetChanged();

            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
            }
        });
        builder.show();
    }

    public String dbReader(String names) {

        this.names = names;

        // подключаемся к базе данных             
        datasource.open();

        // подготовим данные для вставки
        cv.put("names", names);

        // вставляем запись и получаем ее ID
        rowID = datasource.db.insert("mytable", null, cv);

        Log.d(LOG_TAG, "row inserted, ID = " + rowID);

        // делаем запрос всех данных из таблицы mytable, получаем Cursor 
        c = datasource.db.query("mytable", null, null, null, null, null, null);

        // ставим позицию курсора на первую строку выборки
        // если в выборке нет строк, вернется false
        if (c.moveToFirst()) {

            // определяем номера столбцов по имени и id в выборке

            idColIndex = c.getColumnIndex("id");

            nameColIndex = c.getColumnIndex("names");

            do {


                Log.d(LOG_TAG, "ID = " + c.getInt(idColIndex) + ", name = " + c.getString(nameColIndex));

                this.names = c.getString(nameColIndex);


            } while (c.moveToNext());
        } else {
        }
        c.close();

        myAdapter.notifyDataSetChanged();

        // закрываем подключение к БД
        datasource.close();

        return this.names;
    }

    void dbShowAll() {

        // подключаемся к базе данных             
        datasource.open();

        // получаем значения и отображаем при загрузке приложения

        c = datasource.db.query("mytable", null, null, null, null, null, null);


        if (c.moveToFirst()) {

            nameColIndex = c.getColumnIndex("names");

            do {

                names = c.getString(nameColIndex);

                bazar.add(new Bazar(names, R.drawable.unread));

            } while (c.moveToNext());

        } else {

            Log.d(LOG_TAG, "Cursor is null");
            c.close();
            datasource.close();
        }
    }
}

class myDataSource {

    // Database fields
    public SQLiteDatabase db;
    public DBHelper dbHelper;
    private String LOG_TAG;

    public myDataSource(Context context) {
        dbHelper = new DBHelper(context);
    }

    public void open() throws SQLException {
        db = dbHelper.getWritableDatabase();
    }

    public void close() {
        dbHelper.close();
    }
}
Вы правы, проблема в удалении пункта списка с одновременным удалением по id в базе, перепостроением списка, сохранением измененных данных, и их выводом при следующей загрузке приложения.

Заранее спасибо.

Аватара пользователя
neoksi
Сообщения: 712
Зарегистрирован: 26 июл 2012, 10:42
Контактная информация:

Re: Урок 34. Хранение данных. SQLite

Сообщение neoksi » 15 окт 2012, 18:10

И почему все стремятся работать с БД без ContentProvider?
Ладно, это риторический вопрос. Но если вы реализуете работу через ContentProvider, то у вас будет все работать просто в автоматическом режиме. Вы говорите провайдеру какую запись удалить и он её удаляет, а так же обновляет курсоры и отправляет команду для перерисовки вашего ListView через адаптер. И самый главный плюс в том, что вы можете одновременно работать с БД из разных классов вашего приложения.

Аватара пользователя
rezak90
Сообщения: 3422
Зарегистрирован: 26 июн 2012, 13:22
Откуда: UA
Контактная информация:

Re: Урок 34. Хранение данных. SQLite

Сообщение rezak90 » 15 окт 2012, 18:57

1) люди никак с SQLite разобраться не могут, так что провайдер пока рановато смотреть;
2) использование адаптера должно быть оправдано, так как использовать его ради одной таблицы в пару записей нету смысла, да удобней будет при модернизации приложения.
R.id.team
Политика на форуме запрещена

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Урок 34. Хранение данных. SQLite

Сообщение powercat » 15 окт 2012, 19:48

IMHO - что-то многова-то для кода...я конечно не очень еще разбираюсь, но...
Смотри, строка 42 вышепреведенного тобой кода long id - это и есть твой искомый пункт...соотнеси его плз по моей ссылке (viewtopic.php?f=34&t=1120) и ты поймаешь то, что хочешь

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 06:21

rezak90 совершенно прав, надо сперва разобраться с SQLite.
Теперь по существу.
powercat, смотрел Ваш код, он совершенно идентичен тому, что у меня, за следующим исключением:
когда я подключаюсь к базе, то делаю это через datasource.open();, который в свою очередь просто открывает ее db = dbHelper.getWritableDatabase();.
У Вас, следующая строка: openDatabase(pathToDB, null, SQLiteDatabase.OPEN_READWRITE).
Может проблема в том, что я просто подключаюсь к базе, не указывая параметры для совершения действий?
Не могли бы Вы расшифровать openDatabase(pathToDB, null, SQLiteDatabase.OPEN_READWRITE).

Спасибо.

P.S. Просьба пинать сильнее, здоровее буду. Наверняка есть еще какие-либо косяки, которые не дают мне разобрать тему.

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 06:47

Еще один вопрос, который не дает покоя. Почему при удалении из базы, данные не удаляются с листа, и соответственно приходится вызывать:
// физическое удаление из листа ?
bazar.remove(acmi.position);
после чего, и происходит непосредственное удаление. Вот этим несоответствием и был вызван мой вопрос о наличии маркеров, которые позволяли бы удаление из базы данных соотносить с удалением из листа.
Ведь по логике, получив id значение текущей позиции в листе rowID = acmi.id; и удаляя ее в базе datasource.db.delete("mytable", "id = " + rowID, null); мы должны удалять не только запись в базе, но и в листе?
А ведь, насколько я понимаю, тот id, который в базе, остается даже после удаления записи.
Из этого получается, что имеются два id. И на какой id в дальнейшем ориентироваться, тот который в базе, или даваемый, при вызове из листа. Например, если в дальнейшем я должен оперировать id, передавать через Intent и т.д.

Аватара пользователя
neoksi
Сообщения: 712
Зарегистрирован: 26 июл 2012, 10:42
Контактная информация:

Re: Урок 34. Хранение данных. SQLite

Сообщение neoksi » 16 окт 2012, 07:03

dil_android писал(а):Еще один вопрос, который не дает покоя. Почему при удалении из базы, данные не удаляются с листа, и соответственно приходится вызывать:
// физическое удаление из листа ?
bazar.remove(acmi.position);
после чего, и происходит непосредственное удаление. Вот этим несоответствием и был вызван мой вопрос о наличии маркеров, которые позволяли бы удаление из базы данных соотносить с удалением из листа.
Ведь по логике, получив id значение текущей позиции в листе rowID = acmi.id; и удаляя ее в базе datasource.db.delete("mytable", "id = " + rowID, null); мы должны удалять не только запись в базе, но и в листе?
А ведь, насколько я понимаю, тот id, который в базе, остается даже после удаления записи.
Из этого получается, что имеются два id. И на какой id в дальнейшем ориентироваться, тот который в базе, или даваемый, при вызове из листа. Например, если в дальнейшем я должен оперировать id, передавать через Intent и т.д.
Логика правдива, если мы используем весь инструментарий, а не только его части как в вашем случае.
После действий с БД, необходимо обновить курсор, курсор имеет Observer, call-back метод, который запускает процесс рендеринга листа с новыми данными. Но так как вы после изменения данных в БД, не обновили курсор и не запустили механизм, то приходится удалять по позиции вручную.

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 07:43

Как это осуществляется практически (обновление курсора)? Где прочитать, если не сможете дать код в контексте приложения, чтобы я разобрался.

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Урок 34. Хранение данных. SQLite

Сообщение powercat » 16 окт 2012, 13:27

dil_android писал(а):rezak90 совершенно прав, надо сперва разобраться с SQLite.
Теперь по существу.
powercat, смотрел Ваш код, он совершенно идентичен тому, что у меня, за следующим исключением:
когда я подключаюсь к базе, то делаю это через datasource.open();, который в свою очередь просто открывает ее db = dbHelper.getWritableDatabase();.
У Вас, следующая строка: openDatabase(pathToDB, null, SQLiteDatabase.OPEN_READWRITE).
Может проблема в том, что я просто подключаюсь к базе, не указывая параметры для совершения действий?
Не могли бы Вы расшифровать openDatabase(pathToDB, null, SQLiteDatabase.OPEN_READWRITE).

Спасибо.

P.S. Просьба пинать сильнее, здоровее буду. Наверняка есть еще какие-либо косяки, которые не дают мне разобрать тему.
Это просто другой вариант подключения, без DBHelper-а...сейчас у меня все с ним...
Смотри, ты когда тыкаешь на пункт списка, то должен передать на удаление в базе этот свой лонг id, который получишь из этого пункта, по которому тыкаешь ))) Это и есть связь между пунктом списка и записью в базе.
Вот так у меня сейчас реализована обработка контекстного меня, появляющегося при долгом нажатии на пункт списка:

Код: Выделить всё

public boolean onContextItemSelected(MenuItem item){
		dbHelper=new DBHelper(this);
		db= dbHelper.getWritableDatabase();
		AdapterContextMenuInfo adapterContextMenuInfo=(AdapterContextMenuInfo)item.getMenuInfo();
		long myInt=adapterContextMenuInfo.id;    //   ЭТО ТО, ЧТО ТЕБЕ ТРЕБУЕТСЯ
		switch (item.getItemId()){
			case R.id.delete://   ВЫБИРАЮ УДАЛЕНИЕ ПУНКТА СПИСКА
				String myRow="SELECT * FROM mainTable WHERE _id="+myInt;
				cursor=db.rawQuery(myRow, null);
				if (cursor.moveToFirst()){
					.........
				}
				db.delete("mainTable","_id = "+adapterContextMenuInfo.id,null);
				dbHelper.close();
//   ДАЛЕЕ ПЕРЕПОСТРОЕНИЕ СПИСКА
				db= dbHelper.getWritableDatabase();
				cursor=db.query("mainTable", null, null, null, null, null, null);
				scAdapter.changeCursor(cursor);
				if (!cursor.moveToFirst()){
					......
				}
				dbHelper.close();
				break;
			case R.id.edit:
				.......
				break;
		}
		return super.onContextItemSelected(item);
	}

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 15:41

Тоже самое делаю и я.

Код: Выделить всё

             // получаем инфу о пункте списка                    
             AdapterView.AdapterContextMenuInfo acmi = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();

                //получаем значение текущей id позиции в листе
                rowID = acmi.id;
               
                // подключаемся к базе данных             
                datasource.openToWrite();

                // удаляем запись из базы данных по значению id
                datasource.db.delete("mytable", "id = " + rowID, null);

               // уведомляем, что данные изменились
               myAdapter.notifyDataSetChanged();

                    cursor.close();

                   // закрываем базу данных
                    datasource.close();
Но какие действия дальше, чтобы база данных восприняла удаление и проапдейтилась. После выполнения моего кода, при новой загрузке, удаленная позиция снова отображается в листе.
В Вашем коде есть перепостроение, что это означает? Разве нет более простого решения, как написано у neoksi. Посмотрел в Инете есть метод cursor.requery();, который как я понимаю должен апдейтить лист. Как его использовать?

Аватара пользователя
rezak90
Сообщения: 3422
Зарегистрирован: 26 июн 2012, 13:22
Откуда: UA
Контактная информация:

Re: Урок 34. Хранение данных. SQLite

Сообщение rezak90 » 16 окт 2012, 15:50

notifyDataSetChanger() метод адаптера который делает апдейт, если делать адаптер с курсором то курсор эту тягатину берёт на себя
R.id.team
Политика на форуме запрещена

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 15:57

Хорошо, у меня адаптер без курсора, так как быть то? Как сохранить в БД информацию об удалении?

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Урок 34. Хранение данных. SQLite

Сообщение powercat » 16 окт 2012, 16:13

Ну...у тебя база открывается только для чтения вроде? правильно? а у меня для чтения и редактирования...

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 16:30

public void openToWrite() throws SQLException {
db = dbHelper.getWritableDatabase();
}

Аватара пользователя
rezak90
Сообщения: 3422
Зарегистрирован: 26 июн 2012, 13:22
Откуда: UA
Контактная информация:

Re: Урок 34. Хранение данных. SQLite

Сообщение rezak90 » 16 окт 2012, 16:33

dil_android писал(а):Хорошо, у меня адаптер без курсора, так как быть то? Как сохранить в БД информацию об удалении?
в чём тогда проблема сделать с курсором. Просто если работать со списком и бд без адаптера это очень не правильно, вы не сможете определить какой эелемент был выбран по клику.
R.id.team
Политика на форуме запрещена

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 16:45

Вопрос стоит сделать или не сделать, а разобраться с работой БД на конкретном примере. Тем более, что
SimpleCursorAdapter умеет работать с TextView и ImageView компонентами и их производными, а Checkable-производные не воспримет

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 16:50

Переписывать все приложение только из-за того, что не могу понять как работает механизм БД не стоит. Лучше разобраться в чем я ошибаюсь и, самое главное, научиться работать с БД.

Аватара пользователя
rezak90
Сообщения: 3422
Зарегистрирован: 26 июн 2012, 13:22
Откуда: UA
Контактная информация:

Re: Урок 34. Хранение данных. SQLite

Сообщение rezak90 » 16 окт 2012, 16:56

Если не хотите использовать курсор, тогда в классе Bazar добавте одно поле int id - и передавайте в конструктор айди записи из бд когда заполняете ArrayList и присваивайте его id в классе. После того как удаляете что то из ArrayList верните сначала int dbId = bazar.get(id); это будет айди удяляемой записи, далее уже обращайтесь к бд и удяляйте данные по dbId.
Надеюсь внятно объяснил, по сути добавить пару строчек.
R.id.team
Политика на форуме запрещена

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Урок 34. Хранение данных. SQLite

Сообщение AndreyI » 16 окт 2012, 17:11

dil_android писал(а):Вопрос стоит сделать или не сделать, а разобраться с работой БД на конкретном примере. Тем более, что
SimpleCursorAdapter умеет работать с TextView и ImageView компонентами и их производными, а Checkable-производные не воспримет
То с чем адаптер не умеет работать реализуется через интерфейс ViewBinder

dil_android
Сообщения: 103
Зарегистрирован: 10 сен 2012, 11:58

Re: Урок 34. Хранение данных. SQLite

Сообщение dil_android » 16 окт 2012, 17:51

Еще раз. Мне надо разобраться с тем, как работает SQLite. Один из вопросов, который я ставил в самом начале снят. id базы данных и id листа, как я понял, не имеют ничего общего и удаление происходит по id листа. Ответа на второй вопрос я никак не могу получить,а именно, почему вызывая метод удаления из БД:

rowID = acmi.id;
datasource.db.delete("mytable", "id = " + rowID, null);

и даже "физически" удаляя из листа

bazar.remove(acmi.position);

при загрузке приложения снова отображаются данные, которые должны были быть удалены?
Что я делаю не так?

Ответить