Création d'une activité de résultats de recherche

Lorsqu'une recherche est exécutée à l'aide de la vue de recherche, elle lance une activité de résultats de recherche associée, qui reçoit la requête de recherche en tant qu'intent. Vos résultats de recherche L'activité doit ensuite extraire la requête de recherche de l'Intent, effectuer la recherche et présenter les résultats.

Vos résultats de recherche Activity peut utiliser n'importe quelle interface utilisateur, mais il s'agit le plus souvent d'une simple liste de résultats, généralement mise en œuvre à l'aide d'une vue Recycler. Il est recommandé de définir l'activité de vos résultats de recherche comme "simple", en veillant à ce que la même instance soit utilisée de manière répétée, plutôt que de créer une nouvelle instance pour chaque recherche et d'empiler les résultats de recherche sur votre pile d'arrière-plan.

Pour indiquer qu'une activité sera utilisée pour fournir des résultats de recherche, incluez un filtre d'Intents enregistré pour l'action SEARCH:

  1. < intent-filter >
  2. <action android:name="android.intent.action.SEARCH" />
  3. < /intent-filter >

Vous devez également inclure une balise de métadonnées qui inclut un attribut name qui spécifie android.app .searchable, ainsi qu'un attribut de ressource correspondant qui spécifie la ressource XML interrogeable décrite dans la section précédente.

 

Le Listing 10-30 montre une entrée de manifeste d'application simple pour une activité de recherche.

LISTING 10-30: Enregistrement d'un résultat de recherche Activité

  1. <activity
  2. android:name=".MySearchActivity"android:label="Hoard Search"android:launchMode="singleTop">
  3. <intent-filter>
  4. <action android:name="android.intent.action.SEARCH" />
  5. </intent-filter>
  6. <meta-data
  7. android:name="android.app.searchable"android:resource="@xml/hoard_search"
  8. />
  9. < /activity >

Lorsqu’une recherche est lancée, votre activité de résultats de recherche sera lancée et la requête de recherche sera disponible à partir de l’intention de lancement, accessible via le SearchManager.QUERY supplémentaire, comme indiqué dans le listing 10-31.

 

LISTING 10-31 : Extraire la requête de recherche

  1. @Override
  2. public void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_my_search);
  5. // Parse the launch Intent to perform the search
  6. // and display the results.
  7. parseIntent(); }
  8. @Override
  9. protected void onNewIntent(Intent intent) {
  10. super.onNewIntent(intent);
  11. // If the search Activity exists, and another search
  12. // is performed, set the launch Intent to the newly
  13. // received search Intent and perform a new search.
  14. setIntent(intent); parseIntent(); }
  15. private void parseIntent() {
  16. Intent searchIntent = getIntent();
  17. // If the Activity was started to service a Search request,
  18. // extract the search query.
  19. if (Intent.ACTION_SEARCH.equals(searchIntent.getAction())) {
  20. String searchQuery = searchIntent.getStringExtra(SearchManager.QUERY);
  21. // Perform the search performSearch(searchQuery);
  22. }
  23. }
  24. private void performSearch(String searchQuery) {
  25. // TODO: Perform the search and update the UI to display the results.
  26. }

Lorsque votre activité de recherche reçoit une nouvelle requête de recherche, exécutez la recherche et affichez les résultats dans l'activité. La manière dont vous choisissez de mettre en œuvre votre requête de recherche et d’afficher ses résultats dépend de votre application, des recherches que vous effectuez et de l’emplacement où le contenu interrogeable est stocké.

Recherche d'un fournisseur de contenu

L'utilisation d'un fournisseur de contenu pour exposer les données que vous souhaitez rendre consultables présente de nombreux avantages: l'un des plus puissants est la possibilité de fournir des suggestions de recherche en temps réel.

Si vous fournissez des résultats d’un fournisseur de contenu, il est recommandé d’utiliser un chargeur de curseur pour exécuter une requête et lier le résultat à l’interface utilisateur. Dans la plupart des cas, vous souhaiterez offrir aux utilisateurs la possibilité de sélectionner un résultat de recherche et d’accéder à une partie appropriée de votre application pour l’interagir.

Le Listing 10-32 montre comment créer des résultats de recherche. Activité qui recherche un fournisseur de contenu, affiche les résultats du curseur dans une vue du recycleur et ajoute un écouteur de clic que vous pouvez utiliser pour aider un utilisateur à sélectionner un résultat de recherche. Par souci de brièveté, les ressources de présentation de l'activité et les éléments de résultat de la recherche ne sont pas inclus dans cette liste.

 

