前言:
最近做到這個功能,在這裡做個紀錄。以前我使用SearchBar的功能是我給後台值,後台直接給我收尋結果。
但如果使用到的收尋結果必須要前端(App端)實作呢?
這裡就教你如何前端做收尋應用。
重點程式碼:
重點都是圍繞在Filter。可以看一下官方文檔, 讓我們把畫面帶到Protected methods
在待會我們實作RecyclerView的時候繼承 Filterable 會用到getFilter() 方法,好讓我們在activity調用adapter時使用篩選。
還會再Adapter內實作一個自己的Filter,這時候會override複寫下方兩種方法。
performFiltering 執行篩選
publishResults 篩選結果
SearchView的部分,必須implements SearchView.OnQueryTextListener。
Public methods | |
---|---|
abstract boolean | onQueryTextChange(String newText)
Called when the query text is changed by the user.
|
abstract boolean | onQueryTextSubmit(String query)
Called when the user submits the query.
|
onQueryTextChange 當使用者改變searchview的字體時,call此方法。
onQueryTextSubmit 當使用者按下送出時,call此方法。
實作:
這裡提供 SearchView + ListView 的實作方法。[AndroidStudio] 使用 SearchView 過濾列表資料 —
http://disp.cc/b/11-9ded
裡面的內容清楚明瞭。
如果有問題可以在這裡問。
我實作了一個demo:https://drive.google.com/open?id=0Byk75IYx-dKXWTUya0hKaWtXX3M
但ListView近期已經被RecyclerView代替了。
在RecyclerView內的adapter並沒有filter的方法,因此我們要在Adapter內實作。
提供一個SearchView + RecyclerView的方法
作者:Lipt0n
連結:http://www.jianshu.com/p/5078c7fec29e
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
此處就是在Adapter內實作Filterable並使用的很好範例。
等等我做的東西是實踐在這個上面的東西,就不提供DEMO了。
所有的收尋都拿String當例子,簡單明瞭!
但如果我要客製化呢?變成一個item的搜尋模式該怎麼辦呢?
這邊包含可以搜尋文字。
如果每個item的文字都不一樣,是可以分開收尋的。
只要你的文字內有關鍵字,都會跑在下面的結果內。
如圖:
完整程式碼:
先來看看 今日我們會動到的地方:因為會有網路下載圖片的功能,所以先在manifest加入Gilde。
如何加入這篇有提到
http://nikeru8.blogspot.tw/2017/03/third-party-frescopicasso.html
就不再贅述。
然後你還要一張default image,供給如果Gilde還沒下載完成時的預設圖片。
http://lmgtfy.com/?q=default_image
這邊必須強調一下預設圖片和文字的重要性,如果RecyclerView讀取不到你的圖片或是文字,因為他內在的回收機制,他會去偷取其他item的圖片作為預設圖片。你也知道後果了。文不對題,或是錯位的問題就會絡繹不絕。
activity_main.xml |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" tools:context="com.hello.kaiser.searchbar.MainActivity"> <android.support.v7.widget.SearchView android:id="@+id/search_bar" android:layout_width="match_parent" android:layout_height="wrap_content" /> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
先看activity的佈局文件。
先把xml畫好畫滿。
item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/image_view" android:layout_width="100dp" android:layout_height="100dp" android:padding="5dp" /> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center" android:textSize="30dp" /> </LinearLayout>
再來先把工具寫出來,我們要塞物件的DATA
DATA.java
public class Data { private String title; private String imageUrl; // public Data(String title, String imageUrl) { // this.title = title; // this.imageUrl = imageUrl; // Log.d("ItemData", "CheckPoint imageUrl=" + imageUrl); // } public String getTitle() { return title; } public String getImageUrl() { return imageUrl; } public void setTitle(String title) { this.title = title; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } }
這裡我們要塞圖片和文字。
把我們MainActivity.java 全部補上吧。
public class MainActivity extends AppCompatActivity implements SearchView.OnQueryTextListener { //元件 private android.support.v7.widget.SearchView searchView; private RecyclerView recyclerView; private ArrayList<Data> datalists = new ArrayList<>(); private String[] urlList = null; private String[] titleList = null; private DataAdapter dataAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initSet(); } /** * 這方法在activity所有畫面都完成之後,才會被call。 * 之前我做的專案,有很多Crash案例都是因為畫面還沒完成,點擊事件就先放行,點擊後導致Crash。 */ @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); initListener(); } /** * init畫面 */ private void initView() { searchView = (android.support.v7.widget.SearchView) findViewById(R.id.search_bar); recyclerView = (RecyclerView) findViewById(R.id.recycler_view); } /** * init設定 */ private void initSet() { //RecyclerView設定 dataAdapter = new DataAdapter(this); LinearLayoutManager llm = new LinearLayoutManager(this); llm.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(llm); searchView.setOnQueryTextListener(this); searchView.setIconifiedByDefault(false); //是否要點選搜尋圖示後再打開輸入框 searchView.setFocusable(false); searchView.requestFocusFromTouch(); //要點選後才會開啟鍵盤輸入 searchView.setSubmitButtonEnabled(false);//輸入框後是否要加上送出的按鈕 searchView.setQueryHint("請輸入android搜尋"); //輸入框沒有值時要顯示的提示文字 //假資料設定 urlList = new String[]{ "http://www.jrtstudio.com/sites/default/files/ico_android.png", "https://developer.android.com/_static/images/android/touchicon-180.png", "http://cdn.bgr.com/2012/11/android-icon.jpg?quality=98&strip=all", "http://www.fastbooting.com/uploads/5/5/9/9/55994511/1474140_orig.png", "http://www.pendidikan-diy.go.id/dinas_v4/img/up_down/Android.png", "https://www.android.com/static/2016/img/one/a1_andy_1x.png", "http://zdnet2.cbsistatic.com/hub/i/2016/04/22/a1ced73f-6628-4930-96f4-d224e1d3a707/8a43709ccee674396403ee99472e38f3/android-security-1.jpg", "https://unwire.pro/wp-content/uploads/2017/05/android-kotlin.png", "http://www.jrtstudio.com/sites/default/files/ico_android.png", "https://developer.android.com/_static/images/android/touchicon-180.png", "http://cdn.bgr.com/2012/11/android-icon.jpg?quality=98&strip=all", "http://www.fastbooting.com/uploads/5/5/9/9/55994511/1474140_orig.png", "http://www.pendidikan-diy.go.id/dinas_v4/img/up_down/Android.png", "https://www.android.com/static/2016/img/one/a1_andy_1x.png", "http://zdnet2.cbsistatic.com/hub/i/2016/04/22/a1ced73f-6628-4930-96f4-d224e1d3a707/8a43709ccee674396403ee99472e38f3/android-security-1.jpg", "https://unwire.pro/wp-content/uploads/2017/05/android-kotlin.png", }; titleList = new String[]{ "手機:iphone7", "手機殼:犀牛盾", "手機:htc", "電腦:戴爾", "手機:samsung", "電腦:hp惠普", "電腦:惠普", "電腦:蘋果電腦apple", "手機:sony", "手機:samsung s8", "手機:asus", "電腦:Auser宏碁電腦", "手機、電腦:微軟", "手機:oppo 蕩漾款", "手機:小米", "電腦:聯想", }; //兩筆假資料size()一樣才進入迴圈 if (titleList.length == urlList.length) for (int i = 0; i < (urlList.length); i++) { Data data = new Data();//製作單筆資料 data.setTitle(titleList[i]); data.setImageUrl(urlList[i]); datalists.add(data);//把單筆資料加入陣列 } dataAdapter.setData(datalists);//把陣列塞入adapter recyclerView.setAdapter(dataAdapter);//把adapter塞入recyclerview } /** * 點擊事件 */ private void initListener() { } /** * 複寫implements SearchView.OnQueryTextListener 的方法 */ @Override public boolean onQueryTextSubmit(final String query) { return false; } @Override public boolean onQueryTextChange(final String newText) { //塞選 我們寫在adapter內 dataAdapter.getFilter().filter(newText); return false; } }
重頭戲Adapter
DataAdapter.java
class DataAdapter extends RecyclerView.Adapter<DataAdapter.dataHolder> implements Filterable { private final static String TAG = DataAdapter.class.getSimpleName(); //元件 private Context context; private ArrayList<data> datalists;//是會變動的陣列,用來顯示正個recyclerView的資料 private ArrayList<data> filterDatas;//固定陣列,用來和filter比對用的。 private MyFliter myFliter;//讓mainActivity能使用adapter呼叫篩選功能 //建構子 public DataAdapter(Context context) { this.context = context; } //implements Filterable的必定覆寫的方法,讓我們在activity內能呼叫他 @Override public Filter getFilter() { if (myFliter == null) { myFliter = new MyFliter(); } return myFliter; } class dataHolder extends RecyclerView.ViewHolder { private ImageView imageView; private TextView textView; public dataHolder(View itemView) { super(itemView); imageView = (ImageView) itemView.findViewById(R.id.image_view); textView = (TextView) itemView.findViewById(R.id.text_view); } } //讓我們在activity內能讀取或更新adapter內data的資料 public void setData(ArrayList<data> datalists) { this.datalists = datalists; filterDatas = datalists; notifyDataSetChanged(); } @Override public DataAdapter.dataHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); dataHolder dh = new dataHolder(view); return dh; } @Override public void onBindViewHolder(DataAdapter.dataHolder holder, int position) { Glide.with(context) .load(datalists.get(position).getImageUrl()) .placeholder(R.drawable.default_image) .into(holder.imageView); if (!TextUtils.isEmpty(datalists.get(position).getTitle())) holder.textView.setText(datalists.get(position).getTitle()); else holder.textView.setText("暫無資料"); } @Override public int getItemCount() { return datalists == null ? 0 : datalists.size(); } //實作自己的篩選Filter class MyFliter extends Filter { @Override protected FilterResults performFiltering(CharSequence filterContent) { ArrayList<data> filterData = new ArrayList<>(); //先判斷filterContent是不是null才進入 if (filterContent != null && filterContent.toString().trim().length() > 0) { Log.d(TAG, "確認是否為空值。 filterData.size = " + filterData.size()); //這回圈是在判斷你輸入的文字(filterContent)是否有在filterDatas陣列內有相關的文字,逐條搜尋。 for (int i = 0; i < filterDatas.size(); i++) { String content = filterDatas.get(i).getTitle(); Log.d(TAG, "確認是否進入for迴圈 content = " + content); if (content.contains(String.valueOf(filterContent))) { Log.d(TAG, "確認輸入文字是否相同。"); Data data = new Data(); data.setImageUrl(filterDatas.get(i).getImageUrl()); data.setTitle(filterDatas.get(i).getTitle()); filterData.add(data); } } } else { filterData = filterDatas; Log.d(TAG, "確認什麼都沒打 filterDatas = datalists = " + filterData.size()); } FilterResults filterResults = new FilterResults(); filterResults.count = filterData.size(); filterResults.values = filterData; Log.d(TAG, "final size = " + filterResults.count); return filterResults; } @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { datalists = (ArrayList<data>) filterResults.values; if (filterResults.count > 0) { notifyDataSetChanged(); } else { Data data = new Data(); data.setTitle("沒有結果"); datalists.add(data); notifyDataSetChanged(); } } } }MyFliter 這方法內強破複寫兩種方法
performFiltering 執行篩選
publishResults 篩選結果
在performFiltering內
1、我先判斷在SearchView內的有無輸入任何文字(是否為null)
2、在寫一個for迴圈,把所有進入adatper陣列資料內的title String做比對,如果有相同的文字,就創建一個data物件,塞入你要篩選存放的ArrayList內。
3、 比對完之後把你篩選存放的ArrayList丟入FilterResults內。
在publishFiltering內
1、datalists是在adapter內會變動的陣列,用來顯示RecyclerView用的(filterDatas為固定陣列,用來和filter做資料比對)
2、 把剛剛在performFiltering抓取到的filterResults匯到datalists內。用以顯示recyclerView
3、假如陣列大於0,就notifyDataSetChanged刷新RecyclerView。
在performFiltering內
1、我先判斷在SearchView內的有無輸入任何文字(是否為null)
2、在寫一個for迴圈,把所有進入adatper陣列資料內的title String做比對,如果有相同的文字,就創建一個data物件,塞入你要篩選存放的ArrayList內。
3、 比對完之後把你篩選存放的ArrayList丟入FilterResults內。
在publishFiltering內
1、datalists是在adapter內會變動的陣列,用來顯示RecyclerView用的(filterDatas為固定陣列,用來和filter做資料比對)
2、 把剛剛在performFiltering抓取到的filterResults匯到datalists內。用以顯示recyclerView
3、假如陣列大於0,就notifyDataSetChanged刷新RecyclerView。
DEMO: https://drive.google.com/open?id=0Byk75IYx-dKXNXEzdzlzUEwzc3M
沒有留言:
張貼留言