顯示廣告
隱藏 ✕
看板 KnucklesNote
作者 Knuckles (站長 那克斯)
標題 [AndroidStudio] 使用 SearchView 過濾列表資料
時間 2016-01-26 Tue. 17:46:04


新增一個搜尋看板頁,輸入每個字母時就會顯示可能的看板名稱
[圖]


加上搜尋輸入框 SearchView

在頁面佈局xml檔,加上 SearchView 與 ListView
    <SearchView android:id="@+id/searchview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <ListView android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="#555"
        android:dividerHeight="1px"/>


在頁面的類別程式檔

加上這幾個成員變數
    ListView mListView;
    ArrayAdapter mSearchAdapter;
    ArrayList<String> mSearchList = new ArrayList<>();
    boolean mIsSearch = false;
mSearchList 用來儲存所有看板名稱的字串陣列
mIsSearch 用來記錄 ListView 是否已載入 Adapter


新增一個成員函式 loadData()
    private void loadData(){
        String urlString = "http://disp.cc/api/get.php?act=bSearchList";

        AsyncHttpClient client = new AsyncHttpClient();
        client.get(urlString, new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                JSONArray list = response.optJSONArray("list");
                JSONObject board;
                for(int i=0; i<list.length(); i++){
                    board = list.optJSONObject(i);
                    mSearchList.add(board.optString("name") + " " + board.optString("title"));
                }
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, Throwable e, JSONObject error) {
                Toast.makeText(getApplicationContext(),
                        "Error: " + statusCode + " " + e.getMessage(),
                        Toast.LENGTH_LONG).show();
            }
        });
    }
使用 Asynchronous Http Client 下載所有看板的資料
並轉為字串陣列存在 mSearchList


在成員函式 onCreate() 加上
        SearchView searchView = (SearchView) findViewById(R.id.searchview);
        searchView.setOnQueryTextListener(this);
        searchView.setIconifiedByDefault(false); //是否要點選搜尋圖示後再打開輸入框
        searchView.setFocusable(false);
        searchView.requestFocusFromTouch();      //要點選後才會開啟鍵盤輸入
        searchView.setSubmitButtonEnabled(false);//輸入框後是否要加上送出的按鈕
        searchView.setQueryHint("輸入看板名稱"); //輸入框沒有值時要顯示的提示文字

        mListView = (ListView) findViewById(R.id.listview);
        mSearchAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mSearchList);

        loadData();

依編輯器的提示,在類別的 implements 加上 SearchView.OnQueryTextListener
並加上成員函式 onQueryTextChange() 與 onQueryTextSubmit()
當搜尋框內的文字改變時,會執行 onQueryTextChange()
輸入完按下確定時,會執行 onQueryTextSubmit()

因為資料是使用陣列,所以可以用 ArrayAdapter 將資料轉為 Adapter
不用自己再寫一個繼承 BaseAdapter 的類別

建立 ArrayAdapter 時使用的 android.R.layout.simple_list_item_1
為內建的 xml 資源,用這個就不用自己建 xml 檔

ListView 先不要載入 Adapter,等搜尋框有輸入文字時再載入

修改兩個新增的成員函式為
    @Override
    public boolean onQueryTextChange(String newText) {
        if(!mIsSearch && newText.length()!=0) { //搜尋框有值時
            mListView.setAdapter(mSearchAdapter);
            mIsSearch = true;
        }else if(mIsSearch && newText.length()==0){ //搜尋框是空的時
            mListView.setAdapter(null);
            mIsSearch = false;
        }
        if(mIsSearch) { //過濾Adapter的內容
            Filter filter = mSearchAdapter.getFilter();
            filter.filter(newText);
        }
        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        Toast.makeText(this, "輸入的是:" + query, Toast.LENGTH_SHORT).show();
        return true;
    }