LISTING 10-32 : Effectuer une recherche et afficher les résultats

  1. public class MySearchActivity extends AppCompatActivity
  2. implements LoaderManager.LoaderCallbacks<Cursor>
  3. {
  4. private static final String QUERY_EXTRA_KEY = "QUERY_EXTRA_KEY"; private MySearchResultRecyclerViewAdapter mAdapter;
  5.  
  6. @Override
  7. public void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.searchresult_list);
  10. // Set the adapter
  11. mAdapter = new MySearchResultRecyclerViewAdapter(null, mListener);
  12. // Update the Recycler View
  13. RecyclerView resultsRecyclerView = findViewById(R.id.list);
  14. resultsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
  15. resultsRecyclerView.setAdapter(mAdapter);
  16. // Initiate the Cursor Loader
  17. getSupportLoaderManager().initLoader(0, null, this);
  18. }
  19.  
  20. @Override
  21. protected void onNewIntent(Intent intent) {
  22. super.onNewIntent(intent);
  23.  
  24. // If the search Activity exists, and another search
  25. // is performed, set the launch Intent to the newly
  26. // received search Intent and perform a new search.
  27. setIntent(intent);
  28. getSupportLoaderManager().restartLoader(0, null, this); }
  29. public Loader<Cursor> onCreateLoader(int id, Bundle args) {
  30. // Extract the search query from the Intent.
  31. String query = getIntent().getStringExtra(SearchManager.QUERY);
  32. // Construct the new query in the form of a Cursor Loader.
  33. String[] projection = {
  34. HoardDB.HoardContract.KEY_ID,
  35. HoardDB.HoardContract.KEY_GOLD_HOARD_NAME_COLUMN,
  36. HoardDB.HoardContract.KEY_GOLD_HOARDED_COLUMN
  37. };
  38. String where = HoardDB.HoardContract.KEY_GOLD_HOARD_NAME_COLUMN + " LIKE ?";
  39. String[] whereArgs = {"%" + query + "%"};
  40. String sortOrder = HoardDB.HoardContract.KEY_GOLD_HOARD_NAME_COLUMN + " COLLATE LOCALIZED ASC";
  41.  
  42. // Create the new Cursor loader.
  43. return new CursorLoader(this, MyHoardContentProvider.CONTENT_URI,projection, where, whereArgs, sortOrder); }
  44.  
  45. public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
  46. // Replace the result Cursor displayed by the Cursor Adapter with
  47. // the new result set.
  48. mAdapter.setCursor(cursor); }
  49. public void onLoaderReset(Loader<Cursor> loader) {
  50. // Remove the existing result Cursor from the List Adapter.
  51. mAdapter.setCursor(null); }
  52. private OnListItemInteractionListener mListener = new OnListItemInteractionListener() {
  53.  
  54. @Override
  55. public void onListItemClick(Uri selectedContent) {
  56. // TODO If an item is clicked, open an Activity // to display further details.
  57. }
  58. };
  59. public class MySearchResultRecyclerViewAdapter
  60. extends RecyclerView.Adapter<MySearchResultRecyclerViewAdapter.ViewHolder>
  61. {
  62. private Cursor mValues;
  63. private OnListItemInteractionListener mClickListener;
  64.  
  65. private int mHoardIdIndex = -1;
  66. private int mHoardNameIndex = -1;
  67. private int mHoardAmountIndex = -1;
  68. public MySearchResultRecyclerViewAdapter(Cursor items,OnListItemInteractionListener clickListener) {
  69. mValues = items;
  70. mClickListener = clickListener;
  71. }
  72. public void setCursor(Cursor items) {
  73. mValues = items;
  74. if (items != null) {
  75. mHoardIdIndex = items.getColumnIndex(HoardDB.HoardContract.KEY_ID);
  76. mHoardNameIndex = items.getColumnIndex(
  77. HoardDB.HoardContract.KEY_GOLD_HOARD_NAME_COLUMN);
  78. mHoardAmountIndex = items.getColumnIndex(HoardDB.HoardContract.KEY_GOLD_HOARDED_COLUMN);
  79. }
  80. notifyDataSetChanged();
  81. }
  82.  
  83. @Override
  84. public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  85. View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.searchresult_item, parent, false);
  86. return new ViewHolder(view); }
  87. @Override
  88. public void onBindViewHolder(final ViewHolder holder, int position) {
  89. if (mValues != null) {
  90. // Move the Cursor to the correct position, extract the
  91. // search result values, and assign them to the UI for
  92. // each search result.
  93. mValues.moveToPosition(position);
  94. holder.mNameView.setText(mValues.getString(mHoardNameIndex));
  95. holder.mAmountView.setText(mValues.getString(mHoardAmountIndex));
  96. // Create a Uri that points to this search result item.
  97. int rowId = mValues.getInt(mHoardIdIndex);
  98. final Uri rowAddress = ContentUris.withAppendedId(MyHoardContentProvider.CONTENT_URI,rowId);
  99. // Return the Uri to this search result item if clicked.
  100. holder.mView.setOnClickListener(new View.OnClickListener() {
  101. @Override
  102. public void onClick(View v) {
  103. mClickListener.onListItemClick(rowAddress);
  104. }
  105. });
  106. }
  107. }
  108. @Override
  109. public int getItemCount() {
  110. if (mValues != null)
  111. return mValues.getCount();
  112. else
  113. return 0; }
  114. // View Holder is used as a template to encapsulate the UI
  115. // for each search result item.
  116. public class ViewHolder extends RecyclerView.ViewHolder {
  117. public final View mView;
  118. public final TextView mNameView;
  119. public final TextView mAmountView;
  120. public ViewHolder(View view) {
  121. super(view);
  122. mView = view;
  123. mNameView = view.findViewById(R.id.id);
  124. mAmountView = view.findViewById(R.id.content);
  125. }
  126. }
  127. }
  128. // Interface used to encapsulate the behavior when a user
  129. // clicks on a search result item.
  130. public interface OnListItemInteractionListener {
  131. void onListItemClick(Uri selectedContent);
  132. }
  133. }


Utilisation du widget d'affichage de recherche

Le widget Vue de recherche apparaît et se comporte presque comme une vue Modifier le texte, mais il est conçu pour offrir des suggestions de recherche et pour lancer des requêtes de recherche dans votre application.

Vous pouvez ajouter la vue de recherche n'importe où dans votre hiérarchie de vues et la configurer de la même manière. cependant, il est recommandé de l’ajouter en tant qu’action Voir dans la barre des applications, comme indiqué dans le Listing 10-33.

 

LISTING 10-33 : Ajouter une vue de recherche à la barre d'applications

  1. < menu xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. tools:context="com.professionalandroid.apps.databasechaptersnippets.MainActivity">
  5. <item android:id="@+id/search_view"android:title="@string/search_label"app:showAsAction="collapseActionView|ifRoom"app:actionViewClass="android.support.v7.widget.SearchView" />
  6. < /menu >

La figure 10-4 illustre une vue de recherche réduite affichant une icône en forme de loupe dans la barre d'applications. Vous en apprendre plus sur la barre d’applications.

 

 ”FIGURE 10-4

Pour configurer une vue de recherche pour afficher vos résultats de recherche

