搜尋此網誌

2015年8月21日 星期五

【Android】實作SearchView / Search widget

紀錄一下
http://www.mysamplecode.com/2012/07/android-listview-custom-layout-filter.html
http://stackoverflow.com/questions/17720481/how-could-i-filter-the-listview-using-baseadapter
http://stackoverflow.com/questions/5713653/how-to-get-the-events-of-searchview-in-android
http://www.tutorialsbuzz.com/2014/08/filter-custom-listviewbaseadapter.html
--------------------------------------------------------

2015/08/21 22:54 整理
http://www.mysamplecode.com/2012/11/android-expandablelistview-search.html
http://www.cnblogs.com/over140/archive/2010/11/25/1887892.html
看得不少資料  感覺大部分都是需要 extends Filter來實作過濾listview的search功能,但感覺好像太複雜了,對我來說...
今天試了另一種方法,code比較簡潔,原理和上面的都差不多,也是想要的結果。就分享大概的步驟(http://developer.android.com/training/search/setup.html),如下:

res/menu/menu.xml
新增search item
<item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_search"
        android:imeOptions="actionSearch"
        android:inputType="textCapWords"
        android:title="search"
        android:orderInCategory="90"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="ifRoom|collapseActionView"/>
其中
  • orderInCategory="90" 
    • 因為仍有其他menu item,排在Search右邊,所以先設90(由左至右;數字由小至大)
  • ifRoom 只要actionbar有空間,item就會顯示出來。
  • collapseActionView item使用時會展開,不用時會縮起來。
MainActivity
在畫面上顯示Menu item,在onCreateOptionsMenu的function 加上
getMenuInflater().inflate(R.menu.menu_main, menu);
確保search item會出現在menu上,但這時候search還是一樣沒有功能,需要繼續定義Search View的行為。

res/xml/searchable.xml
這步蠻重要,沒加這個,害我白做工好幾回。
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
            android:label="@string/msg_search"
            android:hint="@string/msg_search_help"/>
參考網路上的解釋:
一個Searchable Configuration至少包含一個android:label,其屬性要和你的 Android manifest(Android清單)中 <application>或<activity>元素的android:label屬性具有相同的值。

AndroidMainifest.xml
<meta-data
     android:name="android.app.searchable"
     android:resource="@xml/searchable" />
定義meta-data告知應用程式可以到res/xml/searchable.xml找到他。
<intent-filter>
     <action android:name="android.intent.action.SEARCH"/>
</intent-filter>
宣告activity須接受 action_SEARCH intent

MainActivity
SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
通過調用setSearchableInfo(SearchableInfo)方法來關聯搜索配置(the searchable configuration )和SearchView:

通過調用getSearchableInfo()方法可從已創建的可搜索的XML配置文件( the searchable configuration XML file)中獲得SearchableInfo對象。 

當可搜索的配置(searchable configuration)正確地與你的SearchView 相關聯時,用戶提交一個搜索查詢後,SearchView可以通過ACTION_SEARCH intent,啟動一個 activity。現在,你需要一個可以篩選(filter)這個intent和處理搜索查詢的活動。



最後和上面網頁的例子都有些不一樣。
ViewItemAdapter

private LayoutInflater mInflater;
    ArrayList<AirSiteObject> data;
    ArrayList<AirSiteObject> oldData;

    public ViewItemAdapter(Context view, ArrayList<AirSiteObject> arrayList) {
        mInflater = LayoutInflater.from(view);
        data = arrayList;
        oldData = new ArrayList<AirSiteObject>();
        oldData.addAll(data);
    }

data 是放置 listview的layout,過濾後的結果也用它呈現。
olddata 放置原本的資料。

額外增加兩個method
public void setSearchPattern(String pattern) {
        filterListByPattern(pattern.trim());
        this.notifyDataSetChanged();
    }

    private void filterListByPattern(String searchPattern) {
        data.clear();
        for (AirSiteObject info : oldData) {
            boolean add = true;
            do {
                if (searchPattern == null || searchPattern.equals("")) {
                    break;
                }
                if (info.getSiteName().contains(searchPattern)) {
                    break;
                }
                if (info.getCounty().contains(searchPattern)) {
                    break;
                }
                add = false;
            } while (false);
            if (add) {
                data.add(info);
            }
        }
    }

註:過濾的是中文,且資料內不可能會有空格產生,所以先用trim()把空格都濾掉。
  • 當search pattern是空的或是null時,就等於呈現的是原始資料。
  • 當pattern過濾條件符合,就加進來。

MainActivity/OnCreateOptionMenu
增加setOnQueryTextListener,但為了美觀,把整個method都show出來。

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);

        SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                mAdapter.setSearchPattern(s);
                return true;
            }
        });
        return super.onCreateOptionsMenu(menu);
    }

其他res內的小bug改一改,應該就可以跑了。

先記錄到這邊,希望下次使用到這個功能,我才能看得懂在寫什麼。


ref: http://developer.android.com/training/search/setup.html

為了讓code版型不要跑掉,光這樣就花了1個半小時在寫這篇,累~

1 則留言:

隨手筆記 提到...

在onCrate()還要增加
listView.setTextFilterEnabled(true);

參考