I was searching on google and stackover could not find the exact solution.
My problem is that, I have a ArrayList<String> adapter and it has
Gatewick London England
Ory Paris France
Heathrow London England
If user enters "Lon" into AutoCompleteTextView then I have to display line number 1 and 3. Because these have London string.
I tried this link and i pasted code here but it gives warning on line #57
String prefix = constraint.toString().toLowerCase();
PkmnAdapter
public class PkmnAdapter extends ArrayAdapter<String> {
private ArrayList<Pkmn> original;
private ArrayList<Pkmn> fitems;
private Filter filter;
public PkmnAdapter(Context context, int textViewResourceId,
ArrayList<Pkmn> items) {
super(context, textViewResourceId);
this.original = new ArrayList<Pkmn>(items);
this.fitems = new ArrayList<Pkmn>(items);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
Pkmn pkmn = fitems.get(position);
if (pkmn != null) {
TextView tt = (TextView) v.findViewById(R.id.RlabPName);
if (tt != null) {
tt.setText(pkmn.getNAME());
}
}
return v;
}
#Override
public Filter getFilter() {
if (filter == null)
filter = new PkmnNameFilter();
return filter;
}
private class PkmnNameFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
String prefix = constraint.toString().toLowerCase();
if (prefix == null || prefix.length() == 0) {
ArrayList<Pkmn> list = new ArrayList<Pkmn>(original);
results.values = list;
results.count = list.size();
} else {
final ArrayList<Pkmn> list = new ArrayList<Pkmn>(original);
final ArrayList<Pkmn> nlist = new ArrayList<Pkmn>();
int count = list.size();
for (int i = 0; i < count; i++) {
final Pkmn pkmn = list.get(i);
final String value = pkmn.getNAME().toLowerCase();
if (value.startsWith(prefix)) {
nlist.add(pkmn);
}
}
results.values = nlist;
results.count = nlist.size();
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
fitems = (ArrayList<Pkmn>) results.values;
clear();
if (fitems != null) {
int count = fitems.size();
for (int i = 0; i < count; i++) {
Pkmn pkmn = (Pkmn) fitems.get(i);
fitems.add(pkmn);
}
}
}
}
}
MainActivity.java to put adapter
Pkmn[] item = new Pkmn[4];
item[0] = new Pkmn("Gatewick London England");
item[1] = new Pkmn("Ory Paris France");
item[2] = new Pkmn("Heathrow London England");
item[3] = new Pkmn("Ataturk Istanbul Turkey");
ArrayList<Pkmn> list = new ArrayList<Pkmn>(Arrays.asList(item));
MultiAutoCompleteTextView auto = (MultiAutoCompleteTextView) findViewById(R.id.multiAutoCompleteTextView1);
PkmnAdapter adap = new PkmnAdapter(this,android.R.layout.simple_list_item_1, list);
First of all, if you enter "Lon" you should not check if the elements start with "Lon". Probably you need to switch the if statement to:
if (value.contains(prefix)) {
nlist.add(pkmn);
}
Before you perform any filtering in your performFiltering() method check if the constraint is null (Hint: use TextUtils class). If so, then return original data. Therefore you are avoiding NPE. You also need to pay attention to critical points where NPE can be thrown like this one:
if (prefix == null || prefix.length() == 0) { }
Cheers,
Related
I have AutoCompletTextView and i want do apply some customize filetring to it for this i have this code
public class ATCAdapter extends ArrayAdapter<String> implements Filterable {
ArrayList<String> _items = new ArrayList<String>();
ArrayList<String> orig = new ArrayList<String>();
public ATCAdapter(Context context, int resource, ArrayList<String> items) {
super(context, resource, items);
for (int i = 0; i < items.size(); i++) {
orig.add(items.get(i));
}
}
#Override
public int getCount() {
if (_items != null)
return _items.size();
else
return 0;
}
#Override
public String getItem(int arg0) {
return _items.get(arg0);
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if(constraint != null) {
Log.d("Constraints", constraint.toString());
}
FilterResults oReturn = new FilterResults();
/* if (orig == null){
for (int i = 0; i < items.size(); i++) {
orig.add(items.get(i));
}
}*/
String temp;
int counters = 0;
if (constraint != null){
_items.clear();
if (orig != null && orig.size() > 0) {
for(int i=0; i<orig.size(); i++)
{
temp = orig.get(i).toUpperCase();
if(temp.startsWith(constraint.toString().toUpperCase()))
{
_items.add(orig.get(i));
counters++;
}
}
}
Log.d("REsult size:" , String.valueOf(_items.size()));
if(counters != 0)
{
_items.clear();
_items = orig;
}
oReturn.values = _items;
oReturn.count = _items.size();
}
return oReturn;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if(results != null && results.count > 0) {
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}
};
return filter;
}
}
and this is how i am setting adapter for
AutoCompleteTextView autoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);
ArrayAdapter adapter = new ATCAdapter(this, android.R.layout.simple_list_item_1, new ArrayList<>(Arrays.asList(languages)));
autoCompleteTextView.setThreshold(1);
autoCompleteTextView.setAdapter(adapter);
Now the problem is i always get null parameter in performFiletring(), Any reason why it's happening ?
IHMO, your code has a problem at
if(counters != 0)
{
_items.clear();
_items = orig;
}
I suggest you update your code as the following:
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
Log.d("Constraints", constraint.toString());
}
FilterResults oReturn = new FilterResults();
String temp;
int counters = 0;
if (constraint != null && constraint.length() > 0) {
_items.clear();
if (orig != null && orig.size() > 0) {
for (int i = 0; i < orig.size(); i++) {
temp = orig.get(i).toUpperCase();
if (temp.startsWith(constraint.toString().toUpperCase())) {
_items.add(orig.get(i));
counters++;
}
}
}
Log.d("Result size:", String.valueOf(_items.size()));
if (counters == 0) {
_items = new ArrayList<>(orig);
}
oReturn.values = _items;
oReturn.count = _items.size();
} else {
_items = new ArrayList<>(orig);
oReturn.values = _items;
oReturn.count = _items.size();
}
return oReturn;
}
and the constructor:
public ATCAdapter(Context context, int resource, ArrayList<String> items) {
super(context, resource, items);
for (int i = 0; i < items.size(); i++) {
orig.add(items.get(i));
}
_items = new ArrayList<>(orig); // ADD THIS LINE
}
In Arraylist, how can one filtered the arraylist to rows between 2 dates.
For example,
find all the students in arraylist from october to december 2015.
new student ("12/05/2015",john, level2);
new student ("13/05/2015",bisnet, level2);
new student ("5/05/2015",tube, level2);
new student ("7/05/2015",eros, level2);
new student ("4/05/2015",mackay, level2);
new student ("8/05/2015",walnet, level2);
Here is my working. if the date is between the start and ending date. It must display true else its false
public boolean systemDate(Date startDate, Date endDate, String exps) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date expenseLsDate=null;
try {
expenseLsDate = sdf.parse(exps.substring(0,10));
} catch (ParseException e) {}
// Date expenseLsDate = dateFortmat(exps);
//SimpleDateFormat dateFormat = new SimpleDateFormat("dd-mmm-yyyy");//dd-mmm-yyyy hh:mm:ss aa
// Date convertedDate = new Date();
// ArrayList<Expenses> filteredDate = new ArrayList<>();
// Iterator<Expenses> iterator = lList.iterator();
/* for (Expenses temp : lList) {
try {
convertedDate = dateFormat.parse(temp.date_of_entry);
} catch (Exception e) {
e.printStackTrace();
}*/
if ((expenseLsDate.before(endDate)) && (expenseLsDate.after(startDate))) //here "date2" and "date1" must be converted to dateFormat
{
Log.e("valid","true");
//filteredDate.add(convertedDate); // You can use these filtered ArrayList
return true;
}
//}
Log.e("valid","false");
return false;
}
The above method is used to filter my baseadapter.
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
mDisplayedValues = (ArrayList<Expenses>) results.values; // has the filtered values
Log.e("c", String.valueOf(mDisplayedValues.size()));
if (mDisplayedValues.isEmpty()||mDisplayedValues==null){
Log.e("FIELD_EMPTY", "NO_RECORD_FOUND");
}
notifyDataSetChanged(); // notifies the data with new filtered values
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
ArrayList<Expenses> FilteredArrList = new ArrayList<Expenses>();
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<Expenses>(mDisplayedValues); // saves the original data in mOriginalValues
}
/******
*
* If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
* else does the Filtering and returns FilteredArrList(Filtered)
*
*****/
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mOriginalValues.size();
results.values = mOriginalValues;
} else {
/* public Expenses(String expenses_id, String date_of_entry, String name, String category_id,
String amount, String currency_id, String comments,
String fileid, String user_id) */
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mOriginalValues.size(); i++) {
String expName = mOriginalValues.get(i).name;
String expAmount = mOriginalValues.get(i).amount;
String expComment = mOriginalValues.get(i).comments;
// Date startDate = systemUtils.dateFortmat(mOriginalValues.get(i).date_of_entry);
if (date1!=null && date2!=null)
isDateValid = systemUtils.systemDate(date1,date2,mOriginalValues.get(i).date_of_entry);
if (expName.toLowerCase().contains(constraint.toString()) || (expAmount.contains(constraint.toString())
|| (expComment.contains(constraint.toString())&& isDateValid))) {
FilteredArrList.add(
new Expenses(mOriginalValues.get(i).expenses_id,
mOriginalValues.get(i).date_of_entry,
mOriginalValues.get(i).name,
mOriginalValues.get(i).category_id,
mOriginalValues.get(i).amount,
mOriginalValues.get(i).currency_id,
mOriginalValues.get(i).comments,
mOriginalValues.get(i).fileid,
mOriginalValues.get(i).user_id ));
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
I Have a doubt in my autocomplete textview. Here i am trying to filter the listview based on the text that is typed in the autocomplete textview. I am getting the values from json parsing. The auto complete textview shows all its values correctly in the drop down list. But in the listview i see no changes in it.
Below is my adapter code for the listview:
public class EventListAdapter extends BaseAdapter implements Filterable {
public static LayoutInflater inflator = null;
private ArrayList<EventsBean> mOriginalvalues = new ArrayList<EventsBean>();
private ArrayList<EventsBean> mDisplayvalues;
ImageLoader imageloader;
public String datenew,datetime,date_text_value,timenew;
public int date_text,year;
public String time,month,description;
public EventListAdapter( ArrayList<EventsBean> mEventarraylist,Activity activity) {
super();
//this.context = context;
this.mOriginalvalues = mEventarraylist;
inflator =(LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageloader=new ImageLoader(activity.getApplicationContext());
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return mOriginalvalues.size();
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View holder;
holder = inflator.inflate(R.layout.activity_list_items, parent, false);
TextView txt_name = (TextView)holder.findViewById(R.id.textname);
TextView txt_owner_name = (TextView)holder.findViewById(R.id.ownername);
TextView txt_time = (TextView)holder.findViewById(R.id.date);
TextView txt_date = (TextView)holder.findViewById(R.id.txt_date_value);
TextView txt_month = (TextView)holder.findViewById(R.id.txt_month_value);
TextView txt_year = (TextView)holder.findViewById(R.id.txt_year_value);
ImageView userimg = (ImageView)holder.findViewById(R.id.imageView1);
txt_name.setText(mOriginalvalues.get(position).getName());
txt_owner_name.setText(mOriginalvalues.get(position).getOwner_name());
String url = mOriginalvalues.get(position).getSource();
date_text_value = mOriginalvalues.get(position).getStart_time();
parseDateFromString(date_text_value);
txt_date.setText(String.valueOf(date_text));
txt_month.setText(month);
txt_year.setText(String.valueOf(year));
Log.i("TEST", "Date:" + date_text_value);
Log.i("TAG", "Country:" + mOriginalvalues.get(position).getCountry());
imageloader.DisplayImage(url, userimg);
txt_time.setText(timenew);
//userimg.getFitsSystemWindows();
return holder;
}
#SuppressLint("SimpleDateFormat")
public Date parseDateFromString(String aDateString){
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Calendar c = Calendar.getInstance();
Date date= new Date();
try {
date= inputFormat.parse(aDateString);
System.out.println(date);
SimpleDateFormat day = new SimpleDateFormat("dd-MMM-yyyy");
SimpleDateFormat time = new SimpleDateFormat("hh:mm a", Locale.getDefault());
SimpleDateFormat month_date = new SimpleDateFormat("MMM");
c.setTime(inputFormat.parse(aDateString));
System.out.println(day.format(date));
datenew = day.format(date).toString();
date_text = c.get(Calendar.DAY_OF_MONTH);
month = month_date.format(c.getTime());
year = c.get(Calendar.YEAR);
System.out.println("Year = " + c.get(Calendar.YEAR));
System.out.println("Month = " + month);
System.out.println("Day = " + date_text);
System.out.println(time.format(date));
timenew = time.format(date).toString();
} catch (ParseException e) {
Log.i("TAG", "DateFormat Pasring Error:" + e.getMessage());
}
return date;
}
#SuppressLint("DefaultLocale")
#Override
public Filter getFilter() {
// TODO Auto-generated method stub
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
// TODO Auto-generated method stub
mOriginalvalues = (ArrayList<EventsBean>) results.values; // has the filtered values
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
// TODO Auto-generated method stub
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
ArrayList<EventsBean> FilteredArrList = new ArrayList<EventsBean>();
if (mDisplayvalues == null) {
mDisplayvalues = new ArrayList<EventsBean>(mOriginalvalues); // saves the original data in mOriginalValues
System.out.println("Display Value:" + mDisplayvalues.size());
}
/********
*
* If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
* else does the Filtering and returns FilteredArrList(Filtered)
*
********/
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mDisplayvalues.size();
results.values = mDisplayvalues;
} else {
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mDisplayvalues.size(); i++) {
EventsBean data = mDisplayvalues.get(i);
System.out.println("Display Value 2:" + mDisplayvalues.size());
if (data.getLocation_city().toLowerCase().startsWith(constraint.toString())
|| data.getLocation_country().toLowerCase().startsWith(constraint.toString())
|| data.getLocation_state().toLowerCase().startsWith(constraint.toString()))
{
FilteredArrList.add(new EventsBean(mDisplayvalues.get(i).getLocation_city(),mDisplayvalues.get(i).getLocation_country(),
mDisplayvalues.get(i).getLocation_state()));
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
I am getting a nullpointer error everytime i try to write some text in my autocomplete textview in my getCount() of my adapter. I have 3 different values in my autocomplete textview state,country and city which are stored in the arraylist.
I have seen lot of examples on net but they all have autocomplete textview values having only one value but i have three how do i filter the listview with them??
Pleae help me in solving up my nullpointer error.
I could solve my problem by making the changes in the getView(). I was getting NullPointer due to wrong getView() contents. but it works fine now.
I have a ListView of over 1000 items, this list is filterable by a Search function in my Adapter, when clicking on an item it replace the current fragment (The one with the list(A)) with a detail fragment (B). Upon the user pressing back or returning to the previous fragment (B) there are duplicate list items.
Any ideas??
public class HallsInStateAdapter extends BaseAdapter implements Filterable {
private Activity activity;
private ArrayList<HashMap<String, String>> data;
private ArrayList<HashMap<String, String>> orginalData;
private static LayoutInflater inflater=null;
private Filter hallFilter;
public HallsInStateAdapter(Activity a, ArrayList<HashMap<String, String>> d) {
activity = a;
setData(d);
this.orginalData = d;
}
public int getCount() {
return getData().size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
vi = inflater.inflate(R.layout.hall_list_view, null);
TextView hallName = (TextView)vi.findViewById(R.id.hallName);
TextView hallSuburb = (TextView)vi.findViewById(R.id.hallAddress);
ImageView hallFavIcon = (ImageView)vi.findViewById(R.id.hallFavouriteIcon);
HashMap<String, String> listData = new HashMap<String, String>();
listData = getData().get(position);
String address = listData.get(HallsInStateFragment.KEY_HALL_ADDRESS);
String suburb = listData.get(HallsInStateFragment.KEY_SUBURB);
String state = listData.get(HallsInStateFragment.KEY_STATE);
String objectID = listData.get(HallsInStateFragment.KEY_OBJECTID);
hallName.setText(address);
hallSuburb.setText(suburb + ", " + state);
hallFavIcon.setVisibility(View.INVISIBLE);
boolean isFavourite = false;
DatabaseHandler db = new DatabaseHandler(activity);
List<Favourite> favs = db.getAllFavourites();
for (int i = 0; i < favs.size(); i++){
if(favs.get(i).getObjectId().equals(objectID)){
isFavourite = true;
break;
}
else {
isFavourite = false;
}
}
db.close();
if (isFavourite == true){
hallFavIcon.setVisibility(View.VISIBLE);
}
return vi;
}
public android.widget.Filter getFilter() {
if (hallFilter == null)
hallFilter = new HallFilter();
return hallFilter;
}
public ArrayList<HashMap<String, String>> getData() {
return data;
}
public void setData(ArrayList<HashMap<String, String>> data) {
this.data = data;
}
private class HallFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
// We implement here the filter logic
if (constraint == null || constraint.length() == 0) {
// No filter implemented we return all the list
results.values = orginalData;
results.count = orginalData.size();
}
else {
// We perform filtering operation
final ArrayList<HashMap<String, String>> filteredLocations = new ArrayList<HashMap<String, String>>();
for (int i = 0; i < orginalData.size(); i ++) {
HashMap<String, String> halls = new HashMap<String, String>();
halls.put(HallsInStateFragment.KEY_OBJECTID, orginalData.get(i).get(HallsInStateFragment.KEY_OBJECTID));
String name = orginalData.get(i).get(HallsInStateFragment.KEY_NAME);
String prefix = orginalData.get(i).get(HallsInStateFragment.KEY_PREFIX);
String address = null;
if(prefix == null || prefix.length() == 0){
address = name;
}
else {
address = prefix + " " + name;
}
halls.put(HallsInStateFragment.KEY_NAME, name);
halls.put(HallsInStateFragment.KEY_PREFIX, prefix);
halls.put(HallsInStateFragment.KEY_HALL_ADDRESS, address);
halls.put(HallsInStateFragment.KEY_STREET, orginalData.get(i).get(HallsInStateFragment.KEY_STREET));
halls.put(HallsInStateFragment.KEY_SUBURB, orginalData.get(i).get(HallsInStateFragment.KEY_SUBURB));
halls.put(HallsInStateFragment.KEY_STATE, orginalData.get(i).get(HallsInStateFragment.KEY_STATE));
halls.put(HallsInStateFragment.KEY_POSTCODE, orginalData.get(i).get(HallsInStateFragment.KEY_POSTCODE));
halls.put(HallsInStateFragment.KEY_LATITUDE, orginalData.get(i).get(HallsInStateFragment.KEY_LATITUDE));
halls.put(HallsInStateFragment.KEY_LONGITUDE, orginalData.get(i).get(HallsInStateFragment.KEY_LONGITUDE));
halls.put(HallsInStateFragment.KEY_TYPE, orginalData.get(i).get(HallsInStateFragment.KEY_TYPE));
String query = constraint.toString().toLowerCase();
String suburb = orginalData.get(i).get(HallsInStateFragment.KEY_SUBURB);
if(name == null || name.length() == 0){
Log.e("SGL", "NULL");
}
else {
if (name.toLowerCase().contains(query) || suburb.toLowerCase().contains(query)) {
Log.i("SGL-QUERY", query);
filteredLocations.add(halls);
}
}
results.values = filteredLocations;
results.count = filteredLocations.size();
}
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//Now we have to inform the adapter about the new list filtered
if (results.count == 0)
notifyDataSetInvalidated();
else {
setData((ArrayList<HashMap<String, String>>) results.values);
notifyDataSetChanged();
}
}
}
}
Yes, before filling your list array, clear your arraylist so last added data will be void and eveytime it will load new data in your arraylist.
Note: An arraylist which you filling before passing to adapter to fill your listview. Hope it make sense
I have a ListView with Strings. With the below code I can highlight search results, but the user must type the words to search case sensitive. How can I implement a none - case sensitive highlighting of search results for example like the native Android Contact search?
Here is my code for Highlighting. I extend the ArrayAdapter and implement customized filter to get the string to search. In the getView method I check if my String in ListView contains the prefixString and highlight it.
public class HighlightListAdapter extends ArrayAdapter {
ArrayList<String> objects;
final Object mLock =new Object();
private ArrayList<String> mOriginalValues;
private ArrayFilter filter;
private String prefixString;
public AuthorsListAdapter(Context context, int textViewResourceId, ArrayList<String> objects) {
super(context, textViewResourceId, objects);
this.objects = objects;
}
class ViewHolder{
TextView author;
}
public View getView(final int position, View convertView, ViewGroup parent){
// assign the view we are converting to a local variable
View v = convertView;
ViewHolder holder = null;
// first check to see if the view is null. if so, we have to inflate it.
// to inflate it basically means to render, or show, the view.
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (v == null) {
holder = new ViewHolder();
v = inflater.inflate(R.layout.author_list_item, null);
holder.author =(TextView) v.findViewById(R.id.author_list_item_text);
v.setTag(holder);
}else{
holder = (ViewHolder) v.getTag();
}
final String author = objects.get(position);
if (author != null) {
holder.author.setText(author);
if(prefixString !=null && prefixString.length()>1){
String s = author;
**if(s.contains(prefixString)){
String rep = s.replace(prefixString, "<b><font color=#2825A6>"+ prefixString+ "</font></b>");
holder.author.setText(Html.fromHtml(rep));
}** // higlight
}
}
return v;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return objects.size();
}
#Override
public Filter getFilter() {
// TODO Auto-generated method stub
if(filter == null){
filter =new ArrayFilter();
}
return filter;
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return this.objects.get(position);
}
private class ArrayFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<String>(objects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<String> list;
synchronized (mLock) {
list = new ArrayList<String>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
**prefixString = prefix.toString();** // get string to search
ArrayList<String> values;
synchronized (mLock) {
values = new ArrayList<String>(mOriginalValues);
}
final int count = values.size();
final ArrayList<String> newValues = new ArrayList<String>();
for (int i = 0; i < count; i++) {
final String value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
objects = (ArrayList<String>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
}
This what I use :
Every occurence is replaced (not only prefix)
Case and accent are ignored while searching but retained in the result.
It uses directly SpannableString, which you can use in setText(). I believe it's more efficient than using an intermediate html step.
.
public 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();
int start = normalizedText.indexOf(search);
if (start < 0) {
// not found, nothing to to
return originalText;
} else {
// highlight each appearance in the original text
// while searching in normalized text
Spannable highlighted = new SpannableString(originalText);
while (start >= 0) {
int spanStart = Math.min(start, originalText.length());
int spanEnd = Math.min(start + search.length(), originalText.length());
highlighted.setSpan(new BackgroundColorSpan(<background_color>), spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
start = normalizedText.indexOf(search, spanEnd);
}
return highlighted;
}
}
The accepted answer is nice. But you can do it by a single line of code. What I've done in my case to avoid the case sensitive issue is:
Spannable sb = new SpannableString(originalText);
sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), originalText.toLowerCase().indexOf(query.toLowerCase()),
originalText.toLowerCase().indexOf(query.toLowerCase()) + query.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
result.setText(sb);
Hope it might help!
Note: Here 'query' is the part of the string that you want to highlight.
Simple & Advanced Search Highlighting Example [Case Insensitive Order]
1. Simple Search (Html):
public static void setSearchTextHighlightSimpleHtml(TextView textView, String fullText, String searchText) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fullText = fullText.replaceAll("(?i)(" + searchText + ")", "<span style=\"background-color:#FCFF48;\"><b><big><font color='#a10901'>$1</font></big></b></span>");
textView.setText(Html.fromHtml(fullText, Html.FROM_HTML_MODE_LEGACY), TextView.BufferType.SPANNABLE);
} else {
fullText = fullText.replaceAll("(?i)(" + searchText + ")", "<b><big><font color='red'>$1</font></big></b>");
textView.setText(Html.fromHtml(fullText), TextView.BufferType.SPANNABLE);
}
} catch (Exception e) {
textView.setText(fullText);
}
}
2. Simple Search (Spannable):
public static void setSearchTextHighlightSimpleSpannable(TextView textView, String fullText, String searchText) {
// highlight search text
if (null != searchText && !searchText.isEmpty()) {
SpannableStringBuilder wordSpan = new SpannableStringBuilder(fullText);
Pattern p = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(fullText);
while (m.find()) {
int wordStart = m.start();
int wordEnd = m.end();
// Now highlight based on the word boundaries
ColorStateList redColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{0xffa10901});
TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, redColor, null);
wordSpan.setSpan(highlightSpan, wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
wordSpan.setSpan(new BackgroundColorSpan(0xFFFCFF48), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
wordSpan.setSpan(new RelativeSizeSpan(1.25f), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
textView.setText(wordSpan, TextView.BufferType.SPANNABLE);
} else {
textView.setText(fullText);
}
}
3. Advanced Search (Spannable):
public static void setAdvancedSearch(TextView textView, String fullText, String searchText) {
if (searchText.length() == 0) {
textView.setText(fullText);
return;
}
final String searchBoundary = " \n()ред.,;?-+!";
char[] boundaries = searchBoundary.toCharArray();
// highlight search text
if (isNotEquals(searchText, boundaries)) {
SpannableStringBuilder wordSpan = new SpannableStringBuilder(fullText);
Pattern p = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(fullText);
while (m.find()) {
int wordStart = m.start();
while (wordStart >= 0 && isNotEquals(fullText.charAt(wordStart), boundaries)) {
--wordStart;
}
wordStart = wordStart + 1;
int wordEnd = m.end();
while (wordEnd < fullText.length() && isNotEquals(fullText.charAt(wordEnd), boundaries)) {
++wordEnd;
}
setWordSpan(wordSpan, wordStart, wordEnd);
}
textView.setText(wordSpan, TextView.BufferType.SPANNABLE);
} else {
textView.setText(fullText);
}
}
private static boolean isNotEquals(char charAt, char[] boundaries) {
return isNotEquals(String.valueOf(charAt), boundaries);
}
private static boolean isNotEquals(String searchText, char[] boundaries) {
for (char boundary : boundaries) {
boolean equals = searchText.equals(String.valueOf(boundary));
if (equals) return false;
}
return true;
}
private static void setWordSpan(SpannableStringBuilder wordSpan, int wordStart, int wordEnd) {
// Now highlight based on the word boundaries
ColorStateList redColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{0xffa10901});
TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, redColor, null);
wordSpan.setSpan(highlightSpan, wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
wordSpan.setSpan(new BackgroundColorSpan(0xFFFCFF48), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
wordSpan.setSpan(new RelativeSizeSpan(1.25f), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
First, your code
if(s.contains(prefixString)){
String rep = s.replace(prefixString, "<b><font color=#2825A6>"+ prefixString+ "</font></b>");
holder.author.setText(Html.fromHtml(rep));
}
is not good. You should use String.startsWith to check if the start of s equals to prefixString. Your actual code works, but it checks presence of prefixString in s, but doesn't care about its position.
For having case insensitive search, you can use String.toLowerCase or String.toUpperCase on both strings when checking presence of prefixString. Case will be ignored.
if(s.toLowerCase().startsWith(prefixString.toLowerCase())){
String rep = "<b><font color=#2825A6>" + prefixString + "</font></b>" + s.substring(prefixString.length());
holder.author.setText(Html.fromHtml(rep));
}