I have a list view with a search EditText above it. Each row in the list view consists of several texts and pictures. When the user enters a keyword in the search EditText, I want the listview to be updated with the rows that contain the keyword. I have borrowed the implementation in this question as below.
My Custom Adapter:
public class CustomAdapter extends BaseAdapter implements Filterable {
private LayoutInflater inflater;
private ArrayList<CustomObject> objects;//mOriginalValues
private Activity activity;
private ArrayList<Bitmap> newsImageList;
private Context context;
private ArrayList<CustomObject> filteredObjects; // Values to be displayed after filtering
private class ViewHolder {
TextView titleTextView;
TextView dateTextView;
TextView bodyTextView;
ImageView logoView;
ImageView newsImageView;
ProgressBar progressBar;
}
public CustomAdapter(Context context, ArrayList<CustomObject> objects, Activity activity) {
// super(context, R.layout.news_item, objects);
inflater = LayoutInflater.from(context);
this.objects = objects;
this.filteredObjects=objects;
this.activity= activity;
this.newsImageList= new ArrayList<Bitmap>();
this.context= context;
for(int i=0; i<objects.size(); i++)
newsImageList.add(null);
}
@Override
public int getCount() {
return filteredObjects.size();
}
@Override
public CustomObject getItem(int position) {
return filteredObjects.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("IN GET VIEW");
ViewHolder holder = null;
if(convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.news_item, null);
holder.titleTextView = (TextView) convertView.findViewById(R.id.txtTitle);
holder.dateTextView = (TextView) convertView.findViewById(R.id.txtDate);
holder.bodyTextView = (TextView) convertView.findViewById(R.id.txtBody);
holder.logoView= (ImageView) convertView.findViewById(R.id.source_imageView);
holder.newsImageView= (ImageView) convertView.findViewById(R.id.news_imageView);
holder.progressBar= (ProgressBar) convertView.findViewById(R.id.img_progress_bar);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.titleTextView.setText(objects.get(position).getTitle());
holder.dateTextView.setText(objects.get(position).getDate());
holder.bodyTextView.setText(objects.get(position).getBody());
//for the source logo
String uri = "@drawable/"+objects.get(position).getSource_logo()+"_logo";
int imageResource = activity.getResources().getIdentifier(uri, null, activity.getPackageName());
Drawable res =activity.getResources().getDrawable(imageResource);
holder.logoView.setImageDrawable(res);
//the news image
String newsImageURL = objects.get(position).getNewsImageURL();
if(newsImageURL!=null) {
// show The Image in a ImageView
//pre-execute
holder.progressBar.setVisibility(View.VISIBLE);
holder.newsImageView.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.bodyTextView.getLayoutParams();
params.addRule(RelativeLayout.BELOW, R.id.relative_container);
Picasso.with(context).load(newsImageURL).into(holder.newsImageView);//EXECUTE in background
holder.progressBar.setVisibility(View.INVISIBLE);//post-execute
}
else
{
System.out.println("NO IMAGE");
//adjusting the view
holder.progressBar.setVisibility(View.INVISIBLE);
holder.newsImageView.setVisibility(View.GONE);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.bodyTextView.getLayoutParams();
params.addRule(RelativeLayout.BELOW, R.id.txtDate);
}
return convertView;
}
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint,FilterResults results) {
filteredObjects = (ArrayList<CustomObject>) results.values; // has the filtered values
notifyDataSetChanged(); // notifies the data with new filtered values
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
System.out.println("FILTERING with the keyword:"+ constraint);
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
ArrayList<Object> FilteredArrList = new ArrayList<Object>();
if (objects == null) {
objects = new ArrayList<CustomObject>(filteredObjects); // 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 = objects.size();
results.values = objects;
} else {
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < objects.size(); i++) {
System.out.println("loop "+i);
String title = objects.get(i).getTitle();
String date = objects.get(i).getDate();
String body = objects.get(i).getBody();
String source_logo = objects.get(i).getSource_logo();
String newsImageURL = objects.get(i).getNewsImageURL();
System.out.println("title of this item:"+title+", key word:"+constraint.toString());
if (title.contains(constraint.toString()) || date.contains(constraint.toString()) || body.contains(constraint.toString())) {
System.out.println("FOUND!");
FilteredArrList.add(new CustomObject(title,date, body, source_logo, newsImageURL));
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
class CustomObject {
private String title;
private String date;
private String body;
private String source_logo;
private String newsImageURL;
public CustomObject(String prop1, String prop2, String prop3, String prop4, String prop5) {
this.title = prop1;
this.date = prop2;
this.body=prop3;
this.source_logo=prop4;
this.newsImageURL=prop5;
}
public String getTitle() {
return title;
}
public String getDate() {
return date;
}
public String getBody(){
return body;
}
public String getSource_logo()
{
return source_logo;
}
public String getNewsImageURL(){ return newsImageURL; }
}
In my activity:
listView = (ListView) activity.findViewById(R.id.activity_main_listview);//news list view (title, date, body per item)
searchEditText = (EditText) activity.findViewById(R.id.news_search_editText);
// Add Text Change Listener to EditText
searchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Call back the Adapter with current character to Filter
System.out.println("TEXT CHANGED!");
customAdapter.getFilter().filter(s.toString());
//listView.setAdapter(customAdapter);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
//customAdapter.getFilter().filter(s.toString());
}
});
//preparing the news titles, dates, first part of bodies, and source logo.
ArrayList<CustomObject> objects = new ArrayList<CustomObject>();
for (int i = 0; i < titleList.size(); i++)
objects.add(new CustomObject(titleList.get(i), dateList.get(i), reduceTextLetters(textList.get(i), 150) + "...", sourceList.get(i), imageURLList.get(i)));
customAdapter = new CustomAdapter(activity, objects, activity);
listView.setAdapter(customAdapter);
My Activity XML file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<EditText
android:id="@+id/news_search_editText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="البحث"
/>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/activity_main_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/news_search_editText">
<ListView
android:id="@+id/activity_main_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
<Button
android:text="تحديث ↑"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/refresh_button"
android:background="@drawable/button_refresh"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:layout_marginTop="50dp"
/>
</RelativeLayout>
Unfortunately, the behavior of the filtering is not correct. Although I can see in the log that the comparison between the keyword and the content in each row is done correctly, and only matching keywords are being added to the filtered list, the updated list is not right. When I enter a keyword that is in the list, It displays for me the first few items in the list (some of which has nothing to do with the keyword) and removes the rest. When I enter a keyword that is not in the list, it clears the listview. Therefore, the behavior is strange and unexpected. Can someone suggest a solution?