Activité, vous devez d’abord ajouter une nouvelle balise de métadonnées à l’entrée du manifeste de l’activité qui héberge la vue de recherche, en définissant la valeur android.app.default_searchable sur notre activité de recherche, comme indiqué dans le Listing 10-34.

 

LISTING 10-34 : Liaison d'une vue de recherche à votre activité interrogeable

  1. < activity
  2. android:name=".MainActivity"android:label="@string/app_name">
  3. <intent-filter>
  4. <action android:name="android.intent.action.MAIN"/>
  5. <category android:name="android.intent.category.LAUNCHER"/>
  6. </intent-filter>
  7. <meta-data
  8. android:name="android.app.default_searchable"android:value=".MySearchActivity" />
  9. < /activity >

Extrayez une référence à votre SearchableInfo à l’aide de la méthode getSearchableInfo du gestionnaire de recherche. Utilisez la méthode setSearchableInfo de la vue de recherche pour lier cet objet à votre vue de recherche, comme indiqué dans le Listing 10-35.

 

LISTING 10-35 : Liaison d'une vue de recherche à votre activité interrogeable

  1. @Override
  2. public boolean onCreateOptionsMenu(Menu menu) {
  3. // Inflate the options menu from XML
  4. MenuInflater inflater = getMenuInflater();
  5. inflater.inflate(R.menu.menu_main, menu);
  6. // Use the Search Manager to find the SearchableInfo related
  7. // to this Activity.
  8. SearchManager searchManager =(SearchManager) getSystemService(Context.SEARCH_SERVICE);
  9. SearchableInfo searchableInfo = searchManager.getSearchableInfo(getComponentName());
  10. SearchView searchView = menu.findItem(R.id.search_view).getActionView();
  11. searchView.setSearchableInfo(searchableInfo);
  12. searchView.setIconifiedByDefault(false);
  13. return true;
  14. }

Une fois connecté, votre vue Recherche envoie la requête de recherche saisie à l'activité de recherche pour qu'elle soit exécutée et que les résultats s'affichent.

Par défaut, la vue de la recherche sera affichée sous forme d'icône qui, une fois touchée, s'agrandira pour afficher la zone d'édition de la recherche. Vous pouvez utiliser la méthode setIconifiedByDefault pour la désactiver et l'afficher toujours comme une zone d'édition:

  1. searchView.setIconifiedByDefault(false);

De plus, par défaut, une requête de vue de recherche est lancée lorsque l'utilisateur appuie sur Entrée. Vous pouvez également choisir d'afficher un bouton pour soumettre une recherche à l'aide de la méthode setSubmitButtonEnabled.

  1. searchView.setSubmitButtonEnabled(true);


Fournir des suggestions de recherche à l'aide d'un fournisseur de contenu 

L'une des innovations les plus intéressantes en matière de recherche est la fourniture de suggestions de recherche en temps réel au fur et à mesure que les utilisateurs saisissent leurs requêtes.

Les suggestions de recherche affichent une liste des résultats de recherche possibles sous la vue de la recherche lorsque les utilisateurs entrent dans leurs requêtes, comme indiqué dans la figure 10-5.

Les utilisateurs peuvent ensuite sélectionner une suggestion dans cette liste, ce qui permet à la FIGURE 10-5

nous de traiter ce cas directement, plutôt que d'afficher la liste des

résultats de recherche potentiels comme indiqué dans la section précédente. Les sélections de suggestions de recherche doivent toujours être gérées par l'activité de résultat de la recherche, mais cette dernière peut éventuellement démarrer une nouvelle activité sans que l'activité de recherche ne soit affichée.

Si vous souhaitez fournir des suggestions de recherche, vous devez créer (ou modifier) ​​un fournisseur de contenu pour recevoir les requêtes de recherche et renvoyer des suggestions à l'aide de la projection attendue. La rapidité est essentielle pour les résultats de recherche en temps réel. dans de nombreux cas, il est recommandé de créer un tableau séparé spécialement pour stocker et proposer des suggestions.

Pour prendre en charge les suggestions de recherche, configurez votre fournisseur de contenu pour qu'il reconnaisse des chemins d'URI spécifiques en tant que requêtes de recherche. Le Listing 10-36 montre un Uri Matcher utilisé dans un fournisseur de contenu pour comparer un URI demandé aux valeurs connues du chemin de requête de recherche.

 

LISTING 10-36 : Détection des demandes de suggestion de recherche dans le contenu

  1. Providers private static final UriMatcher uriMatcher;
  2. // Create the constants used to differentiate between the different URI
  3. // requests.
  4. private static final int ALLROWS = 1; private static final int SINGLE_ROW = 2; private static final int SEARCH = 3;
  5. static {
  6. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  7. uriMatcher.addURI("com.professionalandroid.provider.hoarder","lairs", ALLROWS);
  8. uriMatcher.addURI("com.professionalandroid.provider.hoarder","lairs/#",SINGLE_ROW);
  9. uriMatcher.addURI("com.professionalandroid.provider.hoarder",SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH); uriMatcher.addURI("com.professionalandroid.provider.hoarder",SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH);
  10. uriMatcher.addURI("com.professionalandroid.provider.hoarder",SearchManager.SUGGEST_URI_PATH_SHORTCUT, SEARCH);
  11. uriMatcher.addURI("com.professionalandroid.provider.hoarder",SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SEARCH);
  12. }

Utilisez Uri Matcher pour renvoyer le type MIME des suggestions de recherche pour les requêtes de recherche utilisant le gestionnaire getType, comme indiqué dans le Listing 10-37.

 

