Фильтрация данных ListView при использовании CursorLoader

Вероника

Есть активность с 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 ******* = 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 = *******.read(buffer))>0){
            myOutput.write(buffer, 0, length);
        }

        myOutput.flush();
        myOutput.close();
        *******.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<cursor> {

    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<cursor> onCreateLoader(int id, Bundle args) {
        return new MyCursorLoader(this, db, id);
    }

    @Override
    public void onLoadFinished(Loader<cursor> loader, Cursor data) {
        cursorAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<cursor> loader) {
        cursorAdapter.swapCursor(null);
    }
}
</cursor></cursor></cursor></cursor>

Класс 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;
    }
}
1 ответ

Вероника

Все в связке выглядит примерно так. Код даю с импортами, потому что есть дублирующие классы из API и библиотек поддержки - чтобы не было ошибок.

Для реализации фильтра по данным списка, управляемого через CursorLoader нужно проделать всего две вещи:

  1. Получить строку для фильтрации
  2. Составить запрос в БД на получение данных по этой строке
  3. Уведомить загрузчик о изменениях

Строку для фильтра можно вводить в 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<cursor> {

    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<cursor> onCreateLoader(int id, Bundle bundle) {
        String filter = bundle.getString("filter");
        return new MyCursorLoader(this, db, id, filter);
    }

    @Override
    public void onLoadFinished(Loader<cursor> loader, Cursor data) {
          cursorAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<cursor> 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;
        }
    }
}
</cursor></cursor></cursor></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 ******* = myContext.getAssets().open(DATABASE_NAME);

        String outFileName = DB_PATH;

        OutputStream myOutput = new FileOutputStream(outFileName);

        byte[] buffer = new byte[1024];
        int length;
        while ((length = *******.read(buffer))>0){
            myOutput.write(buffer, 0, length);
        }

        myOutput.flush();
        myOutput.close();
        *******.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) {

    }
}

Остальные классы без измений. Данные классы лучше копировать целиком, так как вносил много изменений в разных местах.

licensed under cc by-sa 3.0 with attribution.