使用 SearchView 小部件时如何突出显示 RecyclerView 中的过滤文本
How to highlight filtered text in RecyclerView when using SearchView widget
如何在 RecyclerView 中突出显示搜索文本结果。
我发现了一些关于 Spannable TextView 的帖子,但不确定在我的案例中应该在哪里实施。
感谢您的关注和协助。
MainActivity 或 Chapter1
public class Chapter1 extends AppCompatActivity implements SearchView.OnQueryTextListener {
MyRecAdapter myRecAdapter;
RecyclerView recyclerView;
List<Post> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layout.chapter1_main);
recyclerView = (RecyclerView) findViewById(R.id.myrec);
createdata();
myRecAdapter = new MyRecAdapter(list, Chapter1.this);
recyclerView.setLayoutManager(new LinearLayoutManager(Chapter1.this));
recyclerView.setAdapter(myRecAdapter);
}
void createdata() {
list = new ArrayList<>();
String topic_1_1 = getResources().getString(string.topic_1_1);
String text_1_1 = getString(string.text_1_1);
String topic_1_2 = getResources().getString(string.topic_1_2);
String topic_1_3 = getResources().getString(string.topic_1_3);
String text_1_3 = getString(string.text_1_3);
list.add(new Post(topic_1_1, text_1_1));
list.add(new Post(topic_1_2, ""));
list.add(new Post(topic_1_3, text_1_3));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_search, menu);
getMenuInflater().inflate(R.menu.main, menu);
MenuItem item = menu.findItem(R.id.menu_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setOnQueryTextListener(this);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onQueryTextChange(String newText) {
final List<Post> filteredModelList = filter(list, newText);
if (filteredModelList.size() > 0) {
myRecAdapter.setFilter(filteredModelList);
return true;
} else {
Toast.makeText(Chapter1.this, "Not Found", Toast.LENGTH_SHORT).show();
return false;
}
}
private List<Post> filter(List<Post> models, String query ) {
query = query.toLowerCase();
final List<Post> filteredModelList = new ArrayList<>();
for (Post model : models) {
final String text = model.getPostTitle().toLowerCase();
final String text_sub = model.getPostSubTitle().toLowerCase();
if (text.contains(query)) {
filteredModelList.add(model);
}
else {
if (text_sub.contains(query)) {
filteredModelList.add(model);
}
}
}
createdata();
myRecAdapter = new MyRecAdapter(filteredModelList, Chapter1.this);
recyclerView.setLayoutManager(new LinearLayoutManager(Chapter1.this));
recyclerView.setAdapter(myRecAdapter);
myRecAdapter.notifyDataSetChanged();
return filteredModelList;
}
}
适配器
public class MyRecAdapter extends RecyclerView.Adapter<MyRecAdapter.VH> {
public List<Post> parkingList;
public Context context;
ArrayList<Post> mCountryModel;
public MyRecAdapter(List<Post> parkingList, Context context) {
this.parkingList = parkingList;
this.context = context;
}
@Override
public MyRecAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyRecAdapter.VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.mycardview, parent, false));
}
@Override
public void onBindViewHolder(MyRecAdapter.VH holder, int position) {
holder.t1.setText(Html.fromHtml(parkingList.get(position).getPostTitle())); holder.t2.setText(Html.fromHtml(parkingList.get(position).getPostSubTitle()));
}
@Override
public int getItemCount() {
return parkingList.size();
}
public class VH extends RecyclerView.ViewHolder {
TextView t1, t2;
public VH(View view) {
super(view);
t1 = (TextView) view.findViewById(R.id.list_title);
t2 = (TextView) view.findViewById(R.id.list_desc);
}
}
public void setFilter(List<Post> countryModels) {
mCountryModel = new ArrayList<>();
mCountryModel.addAll(countryModels);
notifyDataSetChanged();
}
}
将您的适配器更改为
public class MyRecAdapter extends RecyclerView.Adapter<MyRecAdapter.VH> {
public List<Post> parkingList;
public Context context;
ArrayList<Post> mCountryModel;
String searchText;
public MyRecAdapter(List<Post> parkingList, Context context) {
this.parkingList = parkingList;
this.context = context;
}
@Override
public MyRecAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyRecAdapter.VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.mycardview, parent, false));
}
@Override
public void onBindViewHolder(MyRecAdapter.VH holder, int position) {
String title = parkingList.get(position).getPostTitle();
String desc = parkingList.get(position).getPostSubTitle();
holder.t1.setText(Html.fromHtml(title));
if(searchText.length()>0){
//color your text here
int index = desc.indexOf(searchText);
while(index>0){
SpannableStringBuilder sb = new SpannableStringBuilder(desc);
ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); //specify color here
sb.setSpan(fcs, index, index+searchText.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
index = desc.indexOf(searchText,index+1);
}
holder.t2.setText(sb);
}else{
holder.t2.setText(Html.fromHtml(desc));
}
}
@Override
public int getItemCount() {
return parkingList.size();
}
public class VH extends RecyclerView.ViewHolder {
TextView t1, t2;
public VH(View view) {
super(view);
t1 = (TextView) view.findViewById(R.id.list_title);
t2 = (TextView) view.findViewById(R.id.list_desc);
}
}
public void setFilter(List<Post> countryModels,String searchText) {
mCountryModel = new ArrayList<>();
mCountryModel.addAll(countryModels);
this.searchText = searchText;
notifyDataSetChanged();
}
}
并将 onQueryTextChange 设置为
@Override
public boolean onQueryTextChange(String newText) {
final List<Post> filteredModelList = filter(list, newText);
if (filteredModelList.size() > 0) {
myRecAdapter.setFilter(filteredModelList,newText);
return true;
} else {
Toast.makeText(Chapter1.this, "Not Found", Toast.LENGTH_SHORT).show();
return false;
}
}
while(index>**-1**){
SpannableStringBuilder sb = new SpannableStringBuilder(desc);
ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); //specify color here
sb.setSpan(fcs, index, **index** + searchText.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
index = desc.indexOf(searchText,index+1);
}
上一个答案的一个小补充,您应该改为检查索引 >-1,否则第一个文本将不会突出显示。
对于 setSpan , int 端应该是你的索引 + searchText.length().
根据接受的答案,如果字符串值 "Mumbai" 在字符串中出现相同的情况,则 indexOf() 不起作用。所以在这里我使用了 "Pattern" 和 "Matcher" class 来让它工作。
此外,我还添加了“.toLowerCase()”,使字符串具有相同的大小写和查询字符串作为忽略大小写。
如果您不需要忽略查询字符串的大小写,您可以从此代码片段中删除 .toLowerCase()
SpannableStringBuilder sb = new SpannableStringBuilder(desc);
Pattern word = Pattern.compile(query.toLowerCase());
Matcher match = word.matcher(desc.toLowerCase());
while (match.find()) {
ForegroundColorSpan fcs = new ForegroundColorSpan(
ContextCompat.getColor(context, R.color.colorPrimary)); //specify color here
sb.setSpan(fcs, match.start(), match.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
viewHolders.text_view_title.setText(sb);
在 Adapter 的 Bind view holder 上使用 inside class
String title1 = Itemlist.get(position).getN_stall_name().toLowerCase(Locale.getDefault());
holder.title.setText(Itemlist.get(position).getN_stall_name());
if (title1.contains(searchText)) {
int startPos = title1.indexOf(searchText);
int endPos = startPos + searchText.length();
Spannable spanString = Spannable.Factory.getInstance().newSpannable(holder.title.getText());
spanString.setSpan(new ForegroundColorSpan(Color.RED), startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
holder.title.setText(spanString);
}
现在在适配器中创建 public 方法 class
public void updateList(List<GetShop> list, String searchText) {
Itemlist = new ArrayList<>();
Itemlist.addAll(list);
this.searchText = searchText;
notifyDataSetChanged();
}
在您使用搜索视图的地方 Activity 使用搜索视图侦听器
SearchView searchview=findviewbyid(R.id.searchview);
searchview.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
newText = newText.toLowerCase();
ArrayList<GetShop> newList = new ArrayList<>();
for (GetShop userInfo : items) {
String type = userInfo.getN_stall_name().toLowerCase();
if (type.contains(newText)) {
newList.add(userInfo);
}
}
disp_adapter.updateList(newList, newText);
return true;
}
});
/**
* Kotlin Code
* highlight the backgroundText
* searchText -> pass the hightlight that appear in background
* name -> actual string name
* textView -> append the data to widget.
*/
private fun highlightText(
searchText: String,
name: String,
textView: TextView
) {
if (searchText.isNotEmpty() && name.contains(searchText)) {
val sb = SpannableStringBuilder(name)
var index: Int = name.lowercase().indexOf(searchText.lowercase())
while (index >= 0 && index < name.length) {
val fcs = BackgroundColorSpan(Color.rgb(0, 200, 200)) //specify color here
sb.setSpan(
fcs,
index,
index + searchText.length,
Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
index = name.indexOf(searchText, index + 1)
}
textView.text = sb
} else {
textView.text = name
}
}
如何在 RecyclerView 中突出显示搜索文本结果。 我发现了一些关于 Spannable TextView 的帖子,但不确定在我的案例中应该在哪里实施。 感谢您的关注和协助。
MainActivity 或 Chapter1
public class Chapter1 extends AppCompatActivity implements SearchView.OnQueryTextListener {
MyRecAdapter myRecAdapter;
RecyclerView recyclerView;
List<Post> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layout.chapter1_main);
recyclerView = (RecyclerView) findViewById(R.id.myrec);
createdata();
myRecAdapter = new MyRecAdapter(list, Chapter1.this);
recyclerView.setLayoutManager(new LinearLayoutManager(Chapter1.this));
recyclerView.setAdapter(myRecAdapter);
}
void createdata() {
list = new ArrayList<>();
String topic_1_1 = getResources().getString(string.topic_1_1);
String text_1_1 = getString(string.text_1_1);
String topic_1_2 = getResources().getString(string.topic_1_2);
String topic_1_3 = getResources().getString(string.topic_1_3);
String text_1_3 = getString(string.text_1_3);
list.add(new Post(topic_1_1, text_1_1));
list.add(new Post(topic_1_2, ""));
list.add(new Post(topic_1_3, text_1_3));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_search, menu);
getMenuInflater().inflate(R.menu.main, menu);
MenuItem item = menu.findItem(R.id.menu_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setOnQueryTextListener(this);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onQueryTextChange(String newText) {
final List<Post> filteredModelList = filter(list, newText);
if (filteredModelList.size() > 0) {
myRecAdapter.setFilter(filteredModelList);
return true;
} else {
Toast.makeText(Chapter1.this, "Not Found", Toast.LENGTH_SHORT).show();
return false;
}
}
private List<Post> filter(List<Post> models, String query ) {
query = query.toLowerCase();
final List<Post> filteredModelList = new ArrayList<>();
for (Post model : models) {
final String text = model.getPostTitle().toLowerCase();
final String text_sub = model.getPostSubTitle().toLowerCase();
if (text.contains(query)) {
filteredModelList.add(model);
}
else {
if (text_sub.contains(query)) {
filteredModelList.add(model);
}
}
}
createdata();
myRecAdapter = new MyRecAdapter(filteredModelList, Chapter1.this);
recyclerView.setLayoutManager(new LinearLayoutManager(Chapter1.this));
recyclerView.setAdapter(myRecAdapter);
myRecAdapter.notifyDataSetChanged();
return filteredModelList;
}
}
适配器
public class MyRecAdapter extends RecyclerView.Adapter<MyRecAdapter.VH> {
public List<Post> parkingList;
public Context context;
ArrayList<Post> mCountryModel;
public MyRecAdapter(List<Post> parkingList, Context context) {
this.parkingList = parkingList;
this.context = context;
}
@Override
public MyRecAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyRecAdapter.VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.mycardview, parent, false));
}
@Override
public void onBindViewHolder(MyRecAdapter.VH holder, int position) {
holder.t1.setText(Html.fromHtml(parkingList.get(position).getPostTitle())); holder.t2.setText(Html.fromHtml(parkingList.get(position).getPostSubTitle()));
}
@Override
public int getItemCount() {
return parkingList.size();
}
public class VH extends RecyclerView.ViewHolder {
TextView t1, t2;
public VH(View view) {
super(view);
t1 = (TextView) view.findViewById(R.id.list_title);
t2 = (TextView) view.findViewById(R.id.list_desc);
}
}
public void setFilter(List<Post> countryModels) {
mCountryModel = new ArrayList<>();
mCountryModel.addAll(countryModels);
notifyDataSetChanged();
}
}
将您的适配器更改为
public class MyRecAdapter extends RecyclerView.Adapter<MyRecAdapter.VH> {
public List<Post> parkingList;
public Context context;
ArrayList<Post> mCountryModel;
String searchText;
public MyRecAdapter(List<Post> parkingList, Context context) {
this.parkingList = parkingList;
this.context = context;
}
@Override
public MyRecAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyRecAdapter.VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.mycardview, parent, false));
}
@Override
public void onBindViewHolder(MyRecAdapter.VH holder, int position) {
String title = parkingList.get(position).getPostTitle();
String desc = parkingList.get(position).getPostSubTitle();
holder.t1.setText(Html.fromHtml(title));
if(searchText.length()>0){
//color your text here
int index = desc.indexOf(searchText);
while(index>0){
SpannableStringBuilder sb = new SpannableStringBuilder(desc);
ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); //specify color here
sb.setSpan(fcs, index, index+searchText.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
index = desc.indexOf(searchText,index+1);
}
holder.t2.setText(sb);
}else{
holder.t2.setText(Html.fromHtml(desc));
}
}
@Override
public int getItemCount() {
return parkingList.size();
}
public class VH extends RecyclerView.ViewHolder {
TextView t1, t2;
public VH(View view) {
super(view);
t1 = (TextView) view.findViewById(R.id.list_title);
t2 = (TextView) view.findViewById(R.id.list_desc);
}
}
public void setFilter(List<Post> countryModels,String searchText) {
mCountryModel = new ArrayList<>();
mCountryModel.addAll(countryModels);
this.searchText = searchText;
notifyDataSetChanged();
}
}
并将 onQueryTextChange 设置为
@Override
public boolean onQueryTextChange(String newText) {
final List<Post> filteredModelList = filter(list, newText);
if (filteredModelList.size() > 0) {
myRecAdapter.setFilter(filteredModelList,newText);
return true;
} else {
Toast.makeText(Chapter1.this, "Not Found", Toast.LENGTH_SHORT).show();
return false;
}
}
while(index>**-1**){
SpannableStringBuilder sb = new SpannableStringBuilder(desc);
ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); //specify color here
sb.setSpan(fcs, index, **index** + searchText.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
index = desc.indexOf(searchText,index+1);
}
上一个答案的一个小补充,您应该改为检查索引 >-1,否则第一个文本将不会突出显示。 对于 setSpan , int 端应该是你的索引 + searchText.length().
根据接受的答案,如果字符串值 "Mumbai" 在字符串中出现相同的情况,则 indexOf() 不起作用。所以在这里我使用了 "Pattern" 和 "Matcher" class 来让它工作。 此外,我还添加了“.toLowerCase()”,使字符串具有相同的大小写和查询字符串作为忽略大小写。 如果您不需要忽略查询字符串的大小写,您可以从此代码片段中删除 .toLowerCase()
SpannableStringBuilder sb = new SpannableStringBuilder(desc);
Pattern word = Pattern.compile(query.toLowerCase());
Matcher match = word.matcher(desc.toLowerCase());
while (match.find()) {
ForegroundColorSpan fcs = new ForegroundColorSpan(
ContextCompat.getColor(context, R.color.colorPrimary)); //specify color here
sb.setSpan(fcs, match.start(), match.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
viewHolders.text_view_title.setText(sb);
在 Adapter 的 Bind view holder 上使用 inside class
String title1 = Itemlist.get(position).getN_stall_name().toLowerCase(Locale.getDefault());
holder.title.setText(Itemlist.get(position).getN_stall_name());
if (title1.contains(searchText)) {
int startPos = title1.indexOf(searchText);
int endPos = startPos + searchText.length();
Spannable spanString = Spannable.Factory.getInstance().newSpannable(holder.title.getText());
spanString.setSpan(new ForegroundColorSpan(Color.RED), startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
holder.title.setText(spanString);
}
现在在适配器中创建 public 方法 class
public void updateList(List<GetShop> list, String searchText) {
Itemlist = new ArrayList<>();
Itemlist.addAll(list);
this.searchText = searchText;
notifyDataSetChanged();
}
在您使用搜索视图的地方 Activity 使用搜索视图侦听器
SearchView searchview=findviewbyid(R.id.searchview);
searchview.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
newText = newText.toLowerCase();
ArrayList<GetShop> newList = new ArrayList<>();
for (GetShop userInfo : items) {
String type = userInfo.getN_stall_name().toLowerCase();
if (type.contains(newText)) {
newList.add(userInfo);
}
}
disp_adapter.updateList(newList, newText);
return true;
}
});
/**
* Kotlin Code
* highlight the backgroundText
* searchText -> pass the hightlight that appear in background
* name -> actual string name
* textView -> append the data to widget.
*/
private fun highlightText(
searchText: String,
name: String,
textView: TextView
) {
if (searchText.isNotEmpty() && name.contains(searchText)) {
val sb = SpannableStringBuilder(name)
var index: Int = name.lowercase().indexOf(searchText.lowercase())
while (index >= 0 && index < name.length) {
val fcs = BackgroundColorSpan(Color.rgb(0, 200, 200)) //specify color here
sb.setSpan(
fcs,
index,
index + searchText.length,
Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
index = name.indexOf(searchText, index + 1)
}
textView.text = sb
} else {
textView.text = name
}
}