LISTING 10-37: Renvoi du type MIME correct pour les résultats de recherche

  1. @Nullable @Override
  2. public String getType(@NonNull Uri uri) {
  3. // Return a string that identifies the MIME type
  4. // for a Content Provider URI switch (uriMatcher.match(uri)) {
  5. case ALLROWS:
  6. return "vnd.android.cursor.dir/vnd.professionalandroid.lairs";
  7. case SINGLE_ROW:
  8. return "vnd.android.cursor.item/vnd.professionalandroid.lairs";
  9. case SEARCH :
  10. return SearchManager.SUGGEST_MIME_TYPE;
  11. default:throw new IllegalArgumentException("Unsupported URI: " + uri);
  12.  
  13. }
  14. }

Le gestionnaire de recherche demande des suggestions de recherche en lançant une requête sur votre fournisseur de contenu et en transmettant le terme de recherche actuel en tant que dernier élément du chemin de l'URI, créant ainsi de nouvelles requêtes à mesure que l'utilisateur continue à taper. Pour renvoyer des suggestions, votre fournisseur de contenu doit renvoyer un curseur avec un ensemble de colonnes prédéfinies.

Deux colonnes sont requises: SUGGEST_COLUMN_TEXT_1, qui affiche le texte du résultat de la recherche, et _id, qui indique l'ID de ligne unique. Vous pouvez également fournir une autre colonne contenant du texte et une icône à afficher à gauche ou à droite du résultat du texte.

 

Le Listing 10-38 montre comment créer une projection qui renvoie un curseur approprié pour les suggestions de recherche.

LISTING 10-38 : Création d'une projection pour renvoyer des suggestions de recherche

  1. private static final HashMap<String, String> SEARCH_SUGGEST_PROJECTION_MAP;
  2. static {
  3. SEARCH_SUGGEST_PROJECTION_MAP = new HashMap<String, String>();
  4. // Map our ID column to "_id"
  5. SEARCH_SUGGEST_PROJECTION_MAP.put("_id",HoardDB.HoardContract.KEY_ID + " AS " + "_id");
  6. // Map our search field to the suggestions's first text field
  7. SEARCH_SUGGEST_PROJECTION_MAP.put(
  8. SearchManager.SUGGEST_COLUMN_TEXT_1,HoardDB.HoardContract.KEY_GOLD_HOARD_NAME_COLUMN + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1);
  9. }

 

Pour effectuer la requête qui fournira les suggestions de recherche, utilisez Uri Matcher dans l’implémentation de votre requête, en appliquant la carte de projection du formulaire défini dans la liste précédente, comme indiqué dans le Listing 10-39.

 

LISTING 10-39 : Renvoi des suggestions de recherche pour une requête

  1. @Nullable @Override
  2. public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
  3. // Open the database.
  4. SQLiteDatabase db = null;
  5. try {
  6. db = mHoardDBOpenHelper.getWritableDatabase();
  7. } catch (SQLiteException ex) {
  8. db = mHoardDBOpenHelper.getReadableDatabase(); }
  9. // Replace these with valid SQL statements if necessary.
  10. String groupBy = null;
  11. String having = null;
  12. // Use an SQLite Query Builder to simplify constructing the // database query.
  13. SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
  14. // If this is a row query, limit the result set to the passed in row.
  15. switch (uriMatcher.match(uri)) {
  16. case SINGLE_ROW :
  17. String rowID = uri.getLastPathSegment();
  18. queryBuilder.appendWhere(HoardDB.HoardContract.KEY_ID + "=" + rowID);
  19. case SEARCH :
  20. String query = uri.getLastPathSegment();
  21. queryBuilder.appendWhere( HoardDB.HoardContract.KEY_GOLD_HOARD_NAME_COLUMN + " LIKE \"%" + query + "%\"");
  22. queryBuilder.setProjectionMap(SEARCH_SUGGEST_PROJECTION_MAP);
  23. break;
  24. default: break;
  25. }
  26. // Specify the table on which to perform the query. This can
  27. // be a specific table or a join as required.
  28. queryBuilder.setTables(HoardDB.HoardDBOpenHelper.DATABASE_TABLE);
  29. // Execute the query.
  30. Cursor cursor = queryBuilder.query(db,projection,selection,selectionArgs,groupBy,having,sortOrder);
  31. // Return the result Cursor.
  32. return cursor;
  33. }

 

La dernière étape consiste à mettre à jour votre ressource XML interrogeable, comme indiqué dans le Listing 10-40. Vous devez spécifier l'autorité du fournisseur de contenu qui sera utilisé pour fournir des suggestions de recherche pour la vue de recherche. Il peut s’agir du même fournisseur de contenu que celui utilisé pour exécuter des recherches régulières (si vous avez mappé les colonnes comme requis) ou d’un fournisseur complètement différent.

Il est également utile de spécifier un attribut searchSuggestIntentAction et searchSuggestIntentData. Ces attributs sont utilisés pour créer une intention déclenchée lorsqu’une suggestion de recherche est sélectionnée par l’utilisateur, indiquant l’action Intent, ainsi que l’URI de base à utiliser dans la valeur de données de l’intention.

 

LISTING 10-40 : Configuration d'une ressource interrogeable pour les suggestions de recherche

  1. < ?xml version="1.0" encoding="utf-8"? >
  2. <searchable
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:label="@string/app_name"android:hint="@string/search_hint"
  5. android:searchSuggestAuthority="com.professionalandroid.provider.hoarder"
  6. android:searchSuggestIntentAction="android.intent.action.VIEW"
  7. android:searchSuggestIntentData="content://com.professionalandroid.provider.hoarder/lairs">
  8. < /searchable >

 Si vous spécifiez une action d'intention et un URI de base dans la ressource pouvant faire l'objet d'une recherche, vous devez mettre à jour votre projection pour inclure une colonne nommée SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID incluant l'ID de ligne qui sera ajouté à l'URI de base, comme indiqué dans l'énumération 10-41.

 

