Android多重搜寻,例如传送,搜寻联络人
在本教程中,我们将讨论并实现搜索功能,该功能将在下拉菜单中显示匹配的结果,并允许根据搜索到的字符串过滤ListView结果。
这种界面通常出现在"食物交付"应用程序中,这些应用程序有很多可供选择的选项。
用户可以基于某个标签/类别进行搜索以快速找到他们想要的结果。
在本教程的最后,您将能够提出一个与下面给出的应用程序相似的工作应用程序。
Android多重搜寻
对于上述应用程序,我们将不使用SearchView。
相反,我们将使用CardView中包装的EditText。
弹出的建议列表下拉列表将是RecyclerView。
代码
下面给出了" activity_main.xml"布局。
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:card_view="https://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#212121" android:minHeight="?attr/actionBarSize"> <TextView android:id="@+id/toolbar_title" style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/app_name" android:textColor="#FFF" </android.support.v7.widget.Toolbar> <RelativeLayout android:id="@+id/view_search" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#50000000" android:clickable="true" android:visibility="invisible"> <ProgressBar android:id="@+id/marker_progress" style="?android:attr/progressBarStyle" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerInParent="true" android:indeterminate="true" android:visibility="gone" </RelativeLayout> <ListView android:id="@+id/listContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#fff" android:clipToPadding="false" android:divider="#fff" android:paddingTop="56dp" android:visibility="gone" <android.support.v7.widget.CardView android:id="@+id/card_search" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="4dp" android:visibility="invisible" card_view:cardCornerRadius="2dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/linearLayout_search" android:layout_width="match_parent" android:layout_height="48dp"> <ImageView android:id="@+id/image_search_back" android:layout_width="48dp" android:layout_height="48dp" android:background="?android:attr/selectableItemBackground" android:clickable="true" android:padding="12dp" android:src="@mipmap/ic_arrow_back" <EditText android:id="@+id/edit_text_search" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#fff" android:focusable="true" android:gravity="center_vertical" android:hint="@string/search_restaurants_and_cuisines" android:imeOptions="actionSearch" android:inputType="textCapWords" android:maxLines="1" android:paddingLeft="12dp" android:paddingRight="8dp" </LinearLayout> <View android:id="@+id/line_divider" android:layout_width="match_parent" android:layout_height=".5dp" android:layout_below="@+id/linearLayout_search" android:background="#eee" <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="250dp" android:layout_below="@+id/line_divider" android:divider="#FFFFFF" </RelativeLayout> </android.support.v7.widget.CardView> <TextView android:id="@+id/txtNoResultsFound" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:padding="@dimen/activity_horizontal_margin" android:text="@string/no_results_found" <ListView android:id="@+id/listView" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_below="@+id/toolbar" android:layout_marginBottom="@dimen/corners_small_value" android:layout_marginLeft="@dimen/corners_small_value" android:layout_marginRight="@dimen/corners_small_value"> </ListView> <View android:id="@+id/toolbar_shadow" android:layout_width="match_parent" android:layout_height="4dp" android:layout_below="@+id/toolbar" android:background="@drawable/toolbar_shadow" </RelativeLayout>
在上面的代码中,CardView是一个Custom SearchView UI,左侧带有一个后退按钮。
ListView将显示适配器中的所有餐厅。
如下所示,在Model.java文件中定义了餐厅和美食类型的数据源。
package com.theitroad.efficientsearch; import java.util.List; public class Model { public String name; public String id; public List cuisines; public boolean isCuisine; public int numberOfCuisine; public Model(String id, String name, List cuisines, boolean isCuisine, int numberOfCuisine) { this.name = name; this.id = id; this.cuisines = cuisines; this.isCuisine = isCuisine; this.numberOfCuisine = numberOfCuisine; } }
区分餐厅和美食的参数是isCuisine,其类型为Boolean。
MainActivity.java
在下面给出。
package com.theitroad.efficientsearch; import android.content.Context; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.CardView; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class MainActivity extends AppCompatActivity implements CuisineSearchAdapter.ItemListener { Toolbar toolbar; private ImageView image_search_back; private RelativeLayout view_search; private EditText edit_text_search; private CardView card_search; private View line_divider, toolbar_shadow; RecyclerView recyclerView; ListView listView; String text = ""; List modelsList, filterModels; List cuisinesModels; ListViewAdapter listViewAdapter; CuisineSearchAdapter cuisineSearchAdapter; ShowSearchView showSearchView; boolean editTextChangedFromClick = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); showSearchView = new ShowSearchView(); toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.inflateMenu(R.menu.menu_main); image_search_back = (ImageView) findViewById(R.id.image_search_back); view_search = (RelativeLayout) findViewById(R.id.view_search); edit_text_search = (EditText) findViewById(R.id.edit_text_search); card_search = (CardView) findViewById(R.id.card_search); line_divider = findViewById(R.id.line_divider); toolbar_shadow = findViewById(R.id.toolbar_shadow); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); listView = (ListView) findViewById(R.id.listView); TextView no_results = (TextView) findViewById(R.id.txtNoResultsFound); listView.setEmptyView(no_results); populateRestaurantsAndCuisines(); image_search_back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { text = ""; showSearchView.handleToolBar(MainActivity.this, card_search, toolbar, view_search, recyclerView, edit_text_search, line_divider); toolbar_shadow.setVisibility(View.VISIBLE); listViewAdapter = new ListViewAdapter(modelsList); listView.setAdapter(listViewAdapter); } }); edit_text_search.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { text = s.toString(); if (editTextChangedFromClick) { editTextChangedFromClick = false; if (recyclerView.getVisibility() == View.VISIBLE) recyclerView.setVisibility(View.GONE); } else { if (recyclerView.getVisibility() != View.VISIBLE) recyclerView.setVisibility(View.VISIBLE); if (s.toString().length() > 0) { performFiltering(filterModels); } else { CuisineSearchAdapter cuisineSearchAdapter = new CuisineSearchAdapter(cuisinesModels, modelsList, MainActivity.this, MainActivity.this, text); cuisineSearchAdapter.notifyDataSetChanged(); recyclerView.setAdapter(cuisineSearchAdapter); listViewAdapter = new ListViewAdapter(modelsList); listView.setAdapter(listViewAdapter); } } } @Override public void afterTextChanged(Editable s) { } }); edit_text_search.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { //Your piece of code on keyboard search click recyclerView.setVisibility(View.GONE); ((InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(view_search.getWindowToken(), 0); listViewAdapter.getFilter().filter(v.getText().toString()); return true; } return false; } }); toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int menuItem = item.getItemId(); switch (menuItem) { case R.id.action_search: recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); cuisineSearchAdapter = new CuisineSearchAdapter(cuisinesModels, filterModels, MainActivity.this, MainActivity.this, text); recyclerView.setAdapter(cuisineSearchAdapter); showSearchView.handleToolBar(MainActivity.this, card_search, toolbar, view_search, recyclerView, edit_text_search, line_divider); break; default: break; } return false; } }); } private void populateRestaurantsAndCuisines() { modelsList = new ArrayList(); cuisinesModels = new ArrayList(); List cuisinesList = new ArrayList(); for (int i = 0; i < 7; i++) cuisinesList.add("Cafes"); for (int i = 0; i < 4; i++) cuisinesList.add("Burgers"); for (int i = 0; i < 4; i++) cuisinesList.add("Bars"); modelsList.add(new Model("1", "McDonalds", new ArrayList(Arrays.asList("Cafes", "Burgers")), false, -1)); modelsList.add(new Model("2", "KFC", new ArrayList(Arrays.asList("Cafes", "Burgers")), false, -1)); modelsList.add(new Model("3", "Burger King", new ArrayList(Arrays.asList("Cafes", "Burgers")), false, -1)); modelsList.add(new Model("4", "Subway", new ArrayList(Arrays.asList("Burgers")), false, -1)); modelsList.add(new Model("5", "Cafe Coffee Day", new ArrayList(Arrays.asList("Cafes")), false, -1)); modelsList.add(new Model("6", "Costa", new ArrayList(Arrays.asList("Cafes")), false, -1)); modelsList.add(new Model("7", "Coffee Beans", new ArrayList(Arrays.asList("Cafes")), false, -1)); modelsList.add(new Model("8", "Starbucks", new ArrayList(Arrays.asList("Cafes")), false, -1)); modelsList.add(new Model("9", "Blues", new ArrayList(Arrays.asList("Bars")), false, -1)); modelsList.add(new Model("10", "Hard Rock Cafe", new ArrayList(Arrays.asList("Bars", "Cafe")), false, -1)); modelsList.add(new Model("11", "The Backyard Underground", new ArrayList(Arrays.asList("Bars")), false, -1)); modelsList.add(new Model("12", "Downtown", new ArrayList(Arrays.asList("Bars")), false, -1)); Map cuisineMap = new HashMap(); for (String cuisine : cuisinesList) { Integer n = cuisineMap.get(cuisine); n = (n == null) ? 1 : ++n; cuisineMap.put(cuisine, n); } for (Map.Entry entry : cuisineMap.entrySet()) { Model model = new Model("", entry.getKey(), null, true, entry.getValue()); modelsList.add(model); cuisinesModels.add(model); } filterModels = new ArrayList(modelsList); initialiseAdapters(); } private void initialiseAdapters() { listViewAdapter = new ListViewAdapter(filterModels); listView.setAdapter(listViewAdapter); } @Override public void onItemClick(Model model) { editTextChangedFromClick = true; if (model.isCuisine) { edit_text_search.setText(model.name); listViewAdapter.getFilter().filter(model.name); } else { edit_text_search.setText(model.name); showSearchView.handleToolBar(MainActivity.this, card_search, toolbar, view_search, recyclerView, edit_text_search, line_divider); Toast.makeText(getApplicationContext(), model.name + " was selected.", Toast.LENGTH_LONG).show(); } } public void performFiltering(List filteredSuggestions) { filteredSuggestions.clear(); for (Model model : modelsList) { if (model.name.toLowerCase().contains(text.toLowerCase())) { filteredSuggestions.add(model); } } CuisineSearchAdapter cuisineSearchAdapter = new CuisineSearchAdapter(cuisinesModels, filteredSuggestions, MainActivity.this, MainActivity.this, text); cuisineSearchAdapter.notifyDataSetChanged(); recyclerView.setAdapter(cuisineSearchAdapter); } }
工具列布局是从" menu_main.xml"文件中设置的,如下所示。
<menu xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" tools:context=".MainActivity"> <item android:id="@+id/action_search" android:icon="@mipmap/ic_action_search" android:orderInCategory="100" android:title="Search" app:showAsAction="always" </menu>
在详细分析MainActivity.java类之前,让我们看一下带有过滤器的ListViewAdapter的代码。
我们已经在此处实施的某些功能。
下面给出了ListViewAdapter.java类的代码。
package com.theitroad.efficientsearch; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class ListViewAdapter extends BaseAdapter implements Filterable { private List modelList; private List mStringFilterList; private ValueFilter valueFilter; private class ViewHolder { TextView vendorName; } public ListViewAdapter(List modelList) { this.modelList = modelList; mStringFilterList = modelList; } @Override public int getCount() { if (modelList != null) return modelList.size(); else return 0; } @Override public Model getItem(int position) { return modelList.get(position); } @Override public long getItemId(int position) { Model object = getItem(position); if (object.isCuisine) { return -1; } else return Integer.parseInt(object.id); } @Override public View getView(int position, View convertView, final ViewGroup parent) { ViewHolder holder = null; Model vendorModel = getItem(position); if (vendorModel.isCuisine) { return LayoutInflater.from(parent.getContext()).inflate(R.layout.row_null, null); } else { if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row_model, parent, false); holder = new ViewHolder(); holder.vendorName = ((TextView) convertView.findViewById(R.id.txt_vendor_name)); convertView.setTag(holder); } else { if (holder == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row_model, parent, false); holder = new ViewHolder(); holder.vendorName = ((TextView) convertView.findViewById(R.id.txt_vendor_name)); convertView.setTag(holder); } else holder = (ViewHolder) convertView.getTag(); } holder.vendorName.setText(vendorModel.name); return convertView; } } @Override public Filter getFilter() { if (valueFilter == null) { valueFilter = new ValueFilter(); } return valueFilter; } private class ValueFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint != null && constraint.length() > 0) { List filterList = new ArrayList(); for (int i = 0; i < mStringFilterList.size(); i++) { if (!mStringFilterList.get(i).isCuisine) { if (mStringFilterList.get(i).cuisines.contains(constraint)) { Model model = new Model(mStringFilterList.get(i).id, mStringFilterList.get(i).name, mStringFilterList.get(i).cuisines, false, -1); filterList.add(model); } } } if (filterList.size() == 0) { for (int i = 0; i < mStringFilterList.size(); i++) { if ((mStringFilterList.get(i).name.toUpperCase()) .contains(constraint.toString().toUpperCase()) && !mStringFilterList.get(i).isCuisine) { Model model = new Model(mStringFilterList.get(i).id, mStringFilterList.get(i).name, mStringFilterList.get(i).cuisines, false, -1); if (!model.isCuisine) filterList.add(model); } } } results.count = filterList.size(); results.values = filterList; } else { results.count = mStringFilterList.size(); results.values = mStringFilterList; } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { modelList = (List) results.values; notifyDataSetChanged(); } } }
在上面的类中,我们检查每个Model实例上的ʻisCuisine参数。 如果是真的,我们添加一个高度为0的空行。 其他
list_row_model.xml`已添加。
下面给出了每种布局的xml代码。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> </LinearLayout>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:id="@+id/rl_car" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/activity_vertical_margin"> <TextView android:id="@+id/txt_vendor_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="5dp" android:textColor="#212121" android:textSize="20sp" android:textStyle="bold" </RelativeLayout>
单击工具列中的搜索图标,将使我们的自定义搜索UI膨胀,并在下面填充RecyclerView。
为此,在ShowSearchView类的实例上调用handleToolbar方法。
下面给出了" ShowSearchView.java"的代码。
package com.theitroad.efficientsearch; import android.animation.Animator; import android.content.Context; import android.content.res.Resources; import android.os.Build; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewAnimationUtils; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; public class ShowSearchView { public static void handleToolBar(final Context context, final CardView search, Toolbar toolbarMain, final View view, final RecyclerView recyclerView, final EditText editText, final View line_divider) { final Animation fade_in = AnimationUtils.loadAnimation(context.getApplicationContext(), android.R.anim.fade_in); final Animation fade_out = AnimationUtils.loadAnimation(context.getApplicationContext(), android.R.anim.fade_out); if (search.getVisibility() == View.VISIBLE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { final Animator animatorHide = ViewAnimationUtils.createCircularReveal(search, search.getWidth() - (int) convertDpToPixel(56, context), (int) convertDpToPixel(23, context), (float) Math.hypot(search.getWidth(), search.getHeight()), 0); animatorHide.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { view.startAnimation(fade_out); view.setVisibility(View.INVISIBLE); search.setVisibility(View.GONE); ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(view.getWindowToken(), 0); recyclerView.setVisibility(View.GONE); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animatorHide.setDuration(300); animatorHide.start(); } else { ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(view.getWindowToken(), 0); view.startAnimation(fade_out); view.setVisibility(View.INVISIBLE); search.setVisibility(View.GONE); } editText.setText(""); toolbarMain.getMenu().clear(); toolbarMain.inflateMenu(R.menu.menu_main); search.setEnabled(false); } else { toolbarMain.getMenu().clear(); toolbarMain.setNavigationIcon(null); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { final Animator animator = ViewAnimationUtils.createCircularReveal(search, search.getWidth() - (int) convertDpToPixel(56, context), (int) convertDpToPixel(23, context), 0, (float) Math.hypot(search.getWidth(), search.getHeight())); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { view.setVisibility(View.VISIBLE); view.startAnimation(fade_in); ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); search.setVisibility(View.VISIBLE); if (search.getVisibility() == View.VISIBLE) { animator.setDuration(300); animator.start(); search.setEnabled(true); } fade_in.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { editText.requestFocus(); recyclerView.setVisibility(View.VISIBLE); } @Override public void onAnimationRepeat(Animation animation) { } }); } else { search.setVisibility(View.VISIBLE); search.setEnabled(true); recyclerView.setVisibility(View.VISIBLE); editText.requestFocus(); ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); } } } public static float convertDpToPixel(float dp, Context context) { Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); return dp * (metrics.densityDpi/160f); } }
handleToolbar方法根据分别单击的搜索图标/后退箭头来显示/隐藏自定义UI。
该视图以圆形显示动画的形式进行动画进/出。
RecyclerView充满了美食和餐厅。
尽管仅显示美食,直到用户键入任何内容。
RecyclerView的适配器在类" CuisinesSearchAdapter.java"中定义,如下所示。
package com.theitroad.efficientsearch; import android.content.Context; import android.graphics.Color; import android.support.v7.widget.RecyclerView; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.text.Normalizer; import java.util.List; import java.util.Locale; public class CuisineSearchAdapter extends RecyclerView.Adapter { Context mContext; ItemListener mListener; String prefix = ""; private List allVendors, cuisines; public CuisineSearchAdapter(List cuisines, List vendorModels, Context context, ItemListener itemListener, String text) { this.allVendors = vendorModels; this.cuisines = cuisines; this.mContext = context; this.mListener = itemListener; prefix = text; } public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { ImageView icon; TextView textView; View parent; Model vendorModel; MyViewHolder(View itemView) { super(itemView); this.parent = itemView.findViewById(R.id.parentView); this.textView = (TextView) itemView.findViewById(R.id.textView); this.icon = (ImageView) itemView.findViewById(R.id.imageView); itemView.setOnClickListener(this); } void setData(Model model, MyViewHolder holder) { textView = holder.textView; icon = holder.icon; this.vendorModel = model; if (prefix.length() > 0) { if (model.isCuisine) { textView.setText(highlight(prefix, model.name + " (" + model.numberOfCuisine + ")")); icon.setImageResource(R.mipmap.ic_local_offer); } else { textView.setText(highlight(prefix, model.name)); icon.setImageResource(R.mipmap.ic_local_dining); } } else { if (model.isCuisine) { textView.setText(model.name + " (" + model.numberOfCuisine + ")"); icon.setImageResource(R.mipmap.ic_local_offer); } } } @Override public void onClick(View view) { if (mListener != null) { mListener.onItemClick(vendorModel); } } } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { final View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.recyclerview_list_row, parent, false); MyViewHolder myViewHolder = new MyViewHolder(view); return myViewHolder; } @Override public void onBindViewHolder(final MyViewHolder holder, final int listPosition) { if (prefix.length() > 0) holder.setData(allVendors.get(holder.getAdapterPosition()), holder); else holder.setData(cuisines.get(holder.getAdapterPosition()), holder); } @Override public int getItemCount() { if (prefix.length() > 0) return allVendors.size(); else return cuisines.size(); } private static CharSequence highlight(String search, String originalText) { //ignore case and accents //the same thing should have been done for the search text String normalizedText = Normalizer .normalize(originalText, Normalizer.Form.NFD) .replaceAll("\p{InCombiningDiacriticalMarks}+", "") .toLowerCase(Locale.ENGLISH); int start = normalizedText.indexOf(search.toLowerCase(Locale.ENGLISH)); if (start = 0) { int spanStart = Math.min(start, originalText.length()); int spanEnd = Math.min(start + search.length(), originalText.length()); highlighted.setSpan(new ForegroundColorSpan(Color.BLUE), spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); start = normalizedText.indexOf(search, spanEnd); } return highlighted; } } public interface ItemListener { void onItemClick(Model model); } }
RecyclerView每行的布局在下面的xml代码中定义。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:id="@+id/parentView" android:background="#FFF" android:layout_height="wrap_content"> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center_vertical" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:id="@+id/imageView" android:tint="@color/text_color" <TextView android:id="@+id/textView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:padding="16dp" android:textColor="@color/text_color" android:textSize="14sp" </LinearLayout>
当用户输入文本时,RecyclerView数据将从MainActivity.java类中的performFiltering方法中过滤掉。
然后,突出显示RecyclerView行中与键入的文本匹配的子字符串。单击美食会过滤列表,以显示所有以该美食为类型的餐馆。