There are many ways to store your data, such as in an online database, in a local SQLite database, or even in a text file. It is up to you to decide what is the best solution for your application.
A virtual table behaves similarly to a SQLite table, but reads and writes to an object in memory via callbacks, instead of to a database file.
> Create an inner class in DatabaseTable
that extends SQLiteOpenHelper
.
public class DatabaseTable { private static final String TAG = "DictionaryDatabase"; //The columns we'll include in the dictionary table public static final String COL_WORD = "WORD"; public static final String COL_DEFINITION = "DEFINITION"; private static final String DATABASE_NAME = "DICTIONARY"; private static final String FTS_VIRTUAL_TABLE = "FTS"; private static final int DATABASE_VERSION = 1; private final DatabaseOpenHelper mDatabaseOpenHelper; public DatabaseTable(Context context) { mDatabaseOpenHelper = new DatabaseOpenHelper(context); } private static class DatabaseOpenHelper extends SQLiteOpenHelper { private final Context mHelperContext; private SQLiteDatabase mDatabase; private static final String FTS_TABLE_CREATE = "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE + " USING fts3 (" + COL_WORD + ", " + COL_DEFINITION + ")"; DatabaseOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); mHelperContext = context; } @Override public void onCreate(SQLiteDatabase db) { mDatabase = db; mDatabase.execSQL(FTS_TABLE_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE); onCreate(db); } } }
Tip: You also might want to set up a callback to notify your UI activity of this thread's completion.
private void loadDictionary() { new Thread(new Runnable() { public void run() { try { loadWords(); } catch (IOException e) { throw new RuntimeException(e); } } }).start(); } private void loadWords() throws IOException { final Resources resources = mHelperContext.getResources(); InputStream inputStream = resources.openRawResource(R.raw.definitions); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); try { String line; while ((line = reader.readLine()) != null) { String[] strings = TextUtils.split(line, "-"); if (strings.length < 2) continue; long id = addWord(strings[0].trim(), strings[1].trim()); if (id < 0) { Log.e(TAG, "unable to add word: " + strings[0].trim()); } } } finally { reader.close(); } } public long addWord(String word, String definition) { ContentValues initialValues = new ContentValues(); initialValues.put(COL_WORD, word); initialValues.put(COL_DEFINITION, definition); return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues); }
Add the following methods to the DatabaseTable
class to build a SQL statement that searches for the query:
public Cursor getWordMatches(String query, String[] columns) { String selection = COL_WORD + " MATCH ?"; String[] selectionArgs = new String[] {query+"*"}; return query(selection, selectionArgs, columns); } private Cursor query(String selection, String[] selectionArgs, String[] columns) { SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); builder.setTables(FTS_VIRTUAL_TABLE); Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(), columns, selection, selectionArgs, null, null, null); if (cursor == null) { return null; } else if (!cursor.moveToFirst()) { cursor.close(); return null; } return cursor; }Remember that the searchable activity receives the query inside of the
ACTION_SEARCH
intent as an extra, because of the intent filter that you previously created.
SearchView
and action bar are only available on Android 3.0 and later. To support older platforms, you can fall back to the search dialog. The search dialog is a system provided UI that overlays on top of your application when invoked.
To setup the search dialog, first declare in your manifest that you want to support older devices, but want to target Android 3.0 or later versions. When you do this, your application automatically uses the action bar on Android 3.0 or later and uses the traditional menu system on older devices:
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15" />
Because Android 3.0 and higher devices show the SearchView
in the action bar (as demonstrated in the first lesson), only versions older than 3.0 call onOptionsItemSelected()
when the user selects the search menu item.
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.search: onSearchRequested(); return true; default: return false; } }
> At runtime, check the device version to make sure an unsupported use of SearchView
does not occur on older devices. In our example code, this happens in the onCreateOptionsMenu()
method:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.options_menu, menu); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView(); searchView.setSearchableInfo( searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); } return true; }