文章出處

先看結果:

相關的官方文檔在這里:Creating a Search Interface

Android官方提供了兩種方式:

  • 彈出一個Dialog,覆蓋當前的Activity界面
  • 在AppBar中擴展一個搜索框。

    這個是上面動態圖片展示的方式。以下介紹的是這種方式的實現。
    官方建議:如果你寫的程序是給Android 3.0 以上的設備使用,那么推薦使用AppBar的方式。

想要完成這個功能,你需要創建以下幾個文件:

  • 一個XML文件,用于配置搜索框。該文件路徑:res/xml/searchable.xml

    該文件會被系統用來創建SearchableInfo對象

  • 一個用于接收搜索關鍵詞并展示最終結果的Activity

  • 一個內容提供器,用于提供搜索建議

分為兩部分寫。先完成搜索功能,再添加提供搜索建議的功能。

第一部分:基本的搜索功能

這個部分完成五個文件的創建或修改:

  • MainActivity.java
    配置AppBar
  • SearchableActivity.java
    根據Intent的Action,顯示intent的內容
  • res/xml/searchable.xml
    配置搜索框
  • res/menu/options_menu.xml
    添加搜索框及配置AppBar
  • AndroidManifest.xml
    配置SearchableActivity,使其接收ACTION_SEARCH的Intent

該版本的完整代碼:SearchWidgetInAppBar - 完成基本的功能

searchable.xml

初始的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"
    >
</searchable>

之后添加“搜索建議”功能的時候,還需要對它進行修改。

展示結果的Activity

當用戶執行一個搜索的時候,系統會啟動該Activity,并且傳入搜索的詞匯。這個詞匯包含在Intent中,并且標記為ACTION_SEARCH動作。

現在創建一個簡單地包含TextView的Activity就行了。這里將其命名為 SearchableActivity。

打開AndroidManifest.xml對該Activity進行配置:

<application ... >
    <activity android:name=".SearchableActivity" >
        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>
        <meta-data android:name="android.app.searchable"
                   android:resource="@xml/searchable"/>
    </activity>
    ...
</application>

由于intent-filter的設置,當接收到標記為ACTION_SEARCH的動作時,會啟動該Activity。

SearchableActivity.java

public class SearchableActivity extends AppCompatActivity {

    TextView mTvWord = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_searchable);

        mTvWord = (TextView) findViewById(R.id.tv_word);

        Intent intent = getIntent();
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            String text = getString(R.string.notice) + query;
            mTvWord.setText(text);
        }
    }
}

作為示例,只展示要查詢的單詞是什么就可以了。

為了讓其他Activity可以打開該Activity,在AndroidManifest.xml繼續設置:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <application ...>
        ...
        <meta-data
            android:name="android.app.default_searchable"
            android:value=".SearchableActivity"/>
    </application>

</manifest>

AppBar的設置

添加一個搜索按鈕。

res/menu/options_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_search_white_24dp"
        android:title="@string/action_search"
        app:showAsAction="ifRoom|collapseActionView"
        app:actionViewClass="android.support.v7.widget.SearchView"/>

    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never"/>
</menu>

設置的圖標可以到Material icons下載。將解壓后Android文件夾里面的所有文件復制到res/文件夾底下就行了。

app:actionViewClass="android.support.v7.widget.SearchView"

如果不設置這項,會導致錯誤。下面會提到。

collapseActionView是為了可以展開搜索框。

MainActivity.java

public class MainActivity extends AppCompatActivity {
    
    ...
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.options_menu, menu);

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();

        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false);
        searchView.setSubmitButtonEnabled(true);    // 顯示“開始搜索”的按鈕
        searchView.setQueryRefinementEnabled(true); // 提示內容右邊提供一個將提示內容放到搜索框的按鈕
        return true;
    }

}

到目前為止的效果

該版本的完整代碼:SearchWidgetInAppBar - 完成基本的功能

第二部分:添加搜索建議

官方文檔:Adding Custom Suggestions

這一部分需要做的是:

  • 添加一個內容提供器(ContentProvider),為搜索建議框提供數據
  • 一張SQLite表,用于給內容提供器查詢
  • 修改searchable.xml文件,添加搜索建議的支持

該版本的完整代碼:SearchWidgetInAppBar - 完成搜索建議

數據庫

這里用ORMLite作為例子。如果想用Android自帶數據庫,可以查看官方例子:SearchableDictionary

數據表:

