【Android】SearchViewを使って検索画面を実装する


## 1. はじめに

Qittaroというアプリを作っているときに検索画面の実装が必要になりました。

よくある検索ワードを入力してもらって、その結果を表示するという流れのアプリです。

最初は**Fragment**の中に`EditText`を入れて、`setOnEditorActionListener()`でEnterを押されたら...という流れの処理を作りました。

こんな感じっす。

```java:
EditText editText = (EditText)this.findViewById(R.id.edit_text);
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
	@Override
	public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
		if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
			// ここに処理
		}
	}
});
```

みたいになやつです。

しかし、検索ワードの入力が検索結果一覧のところに一緒にあるのも変だなと思っていたら、TwitterのアプリでActionBarのところに検索ワードを入力できるのを発見して探してみました。

そして無事実装できたので、この実装を紹介したいと思います。

## 2. SearchViewの実装

探し当てたのは**android.widget.SearchView**というものでした。

[SearchView | Android Developers](http://developer.android.com/reference/android/widget/SearchView.html)

特に難しいことはなかったので、実装から入力ワードの摘出までを紹介します。

### 2.1. SupportLibrary

いきなりですが、`android.widget.SearchView`は`API Level 11`以上となっています。

Qittaroはandroid version 2.3以上を対象にしているので、このままでは使えませんが、`SupportLibrary`があったのでそちらを使いました。

しかし、実装はそこまで変わらないので、どちらでも簡単に導入できます。

### 2.2. ソースコード

ということで、ソースコードを御覧ください。

```java:SearchFragment.java
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.Menu;
import android.view.ViewGroup;
 
 
public class SearchFragment extends Fragment {
    
    private static final String TAG = SearchFragment.class.getSimpleName();
    private final SearchFragment self = this;
    
    private SearchView searchView;
    private String searchWord;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // FragmentでMenuを表示する為に必要
        this.setHasOptionsMenu(true);
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_search, container, false);
    }
    
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        // Menuの設定
        inflater.inflate(R.menu.search, menu);
        
        // app:actionViewClass="android.support.v7.widget.SearchView"のItemの取得
        MenuItem menuItem = menu.findItem(R.id.search_menu_search_view);
        
        /**
         * API level 11以上の場合はこっちを使う
         * 
         * this.searchView = (SearchView)menuItem.getActionView();
         */
        
        // ActionViewの取得
        this.searchView = (SearchView) MenuItemCompat.getActionView(menuItem);
        
        // 虫眼鏡アイコンを最初表示するかの設定
        this.searchView.setIconifiedByDefault(true);
        
        // Submitボタンを表示するかどうか
        this.searchView.setSubmitButtonEnabled(false);
        
        
        if (!this.searchWord.equals("")) {
            // TextView.setTextみたいなもの
            this.searchView.setQuery(this.searchWord, false);
        } else {
            String queryHint = self.getResources().getString(R.string.search_menu_query_hint_text);
            // placeholderみたいなもの
            this.searchView.setQueryHint(queryHint);
        }
        this.searchView.setOnQueryTextListener(self.onQueryTextListener);
    }
    
    private SearchView.OnQueryTextListener onQueryTextListener = new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String searchWord) {
            // SubmitボタンorEnterKeyを押されたら呼び出されるメソッド
            return self.setSearchWord(searchWord);
        }
 
        @Override
        public boolean onQueryTextChange(String newText) {
            // 入力される度に呼び出される
            return false;
        }
    };
    
    private boolean setSearchWord(String searchWord) {
        ActionBar actionBar = ((ActionBarActivity)this.getActivity()).getSupportActionBar();
        actionBar.setTitle(searchWord);
        actionBar.setDisplayShowTitleEnabled(true);
        if (searchWord != null && !searchWord.equals("")) {
            // searchWordがあることを確認
            this.searchWord = searchWord;
        }
        // 虫眼鏡アイコンを隠す
        this.searchView.setIconified(false);
        // SearchViewを隠す
        this.searchView.onActionViewCollapsed();
        // Focusを外す
        this.searchView.clearFocus();
        return false;
    }
}
```

```xml:search.xml
<!-- res/menu/search.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/search_menu_search_view"
        android:orderInCategory="100"
        android:title="@string/search_menu_search_text"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="always"
        />
 
</menu>
```

### 2.3. スクリーンショット

こんな感じになります。

**スマホの場合**

![ScreenShot01](http://cdn-ak.f.st-hatena.com/images/fotolife/p/programmatore/20140806/20140806222754.png)

----

**タブレットの場合**

![ScreenShot02](http://cdn-ak.f.st-hatena.com/images/fotolife/p/programmatore/20140806/20140806222734.png)

### 2.4. 注意点

特にカスタマイズをしていないので、最小限の設定になります。

今回は`Fragment`で検索ワードを取得したかったので、`Fragment`で`Menu`を実装しました。

`Activity`は特になにもしていません。

ワードにさらに検索オプションみたいなのをつけようとすると苦労すると思います。

その場合は素直に`EditText` + `Button`みたいな実装をする方がよろしいかと思います。

## 3. 終わりに

これ見つけた時はなんかすっごい嬉しかったです。

SDKでこういうのを準備していてくれたんだなって思いまして...。

使えるものは使っておこう精神で良いかと思っています。

これで素敵な検索画面を実装してくださいませ。