Android Spannable:如何清除颜色?
Android Spannable: how to clear color?
我在 RecyclerView CardView 的 SearchView 上设置了一个 Spannable。如果在 CardView 上找到用户输入的文本,该文本将以红色突出显示。清除 SearchView 行后,我希望 CardView 文本 return 变为默认的黑色。目前,文本被清除并且颜色错误地保持为红色。我尝试在 TextView 上使用 "removeSpan" 但没有成功。 TextView 是 "cardBlankText2"。我在这里错过了什么?
RecyclerView Adapter file
private List<ListItem> mListItems;
...
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_contact_item, parent,false);
final ItemHolder itemHolder = new ItemHolder(view);
return itemHolder;
}
private static class ItemHolder extends RecyclerView.ViewHolder {
private ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.RED);
private TextView cardBlankText2;
private ItemHolder(View itemView) {
super(itemView);
cardBlankText2 = (TextView) itemView.findViewById(R.id.cardBlankText2);
}
public ForegroundColorSpan getForegroundColorSpan(){
return foregroundColorSpan;
}
}
public void setFilter(List<ListItem> listItems, String searchString) {
// Note: the String is to get s.toString() from the Main Activity SearchView.
// Note the plural for listItems.
mListItems = new ArrayList<>();
mListItems.addAll(listItems);
this.searchString = searchString;
notifyDataSetChanged();
}
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
final ListItem listItem = mListItems.get(position);
String todoHighlight = listItem.getTodo().toLowerCase(Locale.getDefault());
String note1Highlight = listItem.getNote1().toLowerCase(Locale.getDefault());
// Set up the logic for the highlighted text
int todoStartPos = todoHighlight.indexOf(searchString);
int todoEndPos = todoStartPos + searchString.length();
int note1StartPos = note1Highlight.indexOf(searchString);
int note1EndPos = note1StartPos + searchString.length();
Spannable spanString2 = Spannable.Factory.getInstance().newSpannable(
itemHolder.cardBlankText2.getText());
if (todoStartPos != -1 && searchString.length() > 0 && todoHighlight.contains(searchString)) {
spanString2.setSpan(new ForegroundColorSpan(Color.RED), todoStartPos, todoEndPos,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
itemHolder.cardBlankText2.setText(spanString2);
}
**else if (searchString.length() == 0) {
spanString2.removeSpan(itemHolder.cardBlankText2.getText());
itemHolder.cardBlankText2.setText(spanString2);
}**
}
MainActivity
public class MainActivity extends AppCompatActivity {
...
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.cardview_menu, menu);
final MenuItem searchItem = menu.findItem(R.id.action_search);
searchItem.setVisible(true);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
final SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
final EditText mSearchEditText = (EditText) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
mSearchEditText.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) {
}
@Override
public void afterTextChanged(Editable s) {
final ArrayList<ListItem> filteredModelList2 = filter(allList, s.toString());
if (!mSearchView.isIconified() && filteredModelList2.size() == 0) {
// re-load the list so the Adapter refreshes the RecyclerView list View.
adapter.clear();
adapter.addAll(allList);
} else if (!mSearchView.isIconified() && filteredModelList2.size() > 0) {
adapter.setFilter(filteredModelList2, s.toString());
mRecyclerView.scrollToPosition(0);
}
});
return super.onCreateOptionsMenu(menu);
}
// Do Search filtering from MainActivity's OnQueryTextChange(String newText)
// The Spannable code is in the onBindVH section for the itemHolders.
public ArrayList<ListItem> filter(List<ListItem> listItems, String query) {
query = query.toLowerCase();
final ArrayList<ListItem> filteredModelList = new ArrayList<>();
for (ListItem listItem : listItems) {
final String text = listItem.getTodo().toLowerCase();
final String text2 = listItem.getNote1().toLowerCase();
final String text3 = listItem.getNote2().toLowerCase();
if (text.contains(query) || text2.contains(query) ||
text3.contains(query)) {
filteredModelList.add(listItem);
}
}
return filteredModelList;
}
方法removeSpan()
是setSpan()
的反向操作,所以你应该给它们传递同一个对象。为此,请将 ForegroundColorSpan
设为 ViewHolder
的字段(见下文)
public void onBindViewHolder(final MyViewHolder itemHolder, int position) {
String todoHighlight = mListItems.get(position).getTodo();
itemHolder.cardBlankText2.setText(todoHighlight);
// Set up the logic for the highlighted text
int todoStartPos = todoHighlight.indexOf(searchString);
int todoEndPos = todoStartPos + searchString.length();
// ... skipped some lines ...
Spannable spanString2 = Spannable.Factory.getInstance().newSpannable(
itemHolder.cardBlankText2.getText());
if (todoStartPos != -1 && searchString.length() > 0 && todoHighlight.contains(searchString)) {
spanString2.setSpan(itemHolder.getForegroundColorSpan(), todoStartPos, todoEndPos,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
itemHolder.cardBlankText2.setText(spanString2);
}
else if (searchString.length() == 0) {
spanString2.removeSpan(itemHolder.getForegroundColorSpan());
itemHolder.cardBlankText2.setText(spanString2);
}
}
ViewHolder:
class MyViewHolder extends RecyclerView.ViewHolder{
private ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.RED);
public MyViewHolder(View itemView){
super(itemView);
}
public ForegroundColorSpan getForegroundColorSpan(){
return foregroundColorSpan;
}
}
编辑
远程调试很困难,所以让我们反过来做,MCVE 风格:请使用下面的代码,运行 如果它有效(比如 "highlighting changes according to search text"),那么你更进一步一路上。
public class Activity8_RecyclerViewGrid extends AppCompatActivity
{
private int counter = 0;
private static String[] searchTexts = new String[]{"rem", "ia", "", "ol" };
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity8__recycler_view);
RecyclerView rcv = (RecyclerView)findViewById(R.id.recyclerView);
rcv.setLayoutManager(new GridLayoutManager(this, 2));
final MyAdapter adapter = new MyAdapter(allMyWords());
rcv.setAdapter(adapter);
final TextView tvSearchText = (TextView)findViewById(R.id.tvSearchText);
Button btnFilter = (Button)findViewById(R.id.btnFilter);
btnFilter.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
String searchString = searchTexts[counter%4];
adapter.setFilter(Activity7_GridViewStuff.allMyBooks(), searchString);
tvSearchText.setText(searchString);
counter++;
}
});
}
public static ArrayList<String> allMyWords()
{
String[] theLoremArray = null;
String LOREM_STRING = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.";
String[] sTemp = LOREM_STRING.split(" ");
StringBuilder sb = new StringBuilder();
theLoremArray = new String[sTemp.length/3];
for (int i = 0; i < (sTemp.length - sTemp.length%3); i++)
{
sb.append(sTemp[i]).append(" ");
if (i%3 == 2)
{
theLoremArray[i/3] = sb.toString();
// Log.d(TAG, "mLoremArray [" + i / 3 + "] = " + sb.toString());
sb.delete(0, sb.length());
}
}
ArrayList<String> words = new ArrayList<>();
for (int i = 0; i < theLoremArray.length; i++)
{
words.add( theLoremArray[i]);
}
return words;
}
class MyAdapter extends RecyclerView.Adapter<MyHolder>{
private List<String> data;
private String searchString = "";
MyAdapter(List<String> data){
this.data = data;
}
public void setFilter(List<String> listItems, String searchString){
// data = new ArrayList<>();
// data.addAll(listItems);
this.searchString = searchString;
notifyDataSetChanged();
}
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity8_grid_cell, null);
return new MyHolder(v);
}
@Override
public void onBindViewHolder(final MyHolder holder, int position)
{
holder.text.setText(data.get(position));
String todoHighlight = data.get(position);
// Set up the logic for the highlighted text
int todoStartPos = todoHighlight.indexOf(searchString);
int todoEndPos = todoStartPos + searchString.length();
Spannable spanString2 = Spannable.Factory.getInstance().newSpannable(holder.text.getText());
if(todoStartPos != -1 && searchString.length() > 0 && todoHighlight.contains(searchString)){
spanString2.setSpan(holder.getForegroundColorSpan(), todoStartPos, todoEndPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if (searchString.length() == 0){
spanString2.removeSpan(holder.getForegroundColorSpan());
}
holder.text.setText(spanString2);
}
@Override
public int getItemCount()
{
return data.size();
}
}
class MyHolder extends RecyclerView.ViewHolder{
private ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.CYAN);
TextView text;
public MyHolder(View itemView){
super(itemView);
text = (TextView) itemView.findViewById(R.id.lorem_text);
}
public ForegroundColorSpan getForegroundColorSpan(){
return foregroundColorSpan;
}
}
}
activity8_recycler_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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="com.example.samples.Activity8_RecyclerViewGrid">
<TextView
android:id="@+id/tvSearchText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
android:textColor="#ff0000" />
<Button android:id="@+id/btnFilter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="SET NEW FILTER"
android:layout_gravity="center_horizontal" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="80dp">
</android.support.v7.widget.RecyclerView>
</FrameLayout>
activity8_grid_cell.xml
<?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">
<TextView
android:id="@+id/lorem_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout>
我在 RecyclerView CardView 的 SearchView 上设置了一个 Spannable。如果在 CardView 上找到用户输入的文本,该文本将以红色突出显示。清除 SearchView 行后,我希望 CardView 文本 return 变为默认的黑色。目前,文本被清除并且颜色错误地保持为红色。我尝试在 TextView 上使用 "removeSpan" 但没有成功。 TextView 是 "cardBlankText2"。我在这里错过了什么?
RecyclerView Adapter file
private List<ListItem> mListItems;
...
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_contact_item, parent,false);
final ItemHolder itemHolder = new ItemHolder(view);
return itemHolder;
}
private static class ItemHolder extends RecyclerView.ViewHolder {
private ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.RED);
private TextView cardBlankText2;
private ItemHolder(View itemView) {
super(itemView);
cardBlankText2 = (TextView) itemView.findViewById(R.id.cardBlankText2);
}
public ForegroundColorSpan getForegroundColorSpan(){
return foregroundColorSpan;
}
}
public void setFilter(List<ListItem> listItems, String searchString) {
// Note: the String is to get s.toString() from the Main Activity SearchView.
// Note the plural for listItems.
mListItems = new ArrayList<>();
mListItems.addAll(listItems);
this.searchString = searchString;
notifyDataSetChanged();
}
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
final ListItem listItem = mListItems.get(position);
String todoHighlight = listItem.getTodo().toLowerCase(Locale.getDefault());
String note1Highlight = listItem.getNote1().toLowerCase(Locale.getDefault());
// Set up the logic for the highlighted text
int todoStartPos = todoHighlight.indexOf(searchString);
int todoEndPos = todoStartPos + searchString.length();
int note1StartPos = note1Highlight.indexOf(searchString);
int note1EndPos = note1StartPos + searchString.length();
Spannable spanString2 = Spannable.Factory.getInstance().newSpannable(
itemHolder.cardBlankText2.getText());
if (todoStartPos != -1 && searchString.length() > 0 && todoHighlight.contains(searchString)) {
spanString2.setSpan(new ForegroundColorSpan(Color.RED), todoStartPos, todoEndPos,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
itemHolder.cardBlankText2.setText(spanString2);
}
**else if (searchString.length() == 0) {
spanString2.removeSpan(itemHolder.cardBlankText2.getText());
itemHolder.cardBlankText2.setText(spanString2);
}**
}
MainActivity
public class MainActivity extends AppCompatActivity {
...
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.cardview_menu, menu);
final MenuItem searchItem = menu.findItem(R.id.action_search);
searchItem.setVisible(true);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
final SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
final EditText mSearchEditText = (EditText) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
mSearchEditText.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) {
}
@Override
public void afterTextChanged(Editable s) {
final ArrayList<ListItem> filteredModelList2 = filter(allList, s.toString());
if (!mSearchView.isIconified() && filteredModelList2.size() == 0) {
// re-load the list so the Adapter refreshes the RecyclerView list View.
adapter.clear();
adapter.addAll(allList);
} else if (!mSearchView.isIconified() && filteredModelList2.size() > 0) {
adapter.setFilter(filteredModelList2, s.toString());
mRecyclerView.scrollToPosition(0);
}
});
return super.onCreateOptionsMenu(menu);
}
// Do Search filtering from MainActivity's OnQueryTextChange(String newText)
// The Spannable code is in the onBindVH section for the itemHolders.
public ArrayList<ListItem> filter(List<ListItem> listItems, String query) {
query = query.toLowerCase();
final ArrayList<ListItem> filteredModelList = new ArrayList<>();
for (ListItem listItem : listItems) {
final String text = listItem.getTodo().toLowerCase();
final String text2 = listItem.getNote1().toLowerCase();
final String text3 = listItem.getNote2().toLowerCase();
if (text.contains(query) || text2.contains(query) ||
text3.contains(query)) {
filteredModelList.add(listItem);
}
}
return filteredModelList;
}
方法removeSpan()
是setSpan()
的反向操作,所以你应该给它们传递同一个对象。为此,请将 ForegroundColorSpan
设为 ViewHolder
的字段(见下文)
public void onBindViewHolder(final MyViewHolder itemHolder, int position) {
String todoHighlight = mListItems.get(position).getTodo();
itemHolder.cardBlankText2.setText(todoHighlight);
// Set up the logic for the highlighted text
int todoStartPos = todoHighlight.indexOf(searchString);
int todoEndPos = todoStartPos + searchString.length();
// ... skipped some lines ...
Spannable spanString2 = Spannable.Factory.getInstance().newSpannable(
itemHolder.cardBlankText2.getText());
if (todoStartPos != -1 && searchString.length() > 0 && todoHighlight.contains(searchString)) {
spanString2.setSpan(itemHolder.getForegroundColorSpan(), todoStartPos, todoEndPos,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
itemHolder.cardBlankText2.setText(spanString2);
}
else if (searchString.length() == 0) {
spanString2.removeSpan(itemHolder.getForegroundColorSpan());
itemHolder.cardBlankText2.setText(spanString2);
}
}
ViewHolder:
class MyViewHolder extends RecyclerView.ViewHolder{
private ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.RED);
public MyViewHolder(View itemView){
super(itemView);
}
public ForegroundColorSpan getForegroundColorSpan(){
return foregroundColorSpan;
}
}
编辑 远程调试很困难,所以让我们反过来做,MCVE 风格:请使用下面的代码,运行 如果它有效(比如 "highlighting changes according to search text"),那么你更进一步一路上。
public class Activity8_RecyclerViewGrid extends AppCompatActivity
{
private int counter = 0;
private static String[] searchTexts = new String[]{"rem", "ia", "", "ol" };
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity8__recycler_view);
RecyclerView rcv = (RecyclerView)findViewById(R.id.recyclerView);
rcv.setLayoutManager(new GridLayoutManager(this, 2));
final MyAdapter adapter = new MyAdapter(allMyWords());
rcv.setAdapter(adapter);
final TextView tvSearchText = (TextView)findViewById(R.id.tvSearchText);
Button btnFilter = (Button)findViewById(R.id.btnFilter);
btnFilter.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
String searchString = searchTexts[counter%4];
adapter.setFilter(Activity7_GridViewStuff.allMyBooks(), searchString);
tvSearchText.setText(searchString);
counter++;
}
});
}
public static ArrayList<String> allMyWords()
{
String[] theLoremArray = null;
String LOREM_STRING = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.";
String[] sTemp = LOREM_STRING.split(" ");
StringBuilder sb = new StringBuilder();
theLoremArray = new String[sTemp.length/3];
for (int i = 0; i < (sTemp.length - sTemp.length%3); i++)
{
sb.append(sTemp[i]).append(" ");
if (i%3 == 2)
{
theLoremArray[i/3] = sb.toString();
// Log.d(TAG, "mLoremArray [" + i / 3 + "] = " + sb.toString());
sb.delete(0, sb.length());
}
}
ArrayList<String> words = new ArrayList<>();
for (int i = 0; i < theLoremArray.length; i++)
{
words.add( theLoremArray[i]);
}
return words;
}
class MyAdapter extends RecyclerView.Adapter<MyHolder>{
private List<String> data;
private String searchString = "";
MyAdapter(List<String> data){
this.data = data;
}
public void setFilter(List<String> listItems, String searchString){
// data = new ArrayList<>();
// data.addAll(listItems);
this.searchString = searchString;
notifyDataSetChanged();
}
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity8_grid_cell, null);
return new MyHolder(v);
}
@Override
public void onBindViewHolder(final MyHolder holder, int position)
{
holder.text.setText(data.get(position));
String todoHighlight = data.get(position);
// Set up the logic for the highlighted text
int todoStartPos = todoHighlight.indexOf(searchString);
int todoEndPos = todoStartPos + searchString.length();
Spannable spanString2 = Spannable.Factory.getInstance().newSpannable(holder.text.getText());
if(todoStartPos != -1 && searchString.length() > 0 && todoHighlight.contains(searchString)){
spanString2.setSpan(holder.getForegroundColorSpan(), todoStartPos, todoEndPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if (searchString.length() == 0){
spanString2.removeSpan(holder.getForegroundColorSpan());
}
holder.text.setText(spanString2);
}
@Override
public int getItemCount()
{
return data.size();
}
}
class MyHolder extends RecyclerView.ViewHolder{
private ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.CYAN);
TextView text;
public MyHolder(View itemView){
super(itemView);
text = (TextView) itemView.findViewById(R.id.lorem_text);
}
public ForegroundColorSpan getForegroundColorSpan(){
return foregroundColorSpan;
}
}
}
activity8_recycler_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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="com.example.samples.Activity8_RecyclerViewGrid">
<TextView
android:id="@+id/tvSearchText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
android:textColor="#ff0000" />
<Button android:id="@+id/btnFilter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="SET NEW FILTER"
android:layout_gravity="center_horizontal" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="80dp">
</android.support.v7.widget.RecyclerView>
</FrameLayout>
activity8_grid_cell.xml
<?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">
<TextView
android:id="@+id/lorem_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout>