diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs index 848303751a1c9..890f92a42a1f0 100644 --- a/docs/html/resources/resources_toc.cs +++ b/docs/html/resources/resources_toc.cs @@ -98,6 +98,26 @@ +
The {@link android.widget.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" /> + +<application> +... ++ +
To invoke the search dialog on older devices, call {@link + android.app.Activity#onSearchRequested onSearchRequested()} whenever a user selects the search + menu item from the options menu. Because Android 3.0 and higher devices show the + {@link android.widget.SearchView} in the action bar (as demonstrated in the first lesson), only versions + older than 3.0 call {@link android.app.Activity#onOptionsItemSelected 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 {@link + android.widget.SearchView} does not occur on older devices. In our example code, this happens in + the {@link android.app.Activity#onCreateOptionsMenu 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;
+}
+
diff --git a/docs/html/training/search/index.jd b/docs/html/training/search/index.jd
new file mode 100644
index 0000000000000..bfd16187c4b3a
--- /dev/null
+++ b/docs/html/training/search/index.jd
@@ -0,0 +1,53 @@
+page.title=Adding Search Functionality
+trainingnavtop=true
+startpage=true
+next.title=Setting Up the Search Interface
+next.link=setup.html
+
+@jd:body
+
+ Android's built-in search features offer apps an easy way to provide a + consistent search experience for all users. There are two ways to implement search in your app + depending on the version of Android that is running on the device. This class covers how to add + search with {@link android.widget.SearchView}, which was introduced in Android 3.0, while + maintaining backward compatibility with older versions of Android by using the default search + dialog provided by the system.
+ +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. This lesson shows you how to create a SQLite virtual table that can provide robust + full-text searching. The table is populated with data from a text file that contains a word and + definition pair on each line in the file.
+ +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. To create a virtual table, create a class + for the table:
+
+public class DatabaseTable {
+ private final DatabaseOpenHelper mDatabaseOpenHelper;
+
+ public DatabaseTable(Context context) {
+ mDatabaseOpenHelper = new DatabaseOpenHelper(context);
+ }
+}
+
+
+ Create an inner class in DatabaseTable that extends {@link
+ android.database.sqlite.SQLiteOpenHelper}. The {@link android.database.sqlite.SQLiteOpenHelper} class
+ defines abstract methods that you must override so that your database table can be created and
+ upgraded when necessary. For example, here is some code that declares a database table that will
+ contain words for a dictionary app:
+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);
+ }
+ }
+}
+
+
+ The table now needs data to store. The following code shows you how to read a text file
+ (located in res/raw/definitions.txt) that contains words and their definitions, how
+ to parse that file, and how to insert each line of that file as a row in the virtual table. This
+ is all done in another thread to prevent the UI from locking. Add the following code to your
+ DatabaseOpenHelper inner class.
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);
+}
+
+
+ Call the loadDictionary() method wherever appropriate to populate the table. A
+ good place would be in the {@link android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}
+ method of the DatabaseOpenHelper class, right after you create the table:
+@Override
+public void onCreate(SQLiteDatabase db) {
+ mDatabase = db;
+ mDatabase.execSQL(FTS_TABLE_CREATE);
+ loadDictionary();
+}
+
+
+ When you have the virtual table created and populated, use the query supplied by your {@link
+ android.widget.SearchView} to search the data. 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;
+}
+
+
+ Search for a query by calling getWordMatches(). Any matching results are returned
+ in a {@link android.database.Cursor} that you can iterate through or use to build a {@link android.widget.ListView}.
+ This example calls getWordMatches() in the handleIntent() method of the searchable
+ activity. Remember that the searchable activity receives the query inside of the {@link
+ android.content.Intent#ACTION_SEARCH} intent as an extra, because of the intent filter that you
+ previously created:
+DatabaseTable db = new DatabaseTable(this);
+
+...
+
+private void handleIntent(Intent intent) {
+
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ Cursor c = db.getWordMatches(query, null);
+ //process Cursor and display results
+ }
+}
+
\ No newline at end of file
diff --git a/docs/html/training/search/setup.jd b/docs/html/training/search/setup.jd
new file mode 100644
index 0000000000000..044e422fe6a6a
--- /dev/null
+++ b/docs/html/training/search/setup.jd
@@ -0,0 +1,197 @@
+page.title=Setting Up the Search Interface
+trainingnavtop=true
+next.title=Storing and Searching for Data
+next.link=search.html
+
+@jd:body
+
+ Beginning in Android 3.0, using the {@link android.widget.SearchView} widget as an item in + the action bar is the preferred way to provide search in your app. Like with all items in + the action bar, you can define the {@link android.widget.SearchView} to show at all times, only + when there is room, or as a collapsible action, which displays the {@link + android.widget.SearchView} as an icon initially, then takes up the entire action bar as a search + field when the user clicks the icon.
+ +Note: Later in this class, you will learn how to make your + app compatible down to Android 2.1 (API level 7) for devices that do not support + {@link android.widget.SearchView}.
+ +To add a {@link android.widget.SearchView} widget to the action bar, create a file named
+ res/menu/options_menu.xml in your project and add the following code to the file.
+ This code defines how to create the search item, such as the icon to use and the title of the
+ item. The collapseActionView attribute allows your {@link android.widget.SearchView}
+ to expand to take up the whole action bar and collapse back down into a
+ normal action bar item when not in use. Because of the limited action bar space on handset devices,
+ using the collapsibleActionView attribute is recommended to provide a better
+ user experience.
+<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/search" + android:title="@string/search_title" + android:icon="@drawable/ic_search" + android:showAsAction="collapseActionView|ifRoom" + android:actionViewClass="android.widget.SearchView" /> +</menu> ++ +
Note: If you already have an existing XML file for your menu
+ items, you can add the <item> element to that file instead.
To display the {@link android.widget.SearchView} in the action bar, inflate the XML menu
+ resource (res/menu/options_menu.xml) in the {@link
+ android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method of your activity:
+@Override
+public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.options_menu, menu);
+
+ return true;
+}
+
+
+ If you run your app now, the {@link android.widget.SearchView} appears in your app's action + bar, but it isn't functional. You now need to define how the {@link + android.widget.SearchView} behaves.
+ +A searchable
+ configuration defines how the {@link android.widget.SearchView} behaves and is defined in a
+ res/xml/searchable.xml file. At a minimum, a searchable configuration must contain
+ an android:label attribute that has the same value as the
+ android:label attribute of the <application> or
+ <activity> element in your Android manifest.
+ However, we also recommend adding an android:hint attribute to give the user an idea of what to enter into the search
+ box:
+<?xml version="1.0" encoding="utf-8"?> + +<searchable xmlns:android="http://schemas.android.com/apk/res/android" + android:label="@string/app_name" + android:hint="@string/search_hint" /> ++ +
In your application's manifest file, declare a
+ <meta-data> element that points to the res/xml/searchable.xml file,
+ so that your application knows where to find it. Declare the element in an <activity>
+ that you want to display the {@link android.widget.SearchView} in:
+<activity ... > + ... + <meta-data android:name="android.app.searchable" + android:resource="@xml/searchable" /> + +</activity> ++ +
In the {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method that you + created before, associate the searchable configuration with the {@link android.widget.SearchView} + by calling {@link android.widget.SearchView#setSearchableInfo}:
+
+@Override
+public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.options_menu, menu);
+
+ // Associate searchable configuration with the SearchView
+ SearchManager searchManager =
+ (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ SearchView searchView =
+ (SearchView) menu.findItem(R.id.search).getActionView();
+ searchView.setSearchableInfo(
+ searchManager.getSearchableInfo(getComponentName()));
+
+ return true;
+}
+
+
+ The call to {@link android.app.SearchManager#getSearchableInfo getSearchableInfo()} obtains a + {@link android.app.SearchableInfo} object that is created from the searchable configuration XML + file. When the searchable configuration is correctly associated with your {@link + android.widget.SearchView}, the {@link android.widget.SearchView} starts an activity with the + {@link android.content.Intent#ACTION_SEARCH} intent when a user submits a query. You now need an + activity that can filter for this intent and handle the search query.
+ +A {@link android.widget.SearchView} tries to start an activity with the {@link + android.content.Intent#ACTION_SEARCH} when a user submits a search query. A searchable activity + filters for the {@link android.content.Intent#ACTION_SEARCH} intent and searches for the query in + some sort of data set. To create a searchable activity, declare an activity of your choice to + filter for the {@link android.content.Intent#ACTION_SEARCH} intent:
++<activity android:name=".SearchResultsActivity" ... > + ... + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + </intent-filter> + ... +</activity> ++ +
In your searchable activity, handle the {@link android.content.Intent#ACTION_SEARCH} intent by + checking for it in your {@link android.app.Activity#onCreate onCreate()} method.
+ +Note: If your searchable activity launches in single top mode
+ (android:launchMode="singleTop"), also handle the {@link
+ android.content.Intent#ACTION_SEARCH} intent in the {@link android.app.Activity#onNewIntent
+ onNewIntent()} method. In single top mode, only one instance of your activity is created and
+ subsequent calls to start your activity do not create a new activity on the
+ stack. This launch mode is useful so users can perform searches from the same activity
+ without creating a new activity instance every time.
+public class SearchResultsActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ ...
+ handleIntent(getIntent());
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ ...
+ handleIntent(intent);
+ }
+
+ private void handleIntent(Intent intent) {
+
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ //use the query to search your data somehow
+ }
+ }
+ ...
+}
+
+
+ If you run your app now, the {@link android.widget.SearchView} can accept the user's query and + start your searchable activity with the {@link android.content.Intent#ACTION_SEARCH} intent. It + is now up to you to figure out how to store and search your data given a query.
\ No newline at end of file