@DatabaseTable(tableName = "tb_def")
public class Word {
    @DatabaseField(generatedId = true, columnName = COLUMN_ID)
    private int id;
    @DatabaseField(columnName = COLUMN_WORD)
    private String word;
    @DatabaseField(columnName = COLUMN_SUGGESTION)
    private String suggestion;

    public static final String COLUMN_ID = BaseColumns._ID;
    public static final String COLUMN_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;
    public static final String COLUMN_SUGGESTION = SearchManager.SUGGEST_COLUMN_INTENT_DATA;
    
    ...
    
    public Word(int id, String word, String suggestion) {
    this.id = id;
    this.word = word;
    this.suggestion = suggestion;
    }
    
    ...
}

這里的id字段設置為BaseColumns._ID是為了讓ListView可以讀取。搜索建議是顯示在ListView上的。

word字段設置為SearchManager.SUGGEST_COLUMN_TEXT_1是將該字段作為建議顯示的文本。如果每個建議想顯示兩行數據,還有SearchManager.SUGGEST_COLUMN_TEXT_2。更多內容可以見:SuggestionTable

除此之外,還有一個字段suggestion。當你點擊搜索建議中的數據時,系統會將該字段的數據放入Intent傳送給SearchableActivity。

數據庫:

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {

    ...

    public Cursor getSuggestionWords(String word) {
        QueryBuilder<Word, Integer> qb = getWordDao().queryBuilder();
        CloseableIterator<Word> iterator = null;
        try {
            qb.distinct().where().like(Word.COLUMN_WORD, word + "%");
            iterator = getWordDao().iterator(qb.prepare());
            AndroidDatabaseResults results = (AndroidDatabaseResults) iterator.getRawResults();
            return results.getRawCursor();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (iterator != null) {
                iterator.closeQuietly();
            }
        }
        return null;
    }
    
    ...
    
}

由于ContentProvider需要Cursor作為結果,因此這里用了ORMLite作者所說的方法:Android Cursor with ORMLite to use in CursorAdapter

創建內容提供器


public class DictionaryProvider extends ContentProvider {

    public static String AUTHORITY = "com.schaepher.memorywarehouse.DictionaryProvider";

    private DatabaseHelper mDatabaseHelper = null;

    private static final int SEARCH_SUGGEST = 0;
    private static final UriMatcher mURIMatcher = buildUriMatcher();

    private static UriMatcher buildUriMatcher() {
        UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);

        return matcher;
    }

    @Override
    public boolean onCreate() {
        mDatabaseHelper = DatabaseHelper.getHelper(getContext());
        return false;
    }

    @Override
    public Cursor query(@NonNull Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {

        String query = uri.getLastPathSegment();
        int i = mURIMatcher.match(uri);
        if (i == SEARCH_SUGGEST) {
            return mDatabaseHelper.getSuggestionWords(query);
        } else {
            throw new IllegalArgumentException("Unknown Uri: " + uri);
        }
    }

    @Override
    public String getType(@NonNull Uri uri) {
        int i = mURIMatcher.match(uri);
        if (i == SEARCH_SUGGEST) {
            return SearchManager.SUGGEST_MIME_TYPE;
        } else {
            throw new IllegalArgumentException("Unknown URL " + uri);
        }
    }
    
    ...
    
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.ftd.schaepher.memorywarehouse"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application ...>
    
        ...

        <provider
            android:name=".DictionaryProvider"
            android:authorities="com.schaepher.memorywarehouse.DictionaryProvider"
            android:enabled="true"
            android:exported="false">
        </provider>

    </application>

</manifest>

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:searchSuggestAuthority="com.schaepher.memorywarehouse.DictionaryProvider"
            android:searchSuggestIntentAction="android.intent.action.VIEW">
</searchable>

當點擊搜索建議時,傳入Intent的Action是ACTION_VIEW。

SearchableActivity

SearchableActivity.java

public class SearchableActivity extends AppCompatActivity {

    TextView mTvWord = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_searchable);

        mTvWord = (TextView) findViewById(R.id.tv_word);

        Intent intent = getIntent();

        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            mTvWord.append(intent.getStringExtra(SearchManager.QUERY));
        } else if (Intent.ACTION_VIEW.equals((intent.getAction()))){
            mTvWord.append(intent.getDataString());
        } else {
            mTvWord.setText(R.string.word_not_found);
        }

    }
}

到目前為止的效果

該版本的完整代碼:SearchWidgetInAppBar - 完成搜索建議



文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()