LISTING 10-41 : Mise à jour d'une projection de suggestion de recherche pour inclure les données d'intention

  1. private static final HashMap<String, String> SEARCH_SUGGEST_PROJECTION_MAP;
  2. static {
  3. SEARCH_SUGGEST_PROJECTION_MAP = new HashMap<String, String>();
  4. // Map our ID column to "_id"
  5. SEARCH_SUGGEST_PROJECTION_MAP.put("_id",HoardDB.HoardContract.KEY_ID + " AS " + "_id");
  6. // Map our search field to the suggestions's first text field
  7. SEARCH_SUGGEST_PROJECTION_MAP.put(
  8. SearchManager.SUGGEST_COLUMN_TEXT_1,HoardDB.HoardContract.KEY_GOLD_HOARD_NAME_COLUMN + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1);
  9. // Map the ID column to the suggestion's data ID. This will be
  10. // combined with the base URI specified in our Searchable definition
  11. // to supply the data value for the selection Intent.
  12. SEARCH_SUGGEST_PROJECTION_MAP.put(
  13. SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID,KEY_ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
  14. }

 Il est également possible de spécifier des actions et des URI de données uniques pour chaque suggestion de recherche, à l’aide des constantes SUGGEST_COLUMN_INTENT_ACTION et SUGGEST_COLUMN_INTENT_DATA du gestionnaire de recherche.


Recherche dans la base de données du moniteur sismique

 Dans l'exemple suivant, vous ajoutez une fonctionnalité de recherche au projet Earthquake en ajoutant une vue de recherche prenant en charge les suggestions de recherche dans la barre d'actions:

1. Pour commencer, ouvrez le projet Earthquake et créez une nouvelle classe EarthquakeSearchProvider qui étend ContentProvider. Il sera utilisé exclusivement pour générer des suggestions de recherche pour votre vue de recherche. Incluez les stubs requis remplaçant les méthodes abstraites onCreate, getType, query, insert, delete et update:

  1. public class EarthquakeSearchProvider extends ContentProvider {
  2. @Override
  3. public boolean onCreate() {
  4. return false;
  5. }
  6.  
  7. @Nullable @Override
  8. public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
  9. @Nullable String selection,
  10. @Nullable String[] selectionArgs,
  11. @Nullable String sortOrder) {
  12. return null;
  13. }
  14.  
  15. @Nullable @Override
  16. public String getType(@NonNull Uri uri) {
  17. return null;
  18. }
  19.  
  20. @Nullable @Override
  21. public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
  22. return null;
  23. }
  24.  
  25. @Override
  26. public int delete(@NonNull Uri uri, @Nullable String selection,
  27. @Nullable String[] selectionArgs) {
  28. return 0;
  29. }
  30.  
  31. @Override
  32. public int update(@NonNull Uri uri, @Nullable ContentValues values,
  33. @Nullable String selection,
  34. @Nullable String[] selectionArgs) {
  35. return 0;
  36. }
  37. }
  38.  

2. Ajoutez un UriMatcher, qui peut être utilisé pour traiter les demandes effectuées à l'aide de modèles d'URI différents. Comme vous utiliserez ce fournisseur de contenu exclusivement pour les suggestions de recherche, vous devez uniquement inclure des correspondances pour ces types de requête:

  1. private static final
  2. int SEARCH_SUGGESTIONS = 1;
  3. // Allocate the UriMatcher object, recognize search requests.
  4. private static final UriMatcher uriMatcher;
  5. static {
  6. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI("com.professionalandroid.provider.earthquake",SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGESTIONS); uriMatcher.addURI("com.professionalandroid.provider.earthquake",SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGESTIONS); uriMatcher.addURI("com.professionalandroid.provider.earthquake",SearchManager.SUGGEST_URI_PATH_SHORTCUT, SEARCH_SUGGESTIONS);
  7. uriMatcher.addURI("com.professionalandroid.provider.earthquake", SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SEARCH_SUGGESTIONS);
  8. }

3. Redéfinissez également la méthode getType du fournisseur de contenu pour renvoyer le type MIME pour les suggestions de recherche:

  1. @Nullable @Override
  2. public String getType(@NonNull Uri uri) {
  3. switch (uriMatcher.match(uri)) {
  4. case SEARCH_SUGGESTIONS : return SearchManager.SUGGEST_MIME_TYPE;
  5. default: throw new IllegalArgumentException("Unsupported URI: " + uri);
  6. }
  7. }

4. Plutôt que d'accéder directement à une base de données SQLite, vous utiliserez la base de données de pièces créée au chapitre 9 pour effectuer des recherches. Confirmez que vous pouvez y accéder via le gestionnaire onCreate et renvoyez true:

  1. @Override
  2. public boolean onCreate() {
  3. EarthquakeDatabaseAccessor.getInstance(getContext().getApplicationContext());
  4. return true;
  5. }

5. Ouvrez EarthquakeDAO et ajoutez une nouvelle méthode de requête qui renvoie un curseur de suggestions de recherche basé sur une requête partielle transmise en tant que paramètre. Les colonnes de suggestion de recherche nécessitent des noms spécifiques et, malheureusement, il n’est actuellement pas possible d’utiliser des constantes statiques ou des paramètres passés lors de la définition des alias de colonnes. Bien que cela ne soit pas idéal, codez en dur les constantes de chaîne requises. Vous voudrez peut-être prendre une douche après:

  1. @Query("SELECT mId as _id, " + "mDetails as suggest_text_1, " + "mId as suggest_intent_data_id " + "FROM earthquake " + == "WHERE mDetails LIKE :query " + "ORDER BY mdate DESC")
  2. public Cursor generateSearchSuggestions(String query);

6. Toujours dans l'objet d'accès aux données Earthquake, ajoutez une autre méthode de requête incluant un paramètre query String. Cette méthode renvoie des résultats de recherche complets sous forme d'objet Live Data contenant une liste de séismes correspondant à la requête: 

  1. @Query("SELECT * " + "FROM earthquake " + "WHERE mDetails LIKE :query " + "ORDER BY mdate DESC")
  2. public LiveData<List<Earthquake>> searchEarthquakes(String query);

