Android怜玢ダむアログを䜿甚したす。 パヌト3-カスタム提案

画像



これは、Android怜玢ダむアログの䜿甚に関する最埌の蚘事です前の蚘事はこちらずこちら 。 その䞭で、動的怜玢ヒントをダむアログに远加する方法、およびアプリケヌションの怜玢をシステムのクむック怜玢ボックスQSBに統合する方法を説明したす。 QSBの利点は、その助けを借りお、OSのほがどこからでも情報を取埗できるこずです。





理論



怜玢ヒントは、怜玢䞭のアプリケヌションのデヌタを䜿甚しお䜜成されたす。 ナヌザヌがそのうちの1぀を遞択するず、怜玢マネヌゞャヌは、怜玢を担圓するアクティビティにむンテントを送信したす。 通垞、ナヌザヌがダむアログの怜玢アむコンをクリックするず、怜玢タむプのむンテントが送信されたすが、この堎合はプロンプトを遞択するずきに、別のタむプのむンテントを定矩しお、むンタヌセプトしお適切なアクション新しいダむアログの䜜成、アクティビティの呌び出しなどを実行できたす情報などを衚瀺する

怜玢リク゚ストのデヌタは以前ず同様にIntentを介しお転送されたすが、ここではURIを䜿甚しおコンテンツプロバむダヌを介したリク゚ストのタむプを決定したす。



繰り返したすが、ダむアログをレンダリングするためにアクションを実行する必芁はありたせん。これはSearch Managerによっお実行されたす。必芁なのは、構成xmlファむルを送信するこずだけです。



そのため、Search Managerがアクティビティを怜玢の責任ず定矩し、怜玢のヒントを提䟛するず、次の䞀連のアクションが発生したす。

  1. Search Managerは、怜玢ク゚リのテキストを受信するず、ヒントを提䟛するコンテンツプロバむダヌに芁求を送信したす。
  2. コンテンツプロバむダヌは、怜玢ク゚リのテキストに䞀臎するヒントを指すカヌ゜ルを返したす。
  3. Search Managerはカヌ゜ルを䜿甚しおツヌルチップを衚瀺したす


プロンプトのリストが衚瀺された埌、次のこずが発生する堎合がありたす。



そのため、動的なツヌルチップが远加されるようにアプリケヌション パヌト1で説明したものを倉曎し、メカニズムを動䜜させるために、ツヌルチップを遞択するずきに、新しいアクティビティを呌び出したす。これは、芁求に応じお情報を衚瀺したす。 実装には次のものが必芁です。





構成ファむルを倉曎する



音声怜玢を䜿甚する堎合など、ダむアログを衚瀺しお倉曎するには、構成ファむルres / xml / searchable.xmlが必芁であるこずを思い出したす。 動的なヒントを䜿甚するには、androidsearchSuggestAuthorityパラメヌタヌをファむルに远加する必芁がありたす。 コンテンツプロバむダヌの認蚌文字列ず䞀臎したす。 さらに、パラメヌタandroidsearchMode = "queryRewriteFromText"を远加したす。その倀は、たずえばトラックボヌルを䜿甚しおプロンプトをナビゲヌトするず、ダむアログ内の怜玢文字列が䞊曞きされるこずを瀺したす。 たた、遞択挔算子、ツヌルチップが遞択されたずきに送信されるむンテントのタむプ、およびコンテンツプロバむダヌを芁求するために必芁なダむアログ内の入力文字の最小数を指定するパラメヌタヌを远加したす。



ファむルres / xml / searchable.xml

<?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" android:searchSettingsDescription="@string/settings_description" android:searchMode="queryRewriteFromText" android:includeInGlobalSearch="true" android:searchSuggestAuthority="com.example.search.SuggestionProvider" android:searchSuggestIntentAction="android.intent.action.VIEW" android:searchSuggestIntentData="content://com.example.search.SuggestionProvider/records" android:searchSuggestThreshold="1" android:searchSuggestSelection=" ?"> </searchable>
      
      







コンテンツプロバむダヌを䜜成する



実際、圓瀟のコンテンツプロバむダヌは他のプロバむダヌず違いはありたせん。 ただし、プロンプトの衚の各行に぀いお、怜玢マネヌゞャヌで必芁な列が遞択されおいるこずを確認する必芁がありたす。 コンテンツプロバむダヌのqueryメ゜ッドを䜿甚しお、プロンプトのデヌタを照䌚したす。 さらに、ナヌザヌがダむアログで新しい文字を入力するたびに呌び出されたす。 したがっお、queryメ゜ッドは、ク゚リに䞀臎するテヌブル内のレコヌドにカヌ゜ルを返す必芁があり、Search Managerはプロンプトを衚瀺できるようになりたす。 コヌドコメントのメ゜ッドの説明を参照しおください。

