Skip to content

Commit b285125

Browse files
Robert LyAndroid (Google) Code Review
authored andcommitted
Merge "docs: android u search class" into ics-mr1
2 parents 7f94977 + 055e3f2 commit b285125

File tree

5 files changed

+574
-0
lines changed

5 files changed

+574
-0
lines changed

docs/html/resources/resources_toc.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,26 @@
9898
</ul>
9999
</li>
100100

101+
<li class="toggle-list">
102+
<div><a href="<?cs var:toroot ?>training/search/index.html">
103+
<span class="en">Adding Search Functionality</span>
104+
</a></div>
105+
<ul>
106+
<li><a href="<?cs var:toroot ?>training/search/setup.html">
107+
<span class="en">Setting up the Search Interface</span>
108+
</a>
109+
</li>
110+
<li><a href="<?cs var:toroot ?>training/search/search.html">
111+
<span class="en">Storing and Searching for Data</span>
112+
</a>
113+
</li>
114+
<li><a href="<?cs var:toroot ?>training/search/backward-compat.html">
115+
<span class="en">Remaining Backward Compatible</span>
116+
</a>
117+
</li>
118+
</ul>
119+
</li>
120+
101121
<li class="toggle-list">
102122
<div><a href="<?cs var:toroot ?>training/id-auth/index.html">
103123
<span class="en">Remembering Users</span>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
page.title=Remaining Backward Compatible
2+
trainingnavtop=true
3+
previous.title=Storing and Searching for Data
4+
previous.link=search.html
5+
6+
@jd:body
7+
8+
<div id="tb-wrapper">
9+
<div id="tb">
10+
<h2>This lesson teaches you to</h2>
11+
12+
<ul>
13+
<li><a href="{@docRoot}training/search/backward-compat.html#set-sdk">Set Minimum and Target
14+
SDK Values</a></li>
15+
16+
<li><a href="{@docRoot}training/search/backward-compat.html#provide-sd">Provide the Search
17+
Dialog for Older Devices</a></li>
18+
19+
<li><a href="{@docRoot}training/search/backward-compat.html#check-ver">Check the Android Build
20+
Version at Runtime</a></li>
21+
</ul>
22+
</div>
23+
</div>
24+
25+
<p>The {@link android.widget.SearchView} and action bar are only available on Android 3.0 and
26+
later. To support older platforms, you can fall back to the search dialog. The search dialog is a
27+
system provided UI that overlays on top of your application when invoked.</p>
28+
29+
<h2 id="set-sdk">Set Minimum and Target API levels</h2>
30+
31+
<p>To setup the search dialog, first declare in your manifest that you want to support older
32+
devices, but want to target Android 3.0 or later versions. When you do this, your application
33+
automatically uses the action bar on Android 3.0 or later and uses the traditional menu system on
34+
older devices:</p>
35+
<pre>
36+
&lt;uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15" /&gt;
37+
38+
&lt;application&gt;
39+
...
40+
</pre>
41+
42+
<h2 id="provide-sd">Provide the Search Dialog for Older Devices</h2>
43+
44+
<p>To invoke the search dialog on older devices, call {@link
45+
android.app.Activity#onSearchRequested onSearchRequested()} whenever a user selects the search
46+
menu item from the options menu. Because Android 3.0 and higher devices show the
47+
{@link android.widget.SearchView} in the action bar (as demonstrated in the first lesson), only versions
48+
older than 3.0 call {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} when the
49+
user selects the search menu item.
50+
</p>
51+
<pre>
52+
&#64;Override
53+
public boolean onOptionsItemSelected(MenuItem item) {
54+
switch (item.getItemId()) {
55+
case R.id.search:
56+
onSearchRequested();
57+
return true;
58+
default:
59+
return false;
60+
}
61+
}
62+
</pre>
63+
64+
<h2 id="check-ver">Check the Android Build Version at Runtime</h2>
65+
66+
<p>At runtime, check the device version to make sure an unsupported use of {@link
67+
android.widget.SearchView} does not occur on older devices. In our example code, this happens in
68+
the {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method:</p>
69+
<pre>
70+
&#64;Override
71+
public boolean onCreateOptionsMenu(Menu menu) {
72+
73+
MenuInflater inflater = getMenuInflater();
74+
inflater.inflate(R.menu.options_menu, menu);
75+
76+
if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB) {
77+
SearchManager searchManager =
78+
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
79+
SearchView searchView =
80+
(SearchView) menu.findItem(R.id.search).getActionView();
81+
searchView.setSearchableInfo(
82+
searchManager.getSearchableInfo(getComponentName()));
83+
searchView.setIconifiedByDefault(false);
84+
}
85+
return true;
86+
}
87+
</pre>

docs/html/training/search/index.jd

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
page.title=Adding Search Functionality
2+
trainingnavtop=true
3+
startpage=true
4+
next.title=Setting Up the Search Interface
5+
next.link=setup.html
6+
7+
@jd:body
8+
9+
<div id="tb-wrapper">
10+
<div id="tb">
11+
<h2>Dependencies and prerequisites</h2>
12+
13+
<ul>
14+
<li>Android 3.0 or later (with some support for Android 2.1)</li>
15+
16+
<li>Experience building an Android <a href="{@docRoot}guide/topics/ui/index.html">User
17+
Interface</a></li>
18+
</ul>
19+
20+
<h2>You should also read</h2>
21+
22+
<ul>
23+
<li><a href="{@docRoot}guide/topics/search/index.html">Search</a></li>
24+
25+
<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
26+
Dictionary Sample App</a></li>
27+
</ul>
28+
</div>
29+
</div>
30+
31+
<p>Android's built-in search features offer apps an easy way to provide a
32+
consistent search experience for all users. There are two ways to implement search in your app
33+
depending on the version of Android that is running on the device. This class covers how to add
34+
search with {@link android.widget.SearchView}, which was introduced in Android 3.0, while
35+
maintaining backward compatibility with older versions of Android by using the default search
36+
dialog provided by the system.</p>
37+
38+
<h2>Lessons</h2>
39+
40+
<dl>
41+
<dt><b><a href="setup.html">Setting Up the Search Interface</a></b></dt>
42+
43+
<dd>Learn how to add a search interface to your app and how to configure an activity to handle
44+
search queries.</dd>
45+
46+
<dt><b><a href="search.html">Storing and Searching for Data</a></b></dt>
47+
48+
<dd>Learn a simple way to store and search for data in a SQLite virtual database table.</dd>
49+
50+
<dt><b><a href="backward-compat.html">Remaining Backward Compatible</a></b></dt>
51+
52+
<dd>Learn how to keep search features backward compatible with older devices by using.</dd>
53+
</dl>
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
page.title=Storing and Searching for Data
2+
trainingnavtop=true
3+
previous.title=Setting Up the Search Interface
4+
previous.link=setup.html
5+
next.title=Remaining Backward Compatible
6+
next.link=backward-compat.html
7+
8+
@jd:body
9+
10+
<div id="tb-wrapper">
11+
<div id="tb">
12+
<h2>This lesson teaches you to</h2>
13+
14+
<ul>
15+
<li><a href="{@docRoot}training/search/search.html#create">Create the Virtual
16+
Table</a></li>
17+
18+
<li><a href="{@docRoot}training/search/search.html#populate">Populate the Virtual
19+
Table</a></li>
20+
21+
<li><a href="{@docRoot}training/search/search.html#search">Search for the Query</a></li>
22+
</ul>
23+
</div>
24+
</div>
25+
26+
<p>There are many ways to store your data, such as in an online database, in a local SQLite
27+
database, or even in a text file. It is up to you to decide what is the best solution for your
28+
application. This lesson shows you how to create a SQLite virtual table that can provide robust
29+
full-text searching. The table is populated with data from a text file that contains a word and
30+
definition pair on each line in the file.</p>
31+
32+
<h2 id="create">Create the Virtual Table</h2>
33+
34+
<p>A virtual table behaves similarly to a SQLite table, but reads and writes to an object in
35+
memory via callbacks, instead of to a database file. To create a virtual table, create a class
36+
for the table:</p>
37+
<pre>
38+
public class DatabaseTable {
39+
private final DatabaseOpenHelper mDatabaseOpenHelper;
40+
41+
public DatabaseTable(Context context) {
42+
mDatabaseOpenHelper = new DatabaseOpenHelper(context);
43+
}
44+
}
45+
</pre>
46+
47+
<p>Create an inner class in <code>DatabaseTable</code> that extends {@link
48+
android.database.sqlite.SQLiteOpenHelper}. The {@link android.database.sqlite.SQLiteOpenHelper} class
49+
defines abstract methods that you must override so that your database table can be created and
50+
upgraded when necessary. For example, here is some code that declares a database table that will
51+
contain words for a dictionary app:</p>
52+
<pre>
53+
public class DatabaseTable {
54+
55+
private static final String TAG = "DictionaryDatabase";
56+
57+
//The columns we'll include in the dictionary table
58+
public static final String COL_WORD = "WORD";
59+
public static final String COL_DEFINITION = "DEFINITION";
60+
61+
private static final String DATABASE_NAME = "DICTIONARY";
62+
private static final String FTS_VIRTUAL_TABLE = "FTS";
63+
private static final int DATABASE_VERSION = 1;
64+
65+
private final DatabaseOpenHelper mDatabaseOpenHelper;
66+
67+
public DatabaseTable(Context context) {
68+
mDatabaseOpenHelper = new DatabaseOpenHelper(context);
69+
}
70+
71+
private static class DatabaseOpenHelper extends SQLiteOpenHelper {
72+
73+
private final Context mHelperContext;
74+
private SQLiteDatabase mDatabase;
75+
76+
private static final String FTS_TABLE_CREATE =
77+
"CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
78+
" USING fts3 (" +
79+
COL_WORD + ", " +
80+
COL_DEFINITION + ")";
81+
82+
DatabaseOpenHelper(Context context) {
83+
super(context, DATABASE_NAME, null, DATABASE_VERSION);
84+
mHelperContext = context;
85+
}
86+
87+
&#64;Override
88+
public void onCreate(SQLiteDatabase db) {
89+
mDatabase = db;
90+
mDatabase.execSQL(FTS_TABLE_CREATE);
91+
}
92+
93+
&#64;Override
94+
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
95+
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
96+
+ newVersion + ", which will destroy all old data");
97+
db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
98+
onCreate(db);
99+
}
100+
}
101+
}
102+
</pre>
103+
104+
<h2 id="populate">Populate the Virtual Table</h2>
105+
106+
<p>The table now needs data to store. The following code shows you how to read a text file
107+
(located in <code>res/raw/definitions.txt</code>) that contains words and their definitions, how
108+
to parse that file, and how to insert each line of that file as a row in the virtual table. This
109+
is all done in another thread to prevent the UI from locking. Add the following code to your
110+
<code>DatabaseOpenHelper</code> inner class.</p>
111+
112+
<p class="note"><strong>Tip:</strong> You also might want to set up a callback to notify your UI
113+
activity of this thread's completion.</p>
114+
<pre>
115+
private void loadDictionary() {
116+
new Thread(new Runnable() {
117+
public void run() {
118+
try {
119+
loadWords();
120+
} catch (IOException e) {
121+
throw new RuntimeException(e);
122+
}
123+
}
124+
}).start();
125+
}
126+
127+
private void loadWords() throws IOException {
128+
final Resources resources = mHelperContext.getResources();
129+
InputStream inputStream = resources.openRawResource(R.raw.definitions);
130+
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
131+
132+
try {
133+
String line;
134+
while ((line = reader.readLine()) != null) {
135+
String[] strings = TextUtils.split(line, "-");
136+
if (strings.length &lt; 2) continue;
137+
long id = addWord(strings[0].trim(), strings[1].trim());
138+
if (id &lt; 0) {
139+
Log.e(TAG, "unable to add word: " + strings[0].trim());
140+
}
141+
}
142+
} finally {
143+
reader.close();
144+
}
145+
}
146+
147+
public long addWord(String word, String definition) {
148+
ContentValues initialValues = new ContentValues();
149+
initialValues.put(COL_WORD, word);
150+
initialValues.put(COL_DEFINITION, definition);
151+
152+
return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
153+
}
154+
</pre>
155+
156+
<p>Call the <code>loadDictionary()</code> method wherever appropriate to populate the table. A
157+
good place would be in the {@link android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}
158+
method of the <code>DatabaseOpenHelper</code> class, right after you create the table:</p>
159+
<pre>
160+
&#64;Override
161+
public void onCreate(SQLiteDatabase db) {
162+
mDatabase = db;
163+
mDatabase.execSQL(FTS_TABLE_CREATE);
164+
loadDictionary();
165+
}
166+
</pre>
167+
168+
<h2 id="search">Search for the Query</h2>
169+
170+
<p>When you have the virtual table created and populated, use the query supplied by your {@link
171+
android.widget.SearchView} to search the data. Add the following methods to the
172+
<code>DatabaseTable</code> class to build a SQL statement that searches for the query:</p>
173+
<pre>
174+
public Cursor getWordMatches(String query, String[] columns) {
175+
String selection = COL_WORD + " MATCH ?";
176+
String[] selectionArgs = new String[] {query+"*"};
177+
178+
return query(selection, selectionArgs, columns);
179+
}
180+
181+
private Cursor query(String selection, String[] selectionArgs, String[] columns) {
182+
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
183+
builder.setTables(FTS_VIRTUAL_TABLE);
184+
185+
Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(),
186+
columns, selection, selectionArgs, null, null, null);
187+
188+
if (cursor == null) {
189+
return null;
190+
} else if (!cursor.moveToFirst()) {
191+
cursor.close();
192+
return null;
193+
}
194+
return cursor;
195+
}
196+
</pre>
197+
198+
<p>Search for a query by calling <code>getWordMatches()</code>. Any matching results are returned
199+
in a {@link android.database.Cursor} that you can iterate through or use to build a {@link android.widget.ListView}.
200+
This example calls <code>getWordMatches()</code> in the <code>handleIntent()</code> method of the searchable
201+
activity. Remember that the searchable activity receives the query inside of the {@link
202+
android.content.Intent#ACTION_SEARCH} intent as an extra, because of the intent filter that you
203+
previously created:</p>
204+
<pre>
205+
DatabaseTable db = new DatabaseTable(this);
206+
207+
...
208+
209+
private void handleIntent(Intent intent) {
210+
211+
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
212+
String query = intent.getStringExtra(SearchManager.QUERY);
213+
Cursor c = db.getWordMatches(query, null);
214+
//process Cursor and display results
215+
}
216+
}
217+
</pre>

0 commit comments

Comments
 (0)