今天的主題是RecyclerView + searchView的實作
用白話文講就是常看到的搜尋啦XD
當然,或許機制有點不一樣,不過整體來說就是一樣的東西不用懷疑(;´д`)ゞ
那麼今天做出來會是怎樣呢?直接看範例最快!
Github在此
->https://github.com/thumbb13555/RecyclerViewWithSearch/tree/master
1. 結構
其實今天的這個主題也是一個RecyclerView的應用之一
蛤?不知道RecyclerView?
看這篇吧XD
->碼農日常-『Android studio』基本RecyclerView用法
不過今天的重點是在寫搜尋的方法以及設置SearchView的方法
先來看一下專案結構吧
2. 拉介面&設置SearchView
<?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/action_search" android:title="Search" android:icon="@drawable/ic_baseline_search_24" app:showAsAction="always|collapseActionView" app:actionViewClass="androidx.appcompat.widget.SearchView" /> </menu>
基本上按照上方的做法就完成設置界面了
ViewClass的部分一定要指定,不然會無法取得他的控制項(有點接近設置繼承(´∀`)
再來是主介面樣式(就是個RecyclerVIew....)
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
3. 設置RecyclerView的Adapter
先PO全部
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements Filterable { /**上方的arrayList為RecyclerView所綁定的ArrayList*/ ArrayList<String> arrayList; /**儲存最原先ArrayList的狀態(也就是充當回複RecyclerView最原先狀態的陣列)*/ ArrayList<String> arrayListFilter; public RecyclerViewAdapter(ArrayList<String> arrayList) { this.arrayList = arrayList; arrayListFilter = new ArrayList<>(); /**這裡把初始陣列複製進去了*/ arrayListFilter.addAll(arrayList); } public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ TextView tvItem; public ViewHolder(@NonNull View itemView) { super(itemView); tvItem = itemView.findViewById(android.R.id.text1); /**點擊事件*/ itemView.setOnClickListener(this); } @Override public void onClick(View v) { Toast.makeText(itemView.getContext(), arrayList.get(getAdapterPosition()) , Toast.LENGTH_SHORT).show(); } } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1,parent,false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.tvItem.setText(arrayList.get(position)); } @Override public int getItemCount() { return arrayList.size(); } @Override public Filter getFilter() { return mFilter; } /**使用Filter濾除方法*/ Filter mFilter = new Filter() { /**此處為正在濾除字串時所做的事*/ @Override protected FilterResults performFiltering(CharSequence constraint) { /**先將完整陣列複製過去*/ ArrayList<String> filteredList = new ArrayList<>(); /**如果沒有輸入,則將原本的陣列帶入*/ if (constraint == null || constraint.length() == 0) { filteredList.addAll(arrayListFilter); } else { /**如果有輸入,則照順序濾除相關字串 * 如果你有更好的搜尋演算法,就是寫在這邊*/ for (String movie: arrayListFilter) { if (movie.toLowerCase().contains(constraint.toString().toLowerCase())) { filteredList.add(movie); } } } /**回傳濾除結果*/ FilterResults filterResults = new FilterResults(); filterResults.values = filteredList; return filterResults; } /**將濾除結果推給原先RecyclerView綁定的陣列,並通知RecyclerView刷新*/ @Override protected void publishResults(CharSequence constraint, FilterResults results) { arrayList.clear(); arrayList.addAll((Collection<? extends String>) results.values); notifyDataSetChanged(); } }; }
Okay,這部分有兩個重點
1. 設置RecyclerView本身
2. 設置Filter
接下來我按照我的順序來講述這裡的寫法
首先是設置RecyclerView本身
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{ /**上方的arrayList為RecyclerView所綁定的ArrayList*/ ArrayList<String> arrayList; /**儲存最原先ArrayList的狀態(也就是充當回複RecyclerView最原先狀態的陣列)*/ ArrayList<String> arrayListFilter; public RecyclerViewAdapter(ArrayList<String> arrayList) { this.arrayList = arrayList; arrayListFilter = new ArrayList<>(); /**這裡把初始陣列複製進去了*/ arrayListFilter.addAll(arrayList); } public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ TextView tvItem; public ViewHolder(@NonNull View itemView) { super(itemView); tvItem = itemView.findViewById(android.R.id.text1); /**點擊事件*/ itemView.setOnClickListener(this); } @Override public void onClick(View v) { Toast.makeText(itemView.getContext(), arrayList.get(getAdapterPosition()) , Toast.LENGTH_SHORT).show(); } } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1,parent,false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.tvItem.setText(arrayList.get(position)); } @Override public int getItemCount() { return arrayList.size(); } }
RecyclerView的Adapter其實就是輸入一個陣列後一直去改這個陣列的一個過程
所以在初始化時我們就宣布了兩個陣列
/**上方的arrayList為RecyclerView所綁定的ArrayList*/ ArrayList<String> arrayList; /**儲存最原先ArrayList的狀態(也就是充當回複RecyclerView最原先狀態的陣列)*/ ArrayList<String> arrayListFilter; public RecyclerViewAdapter(ArrayList<String> arrayList) { this.arrayList = arrayList; arrayListFilter = new ArrayList<>(); /**這裡把初始陣列複製進去了*/ arrayListFilter.addAll(arrayList); }
如註解所說,arrayList的那個陣列就是綁定給RecyclerView元件的陣列
也因此想要使RecyclerView有變化,便是要修改arrayList的陣列
那arrayListFilter這個部分就是用來存最初始的那個陣列用的
...Emmm也就是最原先的狀態
再來是第二個部分Filter
直接PO~
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements Filterable { //(略) . . @Override public int getItemCount() { return arrayList.size(); } //(以下新增) @Override public Filter getFilter() { return mFilter; } /**使用Filter濾除方法*/ Filter mFilter = new Filter() { /**此處為正在濾除字串時所做的事*/ @Override protected FilterResults performFiltering(CharSequence constraint) { /**先將完整陣列複製過去*/ ArrayList<String> filteredList = new ArrayList<>(); /**如果沒有輸入,則將原本的陣列帶入*/ if (constraint == null || constraint.length() == 0) { filteredList.addAll(arrayListFilter); } else { /**如果有輸入,則照順序濾除相關字串 * 如果你有更好的搜尋演算法,就是寫在這邊*/ for (String movie: arrayListFilter) { if (movie.toLowerCase().contains(constraint.toString().toLowerCase())) { filteredList.add(movie); } } } /**回傳濾除結果*/ FilterResults filterResults = new FilterResults(); filterResults.values = filteredList; return filterResults; } /**將濾除結果推給原先RecyclerView綁定的陣列,並通知RecyclerView刷新*/ @Override protected void publishResults(CharSequence constraint, FilterResults results) { arrayList.clear(); arrayList.addAll((Collection<? extends String>) results.values); notifyDataSetChanged(); } }; }
4. 設置主要控制(MainActivity)
一樣,先全部
public class MainActivity extends AppCompatActivity { RecyclerViewAdapter mAdapter; String[] strings = {"CRUISER MK. I","CRUISER MK. II","VALENTINE","CRUISER MK. III" ,"CRUISER MK. IV","COVENANTER","CRUSADER","GSR 3301 SETTER","LHMTV","GSOR3301 AVR FS" ,"MANTICORE","MATILDA","CHURCHILL I","CHURCHILL VII","BLACK PRINCE","CAERNARVON" ,"CONQUEROR","SUPER CONQUEROR","CAVALIER","CROMWELL","COMET","CENTURION MK. I" ,"CENTURION MK. 7/1","CENTURION ACTION X","VALENTINE AT","BISHOP","FV304" ,"CRUSADER 5.5-IN. SP","FV207","FV3805","CONQUEROR GUN CARRIAGE" ,"AT 2","ACHILLES","CHALLENGER","CHARIOTEER","FV4004 CONWAY","FV4005 STAGE II" ,"AT 8","AT 7","AT 15","TORTOISE","FV217 BADGER"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); ArrayList arrayList = new ArrayList(); for (int i = 0; i < strings.length; i++) { arrayList.add(strings[i]); } mAdapter = new RecyclerViewAdapter(arrayList); recyclerView.setAdapter(mAdapter); } /**初始化Toolbar內SearchView的設置*/ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); MenuItem menuItem = menu.findItem(R.id.action_search); SearchView searchView = (SearchView) menuItem.getActionView(); /**SearchView設置,以及輸入內容後的行動*/ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { /**調用RecyclerView內的Filter方法*/ mAdapter.getFilter().filter(newText); return false; } }); return super.onCreateOptionsMenu(menu); } }
上半部onCreate內就是設置RecyclerView元件本身的設置
下半部為設置SearchView的部分
再來searchView在輸入時,其觸發點就是onQueryTextChange的這個部分
而這個部分就是調用RecyclerViewAdapter.java內Filter本身的方法
也就是在RecyclerViewAdapter.java中這個部分
@Override public Filter getFilter() { return mFilter; } /**使用Filter濾除方法*/ Filter mFilter = new Filter() { /**此處為正在濾除字串時所做的事*/ @Override protected FilterResults performFiltering(CharSequence constraint) { /**先將完整陣列複製過去*/ ArrayList<String> filteredList = new ArrayList<>(); /**如果沒有輸入,則將原本的陣列帶入*/ if (constraint == null || constraint.length() == 0) { filteredList.addAll(arrayListFilter); } else { /**如果有輸入,則照順序濾除相關字串 * 如果你有更好的搜尋演算法,就是寫在這邊*/ for (String movie: arrayListFilter) { if (movie.toLowerCase().contains(constraint.toString().toLowerCase())) { filteredList.add(movie); } } } /**回傳濾除結果*/ FilterResults filterResults = new FilterResults(); filterResults.values = filteredList; return filterResults; } /**將濾除結果推給原先RecyclerView綁定的陣列,並通知RecyclerView刷新*/ @Override protected void publishResults(CharSequence constraint, FilterResults results) { arrayList.clear(); arrayList.addAll((Collection<? extends String>) results.values); notifyDataSetChanged(); } };
到這邊都寫上後,本次的功能應該是完成囉 :D
你做出來了嗎XD
那今天的文章到這邊囉!
謝謝各位
留言列表