7. Retournez au fournisseur de contenu et implémentez le stub de la méthode de requête. Vérifiez si l'URI reçu est sous la forme d'une demande de suggestions de recherche et, le cas échéant, interrogez la base de données Room à l'aide de la requête partielle en cours:

  1. @Nullable @Override
  2. public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
  3. @Nullable String selection,
  4. @Nullable String[] selectionArgs,
  5. @Nullable String sortOrder) {
  6. if (uriMatcher.match(uri) == SEARCH_SUGGESTIONS) {
  7. String searchQuery = "%" + uri.getLastPathSegment() + "%";
  8. EarthquakeDAO earthquakeDAO = EarthquakeDatabaseAccessor.getInstance(getContext().getApplicationContext()).earthquakeDAO();
  9. Cursor c = earthquakeDAO.generateSearchSuggestions(searchQuery);
  10. // Return a cursor of search suggestions.
  11. return c;
  12. }
  13. return null;
  14. }
  1. Maintenant qu’il est terminé, ajoutez le fournisseur de contenu au manifeste. Notez que ce fournisseur de contenu est très limité. elle n'inclut pas la possibilité d'insérer, de supprimer ou de mettre à jour des enregistrements - et ne prend pas en charge les requêtes autres que les suggestions de recherche:
  2. Ouvrez le fichier de ressources strings.xml (dans le dossier res / values) et ajoutez de nouvelles ressources de chaîne décrivant l'étiquette de recherche Earthquake et l'indicateur de saisie de texte:
  1. < resources >
  2. [... Existing String resource values ...]
  3. <string name="search_label">Search</string>
  4. <string name="search_hint">Search for earthquakes...</string> < /resources >

10. Créez un nouveau fichier searchable.xml dans le dossier res / xml qui définit les métadonnées de votre fournisseur de recherche parasite. Utilisez la chaîne search_hint de l'étape 9 comme valeur de l'indice et utilisez la ressource chaîne app_name comme valeur d'étiquette. Notez que la valeur de l'étiquette doit être identique à celle de l'étiquette d'application spécifiée dans le manifeste. Définissez également l’autorité à utiliser pour générer des suggestions de recherche sur l’autorité du fournisseur de recherche parasite et configurez les attributs searchSuggestIntentAction et searchSuggestIntentData:

  1. < ?xml version="1.0" encoding="utf-8"? >
  2. < searchable
  3. xmlns:android="http://schemas.android.com/apk/res/android"android:label="@string/app_name"android:hint="@string/search_hint"android:searchSuggestAuthority="com.professionalandroid.provider.earthquake"android:searchSuggestIntentAction="android.intent.action.VIEW"android:searchSuggestIntentData="content://com.professionalandroid.provider.earthquake/earthquakes">
  4. < /searchable >

 11. Créez maintenant une nouvelle EarthquakeSearchResultActivity vide qui étend AppCompatActivity:

  1. public class EarthquakeSearchResultActivity
  2. extends AppCompatActivity {
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_earthquake_search_result);
  7. }
  8. }

12. La liste des résultats de la recherche de tremblement de terre sera affichée à l'aide d'une vue Recycler qui utilise la présentation de vos éléments de liste de tremblements de terre existante et l'adaptateur de vue Recycler. Modifiez la présentation de l'activité de résultat de recherche sismique créée à l'étape 11 pour inclure une vue du recycleur:

  1. < ?xml version="1.0" encoding="utf-8"? >
  2. < android.support.v7.widget.RecyclerView
  3. xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/search_result_list"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginLeft="16dp"android:layout_marginRight="16dp" app:layoutManager="LinearLayoutManager" />

 

  1. Dans l'activité de résultat de recherche de tremblement de terre, mettez à jour le gestionnaire onCreate pour qu'il applique l'adaptateur de vue du recycleur de tremblement de terre à la vue de recycleur qui affichera les résultats de la recherche:
    1. private ArrayList<Earthquake>
    2. mEarthquakes = new ArrayList< >();
    3. private EarthquakeRecyclerViewAdapter
    4. mEarthquakeAdapter = new EarthquakeRecyclerViewAdapter(mEarthquakes);
    5. @Override
    6. protected void onCreate(Bundle savedInstanceState) {
    7. super.onCreate(savedInstanceState);
    8. setContentView(R.layout.activity_earthquake_search_result);
    9. RecyclerView recyclerView = findViewById(R.id.search_result_list);
    10. recyclerView.setLayoutManager(new LinearLayoutManager(this));
    11. recyclerView.setAdapter(mEarthquakeAdapter); }
  1. Certaines des étapes suivantes nécessitent des fonctions Lambda. Assurez-vous donc que votre projet cible Java.

14. Ouvrez votre fichier build.gradle du module d'application et vérifiez que les options de compilation de compatibilité cible et source dans le nœud Android sont définies sur 1.8.

  1. android {
  2. [... Existing Android node values ...]
  3.  
  4. compileOptions {
  5. targetCompatibility 1.8
  6. sourceCompatibility 1.8
  7.  
  8. } }

15. Revenez à l'activité Résultats de la recherche et ajoutez un nouvel observateur Live Data qui mettra à jour la liste des tremblements de terre générée par la matrice, affichée par la vue Recycleur. Créez également un nouveau Mutable Live Data qui stockera la requête de recherche actuelle et une méthode setSearchQuery qui modifiera cette requête:

  1. MutableLiveData<String> searchQuery;
  2. private void setSearchQuery(String query) {
  3. searchQuery.setValue(query); }
  4. private final Observer<List<Earthquake>>
  5. searchQueryResultObserver = updatedEarthquakes -> {
  6. // Update the UI with the updated search query results.
  7. mEarthquakes.clear();
  8. if (updatedEarthquakes != null)
  9. mEarthquakes.addAll(updatedEarthquakes);
  10. mEarthquakeAdapter.notifyDataSetChanged();
  11. };

