zoukankan      html  css  js  c++  java
  • 嵌入AppBar并且带搜索建议的搜索框(Android)

    先看结果:

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


  • 相关阅读:
    Spring个人冲刺阶段总结
    开学测试代码
    《人月神话》读后感(三)
    《人月神话》读后感(二)
    《人月神话》读后感(一)
    记账本小程序7天开发记录(第七天)
    记账本小程序7天开发记录(第六天)
    记账本小程序7天开发记录(第五天)
    记账本小程序7天开发记录(第四天)
    记账本小程序7天开发记录(第三天)
  • 原文地址:https://www.cnblogs.com/schaepher/p/6389032.html
Copyright © 2011-2022 走看看