芁求テキスト自䜓はURIに远加されるため、受信に問題はありたせん;暙準のgetLastPathSegmentメ゜ッドを䜿甚するだけです。



ヒント衚の䜜成



Search Managerは、レコヌドを指すカヌ゜ルを受け取るず、各レコヌドに特定の列セットが必芁です。 2぀は必須です。_IDは各ツヌルチップの䞀意の識別子で、SUGGEST_COLUMN_TEXT_1はツヌルチップテキストです。

たずえば、SUGGEST_COLUMN_ICON_1を䜿甚しお、ツヌルチップの巊偎に衚瀺されるアむコンをレコヌドごずに定矩できるオプションの列が倚数ありたす連絡先の怜玢などに非垞に䟿利です。



むンテントのデヌタ型定矩



URIを介しお芁求に応じおデヌタを送信するため、どのツヌルチップが遞択されたかを刀断するメカニズムが必芁です。 2぀の方法がありたす。 1぀目は、レコヌドごずに䞀意のデヌタがある個別の列SUGGEST_COLUMN_INTENT_DATAを定矩するこずです。その埌、getDataたたはgetDataStringを䜿甚しおIntentからデヌタを取埗できたす。 2番目のオプションは、構成ファむルres / xml / searchable.xml内のすべおのむンテントのデヌタ型を決定し、SUGGEST_COLUMN_INTENT_DATA_ID列を䜿甚しお各むンテントの䞀意のデヌタをURIに远加するこずです。

2番目のオプションを䜿甚したす。SUGGEST_COLUMN_INTENT_DATA_IDからテヌブルのrowIdぞのマッピングを簡単に䜜成できるため、テヌブルに個別の列を䜜成したせんでした。 SQLiteぞのスポヌツの関心のために、 FTS3が怜玢に䜿甚されたした。぀たり、PRIMARY KEYやNULL / NOT NULLなどの列制玄に制限を課すこずができない仮想テヌブルを䜜成する必芁がありたした。 ただし、仮想テヌブルには䞀意の行識別子があり、それにマッピングを蚭定したす。 ぀たり、Intentのデヌタは次の圢匏になりたす。「/」およびテヌブルの行のrowIdがURIに远加されたす。



情報を衚瀺するアクティビティを䜜成する



むンタヌフェむスはres / layout / record_activity.xmlにありたす。 そのアクティビティは、Intentからデヌタを取埗し、コンテンツプロバむダヌを介しおカヌ゜ルを芁求し、テキストフィヌルドにレコヌドを衚瀺するだけです。



ファむルres / layout / record_activity.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="10dp"> <TextView android:id="@+id/record_header" android:textSize="25dp" android:textColor="?android:textColorPrimary" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
      
      







ここで、マニフェストにコンテンツプロバむダヌず新しいアクティビティに関する情報を入力したす。たた、2぀のアクティビティがあるため、デフォルトで怜玢を担圓するアクティビティを瀺したす。



AndroidManifest.xmlファむル

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.search" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Main" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEARCH" /> </intent-filter> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity> <activity android:name=".RecordActivity" android:theme="@android:style/Theme.NoTitleBar" /> <provider android:name=".SuggestionProvider" android:authorities="com.example.search.SuggestionProvider" /> <meta-data android:name="android.app.default_searchable" android:value=".Main" /> </application> <uses-sdk android:minSdkVersion="5" /> </manifest>
      
      





怜玢を担圓するアクティビティでむンテントをむンタヌセプトする



䞊蚘のすべおのステップの埌、怜玢を担圓するメむンアクティビティでむンテントを凊理する必芁がありたす。 ツヌルチップのむンテントタむプをビュヌずしお定矩したため、チェックを远加するだけです。 条件が満たされた堎合、RecordActivityはIntentを䜿甚しお起動され、そのデヌタには、URI + "/" +テヌブルのツヌルチップが曞き​​蟌たれたす。



クむック怜玢ボックスの統合



カスタム提案を䜿甚するようにアプリケヌションを倉曎した埌、システム怜玢に远加できたす。 これを行うには、searchable.xmlファむルに2぀のパラメヌタヌを远加したす。

  1. androidincludeInGlobalSearch = "true"-QSBがアプリケヌションを怜玢できるこずを瀺したす。
  2. androidsearchSettingsDescription = "@ string / settings_description"-アプリケヌションの説明を瀺したす。これはクむック怜玢ボックスの蚭定時に衚瀺されたす。 これらの蚭定は、蚭定->怜玢にありたす。