16. Pour simplifier le processus d’application des termes de recherche mis à jour, nous pouvons utiliser Transformations .switchMap. Cette méthode modifie automatiquement les données sous-jacentes d'une donnée en direct en fonction des modifications apportées par une autre. Appliquez une mappe de commutateur qui surveille les données searchQuery Live, et chaque modification, mettez à jour une variable searchResults Live Data en interrogeant la base de données à l'aide du terme de recherche mis à jour. Utilisez ensuite l’observateur de l’étape 15 pour observer les modifications apportées aux données en direct searchResults. Enfin, extrayez la requête de recherche de l'intention qui a lancé l'activité et transmettez-la à la méthode setSearchQuery.

  1. LiveData<List<Earthquake>> searchResults;
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_earthquake_search_result);
  6. RecyclerView recyclerView = findViewById(R.id.search_result_list);
  7. recyclerView.setLayoutManager(new LinearLayoutManager(this));
  8. recyclerView.setAdapter(mEarthquakeAdapter);
  9. // Initialize the search query Live Data.
  10. searchQuery = new MutableLiveData<>();
  11. searchQuery.setValue(null);
  12. // Link the search query Live Data to the search results Live Data.
  13. // Configure Switch Map such that a change in the search query
  14. // updates the search results by querying the database.
  15. searchResults = Transformations.switchMap(searchQuery,
  16. query -> EarthquakeDatabaseAccessor.getInstance(getApplicationContext()).earthquakeDAO().searchEarthquakes("%" + query + "%"));
  17. // Observe changes to the search results Live Data.
  18. searchResults.observe(EarthquakeSearchResultActivity.this,searchQueryResultObserver);
  19. // Extract the search query term and update the search query
  20. // Live Data.
  21. String query = getIntent().getStringExtra(SearchManager.QUERY);
  22. setSearchQuery(query);
  23. }

17. Remplacez également le gestionnaire onNewIntent pour mettre à jour la requête de recherche si une nouvelle intention de requête de recherche est reçue: 

  1. @Override
  2. protected void onNewIntent(Intent intent) {
  3. super.onNewIntent(intent);
  4. // If the search Activity exists, and another search
  5. // is performed, set the launch Intent to the newly
  6. // received search Intent.
  7. setIntent(intent);
  8. // Extract the search query and update the searchQuery Live Data.
  9. String query = getIntent().getStringExtra(SearchManager.QUERY);
  10. setSearchQuery(query); }

18. Ouvrez le manifeste de l'application et modifiez l'élément EarthquakeSearchResultActivity, en rendant son mode de lancement singleTop et en ajoutant un filtre d'intention pour l'action SEARCH. Vous devrez également ajouter une balise de métadonnées qui spécifie la ressource XML interrogeable créée à l'étape 10:

  1. <activity
  2. android:name=".EarthquakeSearchResultActivity"
  3. android:launchMode="singleTop">
  4. <intent-filter>
  5. <action android:name="android.intent.action.SEARCH" />
  6. </intent-filter>
  7. <meta-data
  8. android:name="android.app.searchable"android:resource="@xml/searchable"
  9. />
  10. < /activity >

19. Toujours dans le manifeste, ajoutez une nouvelle balise de métadonnées à l'activité principale de tremblement de terre pour spécifier l'activité de résultats de recherche de tremblement de terre en tant que fournisseur de recherche par défaut:

  1. < activity android:name=".EarthquakeMainActivity" >
  2. <intent-filter>
  3. <action android:name="android.intent.action.MAIN"/>
  4. <category android:name="android.intent.category.LAUNCHER"/>
  5. </intent-filter>
  6. <meta-data
  7. android:name="android.app.default_searchable"android:value=".EarthquakeSearchResultActivity"
  8. />
  9. < /activity >

20. Ajoutez maintenant une vue de recherche à la barre d’application de l’activité principale du séisme en tant que bouton d’action.

Créez une nouvelle ressource options_menu.xml dans le dossier res / menu, qui comprend un élément de menu permettant d'afficher les paramètres, ainsi qu'une nouvelle vue de recherche:

  1. < ?xml version="1.0" encoding="utf-8"? >
  2. < menu xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android">
  3. <item android:id="@+id/settings_menu_item"android:title="Settings" />
  4. <item android:id="@+id/search_view"android:title="@string/search_label"app:showAsAction="collapseActionView|ifRoom"app:actionViewClass="android.support.v7.widget.SearchView" />
  5. < /menu >

21. Revenez à l'activité principale du séisme et modifiez le gestionnaire onCreateOptionsMenu pour gonfler la nouvelle définition de menu XML avant de connecter la vue de recherche à votre définition pouvant faire l'objet d'une recherche:

  1. @Override
  2. public boolean onCreateOptionsMenu(Menu menu) {
  3. super.onCreateOptionsMenu(menu);
  4. // Inflate the options menu from XML
  5. MenuInflater inflater = getMenuInflater();
  6. inflater.inflate(R.menu.options_menu, menu);
  7.  
  8. // Use the Search Manager to find the SearchableInfo related
  9. // to the Search Result Activity.SearchManager searchManager =(SearchManager) getSystemService(Context.SEARCH_SERVICE);
  10. SearchableInfo searchableInfo = searchManager.getSearchableInfo( new ComponentName(getApplicationContext(),EarthquakeSearchResultActivity.class));
  11. SearchView searchView =(SearchView)menu.findItem(R.id.search_view).getActionView();
  12. searchView.setSearchableInfo(searchableInfo);
  13. searchView.setIconifiedByDefault(false);
  14. return true; }

22. Modifiez le gestionnaire onOptionsItemSelected pour utiliser l'identificateur d'élément de menu à partir de la définition XML créée à l'étape 20:

  1. public boolean onOptionsItemSelected(MenuItem item) {
  2. super.onOptionsItemSelected(item);
  3. switch (item.getItemId()) {
  4. case R.id.settings_menu_item:
  5. Intent intent = new Intent(this, PreferencesActivity.class);
  6. startActivityForResult(intent, SHOW_PREFERENCES);
  7. return true;
  8. }
  9. return false; }
  10.  