在搜尋框輸入文字時,先用 mIsSearch 確認是否已載入Adapter
若還未載入,且有輸入文字時,才載入 Adapter
若已載入 Adapter,但輸入文字被刪除時,使用
mListView.setAdapter(null); 來移除 Adapter

當搜尋框有輸入文字時,使用 ArrayAdapter 提供的 getFilter 來過濾 Adapter 的內容


設定列表的點擊事件

在 onCreate() 加上
        mListView.setOnItemClickListener(this);

依編輯器提示加上 implements AdapterView.OnItemClickListener
以及成員函式 onItemClick()

修改成員函式 onItemClick() 為
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        //從ArrayAdapter中用getItem取出第position項的資料
        String itemString = (String) parent.getAdapter().getItem(position);
        Pattern pattern = Pattern.compile("^(\\S+) (.*)$");
        Matcher matcher = pattern.matcher(itemString);
        String boardName="", boardTitle="";
        if(matcher.find()){
            boardName = matcher.group(1);
            boardTitle = matcher.group(2);
        }

        Intent intent = new Intent(this, TextListActivity.class);
        intent.putExtra("boardName", boardName);
        intent.putExtra("boardTitle", boardTitle);
        startActivity(intent);
    }

因為是 ArrayAdapter,使用 getItem() 取得的資料只有一個 String
這個 String 之前是用 boardName + " " + boardTitle 存進去的
所以要用正規表示式的方法,再把他還原成兩個字串

正規表示式 Pattern.compile("^(\\S+) (.*)$");
^(\\S+) 的意思是要將開頭為一個以上非空白字元的字串存進 Group 1
接著空一格
(.*)$ 代表將之後一直到結尾的任意字串存進 Group 2

接著用 pattern.matcher(itemString); 輸入要辨識的文字
然後用 matcher.find() 判斷是否有辨識成功
取出 Group 裡的字串存到 boardName, boardTitle

最後用 Intent 加入 boardName, boardTitle 後
跳至看板的 TextListActivity 頁面


修改列表的字體樣式

列表預設的字體顏色是灰色的,如果想要改成白色的話
可以複寫 ArrayAdapter 的 getView()
或是將 android.R.layout.simple_list_item_1
改為自己建立的 xml 資源檔也可以

複寫 getView() 的方法

將 mSearchAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mSearchList);
改為
        mSearchAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mSearchList) {
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View view = super.getView(position, convertView, parent);
                TextView text = (TextView) view.findViewById(android.R.id.text1);
                text.setTextColor(Color.WHITE);
                return view;
            }
        };

自建 xml 資源檔的方法

在 /res/layout 新增 Layout resource file
名稱輸入 row_boardsearch
將內容改為
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:textColor="#FFF"
    android:textSize="20sp"
    android:paddingLeft="16dp"
    android:minHeight="40dp" />
然後將  android.R.layout.simple_list_item_1
改為 R.layout.row_boardsearch


加上 HeaderView

在列表的上方加個文字說明,像這樣
[圖]


在 /res/values/strings.xml 加上
    <string name="boardSearchHeader">搜尋結果</string>

在 /res/layout 新增 Layout resource file
名稱輸入 row_boardsearchheader
將內容改為
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#555">

    <TextView android:id="@+id/header_boardsearch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="16dp"
        android:textColor="#CCC"
        android:text="@string/boardHistoryHeader"/>
</RelativeLayout>

修改類別的程式檔
在 onCreate() 裡,這行 mListView = (ListView) findViewById(R.id.listview); 下面加上
        View headerView = getLayoutInflater().inflate(R.layout.row_boardsearchheader, mListView, false);
        mListView.addHeaderView(headerView);


--
※ 作者: Knuckles 時間: 2016-01-26 17:46:04
※ 編輯: Knuckles 時間: 2017-01-16 00:00:57
※ 看板: KnucklesNote 文章推薦值: 0 目前人氣: 0 累積人氣: 2028 
分享網址: 複製 已複製
r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