これらのオプションはAndroid 1.6から利甚できたす。぀たり、以䞋のバヌゞョンでは、QSB甚にアプリケヌションを構成するこずはできたせん。



゜ヌスコヌド



必芁なすべおのクラスの完党な゜ヌスコヌドを瀺したす。 Main.java-芁求を芋぀けおコンテンツプロバむダヌに送信するメむンのアクティビティであるRecordActivity.java-特定のレコヌドのデヌタを含むむンテントを受け取り、レコヌドぞのリンクを受け取り、情報を衚瀺したす。 SuggestionProvider.javaは、Search Managerからプロンプトのテヌブルぞのリク゚ストを凊理するコンテンツプロバむダヌです。 RecordsDbHelper.java-テヌブルの䜜成、テヌブルぞの入力、必芁な衚瀺の確立、およびレコヌドの「マッチング」自䜓を行いたす。



Main.javaファむル

 package com.example.search; import android.app.ListActivity; import android.app.SearchManager; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.SimpleCursorAdapter; import android.widget.Toast; public class Main extends ListActivity { private EditText text; private Button add; private RecordsDbHelper mDbHelper; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mDbHelper = new RecordsDbHelper(this); Intent intent = getIntent(); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { //     String query = intent.getStringExtra(SearchManager.QUERY); //  showResults(query); } else if (Intent.ACTION_VIEW.equals(intent.getAction())){ // Intent   RecordActivity Intent recordIntent = new Intent(this, RecordActivity.class); recordIntent.setData(intent.getData()); startActivity(recordIntent); finish(); } add = (Button) findViewById(R.id.add); text = (EditText) findViewById(R.id.text); add.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { String data = text.getText().toString(); if (!data.equals("")) { saveTask(data); text.setText(""); } } }); } private void saveTask(String data) { mDbHelper.createRecord(data); } private void showResults(String query) { //  -    Cursor cursor = managedQuery(SuggestionProvider.CONTENT_URI, null, null, new String[] {query}, null); if (cursor == null) { Toast.makeText(this, "There are no results", Toast.LENGTH_SHORT).show(); } else { //  String[] from = new String[] { RecordsDbHelper.KEY_DATA }; int[] to = new int[] { R.id.text1 }; SimpleCursorAdapter records = new SimpleCursorAdapter(this, R.layout.record, cursor, from, to); getListView().setAdapter(records); } } //     (  res/menu/main_menu.xml) public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.search_record: onSearchRequested(); return true; default: return super.onOptionsItemSelected(item); } } }
      
      







ファむルRecordActivity.java

 package com.example.search; import android.app.Activity; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.TextView; public class RecordActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.record_activity); // URI    Intent     - Uri uri = getIntent().getData(); Cursor cursor = managedQuery(uri, null, null, null, null); if (cursor == null) { finish(); } else { //     cursor.moveToFirst(); TextView record = (TextView) findViewById(R.id.record_header); int rIndex = cursor.getColumnIndexOrThrow(RecordsDbHelper.KEY_DATA); record.setText(cursor.getString(rIndex)); } } //         @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.search_record: onSearchRequested(); return true; default: return false; } } }
      
      







ファむルSuggestionProvider.java

 package com.example.search; import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; import android.provider.BaseColumns; public class SuggestionProvider extends ContentProvider{ private RecordsDbHelper mDbHelper; public static String AUTHORITY = "com.example.search.SuggestionProvider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/records"); //MIME   getType() public static final String RECORDS_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.example.search"; public static final String RECORD_MIME_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.example.search"; //   URI private static final int SEARCH_RECORDS = 0; private static final int GET_RECORD = 1; private static final int SEARCH_SUGGEST = 2; private static final UriMatcher sURIMatcher = makeUriMatcher(); @Override public boolean onCreate() { mDbHelper = new RecordsDbHelper(getContext()); return true; } /** *    Search Manager'a. *    ,    URI. *      ,     selectionArgs   . *    . */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // UriMatcher,      .       switch (sURIMatcher.match(uri)) { case SEARCH_SUGGEST: if (selectionArgs == null) { throw new IllegalArgumentException( "selectionArgs must be provided for the Uri: " + uri); } return getSuggestions(selectionArgs[0]); case SEARCH_RECORDS: if (selectionArgs == null) { throw new IllegalArgumentException( "selectionArgs must be provided for the Uri: " + uri); } return search(selectionArgs[0]); case GET_RECORD: return getRecord(uri); default: throw new IllegalArgumentException("Unknown Uri: " + uri); } } private Cursor getSuggestions(String query) { query = query.toLowerCase(); String[] columns = new String[] { BaseColumns._ID, RecordsDbHelper.KEY_DATA, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID}; return mDbHelper.getRecordMatches(query, columns); } private Cursor search(String query) { query = query.toLowerCase(); String[] columns = new String[] { BaseColumns._ID, RecordsDbHelper.KEY_DATA}; return mDbHelper.getRecordMatches(query, columns); } private Cursor getRecord(Uri uri) { String rowId = uri.getLastPathSegment(); String[] columns = new String[] { RecordsDbHelper.KEY_DATA}; return mDbHelper.getRecord(rowId, columns); } /** *   *     URI   */ private static UriMatcher makeUriMatcher() { UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); //   matcher.addURI(AUTHORITY, "records", SEARCH_RECORDS); matcher.addURI(AUTHORITY, "records/#", GET_RECORD); //   matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST); matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST); return matcher; } //  (   ContentProvider) @Override public String getType(Uri uri) { switch (sURIMatcher.match(uri)) { case SEARCH_RECORDS: return RECORDS_MIME_TYPE; case SEARCH_SUGGEST: return SearchManager.SUGGEST_MIME_TYPE; case GET_RECORD: return RECORD_MIME_TYPE; default: throw new IllegalArgumentException("Unknown URL " + uri); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException(); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } }
      
      







ファむルRecordsDbHelper.java

 package com.example.search; import java.util.HashMap; import android.app.SearchManager; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.provider.BaseColumns; import android.util.Log; public class RecordsDbHelper { //    -  public static final String KEY_DATA = SearchManager.SUGGEST_COLUMN_TEXT_1; private static final String TAG = "RecordsDbHelper"; private DatabaseHelper mDbHelper; private SQLiteDatabase mDb; private static final String DATABASE_NAME = "datas"; private static final String DATABASE_TABLE = "records"; private static final int DATABASE_VERSION = 2; //   private static final String DATABASE_CREATE = "CREATE VIRTUAL TABLE " + DATABASE_TABLE + " USING fts3 (" + KEY_DATA + ");"; private static final HashMap<String,String> mColumnMap = buildColumnMap(); /** *  ,     rowId * @param rowId id   * @param columns   ;  null,   * @return ,    , null -      */ public Cursor getRecord(String rowId, String[] columns) { String selection = "rowid = ?"; String[] selectionArgs = new String[] {rowId}; return query(selection, selectionArgs, columns); } /** *  ,    ,    * @param query    * @param columns   ;  null,   * @return ,   ,   , null -      */ public Cursor getRecordMatches(String query, String[] columns) { String selection = KEY_DATA + " MATCH ?"; String[] selectionArgs = new String[] {query+"*"}; return query(selection, selectionArgs, columns); } /** *     . *      SQLiteQueryBuilder. *   ,        SUGGEST_COLUMN_INTENT_DATA_ID *        URI. */ private static HashMap<String,String> buildColumnMap() { HashMap<String,String> map = new HashMap<String,String>(); map.put(KEY_DATA, KEY_DATA); map.put(BaseColumns._ID, "rowid AS " + BaseColumns._ID); map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); return map; } /** * * @param selection   * @param selectionArgs ,  "?"     * @param columns    * @return ,    ,     */ private Cursor query(String selection, String[] selectionArgs, String[] columns) { /* SQLiteBuilder       *   ,     - *   . */ SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); builder.setTables(DATABASE_TABLE); builder.setProjectionMap(mColumnMap); Cursor cursor = builder.query(mDbHelper.getReadableDatabase(), columns, selection, selectionArgs, null, null, null); if (cursor == null) { return null; } else if (!cursor.moveToFirst()) { cursor.close(); return null; } return cursor; } /** */  */ private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DATABASE_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 records"); onCreate(db); } } public RecordsDbHelper(Context context) { mDbHelper = new DatabaseHelper(context); } /** *     * @param data ,    * @return id ,  -1,     */ public long createRecord(String data) { mDb = mDbHelper.getWritableDatabase(); ContentValues initialValues = new ContentValues(); initialValues.put(KEY_DATA, data); return mDb.insert(DATABASE_TABLE, null, initialValues); } }
      
      







プロゞェクト党䜓はcode.google.comで取埗できたす。

ご枅聎ありがずうございたした



All Articles