23. Si vous démarrez l’application, vous pouvez maintenant lancer une recherche en appuyant sur l’action «Rechercher».

bouton de la barre et la saisie d'une requête. Enfin, modifiez la requête des résultats de la recherche pour traiter le cas où les utilisateurs sélectionnent une suggestion de recherche. Pour l'instant, vous afficherez le résultat de la recherche comme si l'utilisateur avait complété la chaîne de recherche complète. Tout d’abord, revenez à la DAO Tremblement de terre et ajoutez une nouvelle méthode de requête getEarthquake qui prend un ID unique pour séisme et renvoie une donnée en direct contenant le séisme correspondant:

  1. @Query("SELECT * " + "FROM earthquake " + "WHERE mId = :id " + "LIMIT 1")
  2.  
  3. public LiveData<Earthquake> getEarthquake(String id);

24. Ensuite, dans l'activité Résultat de recherche de tremblement de terre, ajoutez une nouvelle variable selectedSearchSuggestionId Mutable Live Data qui stockera l'ID de la suggestion de recherche sélectionnée. Créez une méthode setSelectedSearchSuggestion qui modifiera les données Live SearchSuggestionId sélectionnées en fonction de l'ID de séisme extrait d'un fournisseur de contenu Uri, puis créez un observateur qui définira le terme de requête de recherche à l'aide des détails extraits de la suggestion de recherche sélectionnée: 

  1. MutableLiveData<String>
  2. selectedSearchSuggestionId;
  3. private void setSelectedSearchSuggestion(Uri dataString) {
  4. String id = dataString.getPathSegments().get(1);
  5. selectedSearchSuggestionId.setValue(id); }
  6. final Observer<Earthquake>
  7. selectedSearchSuggestionObserver = selectedSearchSuggestion -> {
  8. // Update the search query to match the selected search suggestion.
  9. if (selectedSearchSuggestion != null) {
  10. setSearchQuery(selectedSearchSuggestion.getDetails());
  11. }
  12. };

25. Modifiez le gestionnaire onCreate pour initialiser la suggestion de recherche sélectionnée Id Live Data et répétez le processus à partir de l'étape 16 pour appliquer une mappe de commutateur. Celui-ci doit surveiller les données en direct selectedSearchSuggestionId et mettre à jour la variable selectedSearchSuggestion Live Data en interrogeant la base de données à l'aide de l'ID de la suggestion sélectionnée. Recherchez également l'action Afficher, qui est envoyée lorsqu'un résultat de recherche suggéré est sélectionné. Dans ce cas, appliquez Observer de l'étape 24 aux données en direct selectedSearchSuggestion et utilisez setSelectedSearchSuggestion pour extraire et définir l'ID de suggestion de recherche sélectionné.

  1. LiveData<Earthquake> selectedSearchSuggestion;
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_earthquake_search_result);
  6. RecyclerView recyclerView = findViewById(R.id.search_result_list); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(mEarthquakeAdapter);
  7. // Initialize the search query Live Data.
  8. searchQuery = new MutableLiveData<>();
  9. searchQuery.setValue(null);
  10. // Link the search query Live Data to the search results Live Data.
  11. // Configure Switch Map such that a change in the search query
  12. // updates the search results by querying the database.
  13. searchResults = Transformations.switchMap(searchQuery,
  14.  
  15. query -> EarthquakeDatabaseAccessor.getInstance(getApplicationContext()).earthquakeDAO().searchEarthquakes("%" + query + "%"));
  16. // Observe changes to the search results Live Data.
  17. searchResults.observe(EarthquakeSearchResultActivity.this,searchQueryResultObserver);
  18. // Initialize the selected search suggestion Id Live Data.
  19. selectedSearchSuggestionId = new MutableLiveData<>();
  20. selectedSearchSuggestionId.setValue(null);
  21. // Link the selected search suggestion ID Live Data to the
  22. // selected search suggestion Live Data.
  23. // Configure Switch Map such that a change in the ID of the
  24. // selected search suggestion, updates the Live Data that
  25. // returns the corresponding Earthquake by querying the database.
  26. selectedSearchSuggestion = Transformations.switchMap(selectedSearchSuggestionId,
  27. id -> EarthquakeDatabaseAccessor.getInstance(getApplicationContext()).earthquakeDAO().getEarthquake(id));
  28. // If the Activity was launched by a search suggestion
  29. if (Intent.ACTION_VIEW.equals(getIntent().getAction())) {
  30. selectedSearchSuggestion.observe(this,selectedSearchSuggestionObserver);
  31. setSelectedSearchSuggestion(getIntent().getData());
  32. } else {
  33. // If the Activity was launched from a search query.
  34. String query = getIntent().getStringExtra(SearchManager.QUERY);
  35. setSearchQuery(query);
  36. }
  37. }

26. Enfin, mettez à jour le gestionnaire onNewIntent pour qu'il vérifie également l'action View afin de mettre à jour la suggestion de recherche sélectionnée ou la requête de recherche, selon le cas:

  1. @Override
  2. protected void onNewIntent(Intent intent) {
  3. super.onNewIntent(intent);
  4. // If the search Activity exists, and another search
  5. // is performed, set the launch Intent to the newly
  6. // received search Intent and perform a new search.
  7. setIntent(intent);
  8. if (Intent.ACTION_VIEW.equals(getIntent().getAction())) {
  9. // Update the selected search suggestion Id.
  10. setSelectedSearchSuggestion(getIntent().getData());
  11. } else {
  12. // Extract the search query and update the searchQuery Live Data.
  13. String query = getIntent().getStringExtra(SearchManager.QUERY); setSearchQuery(query);
  14. }
  15. }