嵌入AppBar并且带搜索建议的搜索框(Android)

来源:互联网 发布:oracle mysql 区别 编辑:程序博客网 时间:2024/06/14 00:09

先看结果:

相关的官方文档在这里: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 - 完成搜索建议




0 0
原创粉丝点击