搜尋此網誌

顯示具有 SearchView 標籤的文章。 顯示所有文章
顯示具有 SearchView 標籤的文章。 顯示所有文章

2015年10月10日 星期六

【Android】實作RecyclerView + ToolBar + SearchView

compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'com.android.support:recyclerview-v7:23.1.0'

res/menu:

在ToolBar新增一個搜尋圖案的按鈕
<item
        android:id="@+id/search"
        android:icon="@drawable/ic_search_black_24dp"
        android:title="Search"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="collapseActionView|ifRoom" />

res/layout:

RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"/>

</FrameLayout>

MainLayout:
content_main指的就是recyclerview
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.example.searchview.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main"/>

    <!--<include layout="@layout/FAB/">-->

</android.support.design.widget.CoordinatorLayout>

MainActivity:

onCreate
//Toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        //預設資料
        availableLocales = new ArrayList<>();
        for (Locale l : Locale.getAvailableLocales()) {
            availableLocales.add(l.getDisplayName());

        }

        //RecyclerView
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        mRecyclerView.setHasFixedSize(true);

        //線性布局
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));


        mAdapter = new LanguageAdapter(availableLocales);
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

RecyclerView布局Manager有三種:
  1. 線性佈局:mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
  2. 格狀佈局:mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));
  3. 瀑布佈局(縱向或橫向):mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, OrientationHelper.VERTICAL));  //HORIZONTAL
  • 設定view為預設動畫:mRecyclerView.setItemAnimator(new DefaultItemAnimator()); 
    • 在adapter使用以下函數,再搭配handler postdelay,可有動畫產生。
    • notifyItemChanged(int)
      notifyItemInserted(int) 
      notifyItemRemoved(int)
      notifyItemMoved(int, int)
      notifyItemRangeChanged(int, int)
      notifyItemRangeInserted(int, int)
      notifyItemRangeRemoved(int, int)
      
    • int為position位址

Adapter & ViewHolder:

private class LanguageAdapter extends RecyclerView.Adapter<ViewHolder> {

        private List<String> mLocales;

        private LanguageAdapter(List<String> mLocales) {
            this.mLocales = mLocales;
            notifyDataSetChanged();
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            TextView tv = (TextView) LayoutInflater.from(parent.getContext())
                    .inflate(android.R.layout.simple_list_item_1, parent, false);

            return new ViewHolder(tv);
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            holder.mTextView.setText(mLocales.get(position));
        }

        @Override
        public int getItemCount() {
            return mLocales.size();
        }

    }

    private static class ViewHolder extends RecyclerView.ViewHolder {

        public TextView mTextView;

        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }
    }

Menu

onCreateOptionsMenu
設定當Search按鈕觸發後的動作,在這裡設定當,搜尋列輸入文字時,立刻判斷預設資料是否包含收尋文字,並透過adapter作即時更新→notifyDatachanged()。
getMenuInflater().inflate(R.menu.menu_main, menu);

        final SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                mRecyclerView.setAdapter(new LanguageAdapter(OnFilter(availableLocales, newText)));
                return true;
            }
        });
        return true;

判斷輸入文字method,回傳List形態結果
//判斷輸入文字
    private List<String> OnFilter(List<String> filterLocales, String text) {
        String search = text.toLowerCase();

        final List<String> filtered = new ArrayList<>();

        for (String info : filterLocales) {
            final String localeName = info.toLowerCase();
            if (localeName.contains(search)) {
                filtered.add(localeName);
            }
        }
        return filtered;
    }

結果示意圖

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個半小時在寫這篇,累~