Фильтрация данных ListView при использовании CursorLoader
1,00
р.
р.
Есть активность с AutoCompleteTextView и ListView. В ListView данные выводятся через свой адаптер. Как сделать так чтобы при выполнении метода onTextChanged у AutoCompleteTextView данные в ListView менялись следующим образом: А именно чтобы в ListView оставались только те слова, которые начинаются на тот набор символов, которые вводим в AutoCompleteTextView? Я понимаю что нужно каждый раз обновлять содержимое курсора, но не представляю как. Код DBHelper public class DBHeler extends SQLiteOpenHelper { private static String DB_PATH = "/data/data/tests.mytest3/databases/" private static final String DATABASE_NAME = "dbase.db" private static final int DATABASE_VERSION = 1 public SQLiteDatabase database private Context myContext final String ruQuery = "SELECT * " + " FROM " + Contract.Entry.TABLE_RUEN final String enQuery = "SELECT * " + " FROM " + Contract.Entry.TABLE_ENRU public DBHeler(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION) this.myContext = context } public Cursor getRuWords() { return database.rawQuery(ruQuery, null) } public Cursor getEnWords() { return database.rawQuery(enQuery, null) } public void createDataBase() throws IOException { boolean dbExist = checkDataBase() if(dbExist){ //ничего не делать - база уже есть }else{ //вызывая этот метод создаем пустую базу, позже она будет перезаписана this.getReadableDatabase() try { copyDataBase() } catch (IOException e) { throw new Error("Error copying database") } } } private boolean checkDataBase(){ SQLiteDatabase checkDB = null try { String myPath = DB_PATH + DATABASE_NAME checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY) } catch(SQLiteException e){ //база еще не существует } if(checkDB != null){ checkDB.close() } return checkDB != null ? true : false } private void copyDataBase() throws IOException{ InputStream myInput = myContext.getAssets().open(DATABASE_NAME) String outFileName = DB_PATH + DATABASE_NAME OutputStream myOutput = new FileOutputStream(outFileName) byte[] buffer = new byte[1024] int length while ((length = myInput.read(buffer))>0){ myOutput.write(buffer, 0, length) } myOutput.flush() myOutput.close() myInput.close() } public void openDataBase() throws SQLException { String myPath = DB_PATH + DATABASE_NAME database = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE) } @Override public synchronized void close() { if(database != null) database.close() super.close() } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } Класс Contract public class Contract { private Contract() { } public static final class Entry implements BaseColumns { public final static String TABLE_RUEN = "ruen" public final static String TABLE_ENRU = "enru" public final static String _ID = BaseColumns._ID public final static String COLUMN_SLOVO = "word" public final static String COLUMN_PEREVOD = "translation" public final static String COLUMN_IZBRANNOE = "favorites" } } Класс адаптера public class MySimpleCursorAdapter extends SimpleCursorAdapter { private int layout public MySimpleCursorAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to, int flag) { super(context, layout, cursor, from, to, flag) this.layout = layout } public static class ViewHolder { public TextView txtBukva public TextView txtPerevod public TextView txtSlovo public ImageButton btnIzbrannoe public ViewHolder(View view) { txtBukva = (TextView) view.findViewById(R.id.txtBukva) txtSlovo = (TextView) view.findViewById(R.id.txtSlovo) txtPerevod = (TextView) view.findViewById(R.id.txtPerevod) btnIzbrannoe = (ImageButton) view.findViewById(R.id.btnIzbrannoe) } } @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) ViewHolder viewHolder = new ViewHolder(view) view.setTag(viewHolder) return view } @Override public void bindView(View view, Context context, Cursor cursor) { ViewHolder holder = (ViewHolder) view.getTag() String bukva = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO)).substring(0, 1).toUpperCase() String slovo = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO)) String perevod = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_PEREVOD)) String izbrannoe = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_IZBRANNOE)) int _ID = cursor.getInt(cursor.getColumnIndex(Contract.Entry._ID)) TextView txtBukva = (TextView) view.findViewById(R.id.txtBukva) TextView txtSlovo = (TextView) view.findViewById(R.id.txtSlovo) TextView txtPerevod = (TextView) view.findViewById(R.id.txtPerevod) ImageButton btnIzbrannoe = (ImageButton) view.findViewById(R.id.btnIzbrannoe) holder.txtBukva.setText(bukva) holder.txtSlovo.setText(slovo) holder.txtPerevod.setText(perevod) holder.btnIzbrannoe.setFocusable(false) if (izbrannoe.equals("1")) { holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_yellow) } else { holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_outline_black) } holder.btnIzbrannoe.setTag(new Slovo(_ID,slovo, perevod,bukva,izbrannoe)) } } MainActivity public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { private EditText txtSearch private Spinner spinner private ListView list private ImageButton btnClear DBHeler db private MySimpleCursorAdapter cursorAdapter final private static int LOADER_RUS = 0 final private static int LOADER_ENG = 1 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) db = new DBHeler(this) try { db.createDataBase() db.openDataBase() } catch (IOException ex) { ex.printStackTrace() } spinner = (Spinner) findViewById(R.id.spinner) txtSearch = (EditText) findViewById(R.id.txtSearch) list = (ListView) findViewById(R.id.list) btnClear = (ImageButton) findViewById(R.id.btnClear) ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(this, R.array.types, android.R.layout.simple_spinner_item) adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinner.setAdapter(adapter) String[] from = new String[] {Contract.Entry.COLUMN_SLOVO, Contract.Entry.COLUMN_SLOVO, Contract.Entry.COLUMN_PEREVOD, Contract.Entry.COLUMN_IZBRANNOE} int[] to = new int[] {R.id.txtBukva, R.id.txtSlovo, R.id.txtPerevod, R.id.btnIzbrannoe} cursorAdapter = new MySimpleCursorAdapter(this, R.layout.item, null, from, to, 0) list.setAdapter(cursorAdapter) // инициализируем оба загрузчика getSupportLoaderManager().initLoader(LOADER_RUS, null, this) getSupportLoaderManager().initLoader(LOADER_ENG, null, this) spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int loadID, long l) { getSupportLoaderManager().getLoader(loadID).forceLoad() // обновляем данные в курсоре } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }) } @Override protected void onResume() { super.onResume() list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(MainActivity.this, SlovoActivity.class) CharSequence strCharSequence = ((TextView)view.findViewById(R.id.txtSlovo)).getText() String str = strCharSequence.toString().toLowerCase().trim() String selectedItem = spinner.getSelectedItem().toString() if (selectedItem.equals("С русского на английский")) { intent.putExtra("slovo", str) intent.putExtra("type", "RU") startActivity(intent) } else if (selectedItem.equals("С английского на русский")) { intent.putExtra("slovo", str) intent.putExtra("type", "EN") startActivity(intent) } } }) } public void onFavoriteClick(View view) { Slovo data = (Slovo)view.getTag() long id = data.id String slovo = data.word String perevod = data.perevod String bukva = data.simbol boolean izbrannoe = data.favorite String selectedItem = spinner.getSelectedItem().toString() if (selectedItem.equals("С русского на английский")) { if (!izbrannoe) { ContentValues values = new ContentValues() values.put(Contract.Entry.COLUMN_IZBRANNOE, "1") // Вставляем новый ряд в базу данных и запоминаем его идентификатор long newRowId = db.database.update(Contract.Entry.TABLE_RUEN, values, Contract.Entry.COLUMN_SLOVO + "= ?", new String[]{slovo}) //izbrannoe = true // Выводим сообщение в успешном случае или при ошибке if (newRowId == -1) { // Если ID -1, значит произошла ошибка Toast.makeText(this, "Ошибка", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "Добавлено в избранное", Toast.LENGTH_SHORT).show() getSupportLoaderManager().getLoader(LOADER_RUS).forceLoad() } } else if (izbrannoe) { ContentValues values = new ContentValues() values.put(Contract.Entry.COLUMN_IZBRANNOE, "0") // Вставляем новый ряд в базу данных и запоминаем его идентификатор long newRowId = db.database.update(Contract.Entry.TABLE_RUEN, values, Contract.Entry.COLUMN_SLOVO + "= ?", new String[]{slovo}) //izbrannoe = true // Выводим сообщение в успешном случае или при ошибке if (newRowId == -1) { // Если ID -1, значит произошла ошибка Toast.makeText(this, "Ошибка", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "Удалено из избранного", Toast.LENGTH_SHORT).show() getSupportLoaderManager().getLoader(LOADER_RUS).forceLoad() } } } else if (selectedItem.equals("С английского на русский")) { if (!izbrannoe) { ContentValues values = new ContentValues() values.put(Contract.Entry.COLUMN_IZBRANNOE, "1") // Вставляем новый ряд в базу данных и запоминаем его идентификатор long newRowId = db.database.update(Contract.Entry.TABLE_ENRU, values, Contract.Entry.COLUMN_SLOVO + "= ?", new String[]{slovo}) //izbrannoe = true // Выводим сообщение в успешном случае или при ошибке if (newRowId == -1) { // Если ID -1, значит произошла ошибка Toast.makeText(this, "Ошибка", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "Добавлено в избранное", Toast.LENGTH_SHORT).show() getSupportLoaderManager().getLoader(LOADER_ENG).forceLoad() } } else if (izbrannoe) { ContentValues values = new ContentValues() values.put(Contract.Entry.COLUMN_IZBRANNOE, "0") // Вставляем новый ряд в базу данных и запоминаем его идентификатор long newRowId = db.database.update(Contract.Entry.TABLE_ENRU, values, Contract.Entry.COLUMN_SLOVO + "= ?", new String[]{slovo}) //izbrannoe = true // Выводим сообщение в успешном случае или при ошибке if (newRowId == -1) { // Если ID -1, значит произошла ошибка Toast.makeText(this, "Ошибка", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "Удалено из избранного", Toast.LENGTH_SHORT).show() getSupportLoaderManager().getLoader(LOADER_ENG).forceLoad() } } } } protected void onDestroy() { super.onDestroy() db.close() } static class MyCursorLoader extends CursorLoader { Cursor cursor DBHeler dbHeler final int loaderID public MyCursorLoader(Context context, DBHeler dbHeler, int id) { super(context) this.dbHeler = dbHeler loaderID = id } @Override protected Cursor onLoadInBackground() { switch (loaderID) { case LOADER_RUS: cursor = dbHeler.getRuWords() break case LOADER_ENG: cursor = dbHeler.getEnWords() break } return cursor } } @Override public Loader onCreateLoader(int id, Bundle args) { return new MyCursorLoader(this, db, id) } @Override public void onLoadFinished(Loader loader, Cursor data) { cursorAdapter.swapCursor(data) } @Override public void onLoaderReset(Loader loader) { cursorAdapter.swapCursor(null) } } Класс Slovo public class Slovo { long id String word String perevod String simbol boolean favorite public Slovo(long id, String word, String perevod, String simbol, String favorite) { this.id = id this.word = word this.perevod = perevod this.simbol = simbol this.favorite = (favorite.equals("1"))? true:false } }
Ответ Все в связке выглядит примерно так. Код даю с импортами, потому что есть дублирующие классы из API и библиотек поддержки - чтобы не было ошибок. Для реализации фильтра по данным списка, управляемого через CursorLoader нужно проделать всего две вещи: Получить строку для фильтрации Составить запрос в БД на получение данных по этой строке Уведомить загрузчик о изменениях Строку для фильтра можно вводить в EditText или SearchView. Подключаем слушатель, который позволяет получать текст по мере набора и делаем в БД запросы по этому набору. Для обновления списка используем метод менеджера загрузчиков restartLoader(), который уведомляет, что выборка из БД изменилась и надо обновить список. Запрос на выборку данных из БД по неполному соответствию (только начало слова) делаем через оператор SQL LIKE, который находит вхождение подстроки в строке. На SQL запрос выглядит так: SELECT * FROM table WHERE UPPER(column) LIKE substrig% что значит - выбрать все столбцы из таблицы table, где в столбце column встречается подстрока substring без учета регистра (с кириллическими символами к сожалению без мутных костылей это не работает). класс MainActivity. Здесь помимо реализации самого фильтра по списку много рефакторинга, улучшающего и оптимизирующего код, надеюсь вы сравните и примите на заметку в будущем: import android.content.ContentValues import android.content.Context import android.content.Intent import android.database.Cursor import android.os.Bundle import android.support.v4.app.LoaderManager import android.support.v4.content.Loader import android.support.v4.content.CursorLoader import android.support.v7.app.AppCompatActivity import android.text.Editable import android.text.TextWatcher import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.EditText import android.widget.ImageButton import android.widget.ListView import android.widget.Spinner import android.widget.Toast public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { private EditText txtSearch private Spinner spinner private ListView list private ImageButton btnClear DBHeler db private MyCursorAdapter cursorAdapter private int currentLoader final private static int LOADER_RUS = 0 final private static int LOADER_ENG = 1 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) db = new DBHeler(this) spinner = (Spinner) findViewById(R.id.spinner) txtSearch = (EditText) findViewById(R.id.txtSearch) list = (ListView) findViewById(R.id.list) btnClear = (ImageButton) findViewById(R.id.btnClear) ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(this, R.array.types, android.R.layout.simple_spinner_item) adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinner.setAdapter(adapter) cursorAdapter = new MyCursorAdapter(this, R.layout.item, null, 0) list.setAdapter(cursorAdapter) // при старте выводим все данные (фильтр - пустая строка) Bundle bundle = new Bundle(1) bundle.putString("filter", "") getSupportLoaderManager().initLoader(LOADER_RUS, bundle, this) getSupportLoaderManager().initLoader(LOADER_ENG, bundle, this) spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int loadID, long l) { getSupportLoaderManager().getLoader(loadID).forceLoad() currentLoader = loadID txtSearch.setText(null) // при смене языка очищаем поле поиска } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }) txtSearch.addTextChangedListener(new TextWatcher(){ public void onTextChanged(CharSequence s, int start, int before, int count) { // получаем строку по мере ввода refreshCursor(s.toString()) } @Override public void afterTextChanged(Editable text) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} }) } private void refreshCursor(String str) { //передаем в загрузчик строку для фильтра и перезапускаем загрузчик для обновления списка Bundle bundle = new Bundle(1) bundle.putString("filter", str) getSupportLoaderManager().restartLoader(currentLoader, bundle, this) } @Override protected void onResume() { super.onResume() list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { Intent intent = new Intent(MainActivity.this, SlovoActivity.class) Cursor cursor = (Cursor) cursorAdapter.getItem(position) String str = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO)).toLowerCase().trim() int selectedItem = spinner.getSelectedItemPosition() intent.putExtra("slovo", str) intent.putExtra("type", selectedItem == 0 ? "RU" : "EN") startActivity(intent) } }) } public void onFavoriteClick(View view) { Slovo data = (Slovo)view.getTag() long id = data.id String slovo = data.word String perevod = data.perevod String bukva = data.simbol boolean izbrannoe = data.favorite String table String fav String toast int currentLoader if (spinner.getSelectedItemPosition() == 0) { table = Contract.Entry.TABLE_RUEN currentLoader = LOADER_RUS } else { table = Contract.Entry.TABLE_ENRU currentLoader = LOADER_ENG } if (izbrannoe) { fav = "0" toast = "Удалено из избранного" } else { fav = "1" toast = "Добавлено в избранное" } ContentValues values = new ContentValues() values.put(Contract.Entry.COLUMN_IZBRANNOE, fav) long newRowId = db.database.update(table, values, Contract.Entry.COLUMN_SLOVO + "= ?", new String[]{slovo}) if (newRowId == -1) { Toast.makeText(this, "Ошибка", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, toast, Toast.LENGTH_SHORT).show() getSupportLoaderManager().getLoader(currentLoader).forceLoad() } } protected void onDestroy() { super.onDestroy() // закрывать БД здесь не следует, так как это приводит к падениям при повороте // многие склоняются к мысли, что БД вообще не следует закрывать } @Override public Loader onCreateLoader(int id, Bundle bundle) { String filter = bundle.getString("filter") return new MyCursorLoader(this, db, id, filter) } @Override public void onLoadFinished(Loader loader, Cursor data) { cursorAdapter.swapCursor(data) } @Override public void onLoaderReset(Loader loader) { cursorAdapter.swapCursor(null) } static class MyCursorLoader extends CursorLoader { Cursor cursor DBHeler dbHeler final int loaderID String filter public MyCursorLoader(Context context, DBHeler dbHeler, int id, String filter) { super(context) this.dbHeler = dbHeler loaderID = id this.filter = filter } @Override protected Cursor onLoadInBackground() { switch (loaderID) { case LOADER_RUS: // выборка с учетом фильтра cursor = dbHeler.getRuWords(filter) break case LOADER_ENG: cursor = dbHeler.getEnWords(filter) break } return cursor } } } Информация о необходимости [не]закрытия базы данных. Для обработки клика на избранном используется такой способ. Класс MyCursorAdapter. Здесь тоже небольшой рефакторинг и оценив, что требуется от адаптера в вашем случае, решил отказаться от SimpleCursorAdapter, так как его возможности все равно не используются: import android.content.Context import android.database.Cursor import android.support.v4.widget.CursorAdapter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageButton import android.widget.TextView public class MyCursorAdapter extends CursorAdapter { private int layout public MyCursorAdapter(Context context, int layout, Cursor cursor, int flag) { super(context, cursor, flag) this.layout = layout } public static class ViewHolder { public TextView txtBukva public TextView txtPerevod public TextView txtSlovo public ImageButton btnIzbrannoe public ViewHolder(View view) { txtBukva = (TextView) view.findViewById(R.id.txtBukva) txtSlovo = (TextView) view.findViewById(R.id.txtSlovo) txtPerevod = (TextView) view.findViewById(R.id.txtPerevod) btnIzbrannoe = (ImageButton) view.findViewById(R.id.btnIzbrannoe) } } @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) ViewHolder viewHolder = new ViewHolder(view) view.setTag(viewHolder) return view } @Override public void bindView(View view, Context context, Cursor cursor) { ViewHolder holder = (ViewHolder) view.getTag() String bukva = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO)).substring(0, 1).toUpperCase() String slovo = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_SLOVO)) String perevod = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_PEREVOD)) String izbrannoe = cursor.getString(cursor.getColumnIndex(Contract.Entry.COLUMN_IZBRANNOE)) int _ID = cursor.getInt(cursor.getColumnIndex(Contract.Entry._ID)) holder.txtBukva.setText(bukva) holder.txtSlovo.setText(slovo) holder.txtPerevod.setText(perevod) holder.btnIzbrannoe.setFocusable(false) // здесь получение ссылок на виджеты совершенно излишне и "убивает" // преимущество использования ViewHolder if (izbrannoe.equals("1")) { holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_yellow) } else { holder.btnIzbrannoe.setImageResource(R.drawable.icon_star_outline_black) } holder.btnIzbrannoe.setTag(new Slovo(_ID,slovo, perevod,bukva,izbrannoe)) } } класс DBhelper. Ваш вариант этого класса не захотел у меня копировать БД, предлагаю свой, не зависимый от имени приложения. Так же небольшой рефакторинг - в БД лучше отправлять не "сырые" запросы rawQuery()), а через метод query(), он более безопасен: public class DBHeler extends SQLiteOpenHelper { private static String DB_PATH private static final String DATABASE_NAME = "dbase.db" private static final int DATABASE_VERSION = 1 public SQLiteDatabase database private Context myContext public DBHeler(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION) this.myContext = context try { //получаем путь до БД вместе с именем. DB_PATH = myContext.getDatabasePath(DATABASE_NAME).toString() createDataBase() openDataBase() } catch (IOException e) { e.printStackTrace() } } public Cursor getRuWords(String filter) { return database.query(Contract.Entry.TABLE_RUEN, new String[] {"*"}, "UPPER(" + Contract.Entry.COLUMN_SLOVO + ")" + " LIKE ?", new String[] {filter + "%"}, null, null, null) } public Cursor getEnWords(String filter) { return database.query(Contract.Entry.TABLE_ENRU, new String[] {"*"}, "UPPER(" + Contract.Entry.COLUMN_SLOVO + ")" + " LIKE ?", new String[] {filter + "%"}, null, null, null) } public void createDataBase() throws IOException { boolean dbExist = checkDataBase() if (!dbExist) { this.getReadableDatabase() try { copyDataBase() } catch (IOException e) { throw new Error("Error copying database") } } } private boolean checkDataBase(){ SQLiteDatabase checkDB = null try { checkDB = SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READONLY) } catch (SQLiteException e) { // база не существует } if (checkDB != null) { checkDB.close() } return checkDB != null } private void copyDataBase() throws IOException { InputStream myInput = myContext.getAssets().open(DATABASE_NAME) String outFileName = DB_PATH OutputStream myOutput = new FileOutputStream(outFileName) byte[] buffer = new byte[1024] int length while ((length = myInput.read(buffer))>0){ myOutput.write(buffer, 0, length) } myOutput.flush() myOutput.close() myInput.close() } public void openDataBase() throws SQLException { database = SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READWRITE) } @Override public synchronized void close() { if(database != null) database.close() super.close() } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } Остальные классы без измений. Данные классы лучше копировать целиком, так как вносил много изменений в разных местах.