Using Multiple Unknown Count of Identifiers to query RealmDB - android

I have a DB named RealmItem and I have all users have territories they are attached to. Which is n size unknown. Every item has a field called territory. Now, is there a way to query Realm to find all Item Records that has any of the territoryID attached to the user. The reason I'm in need of this is because, I pipe a AutoCompleteTextView to a filtering adapter that looks like this:
public class ItemFilterAdapter extends BaseAdapter implements Filterable {
private static final String TAG = ItemFilterAdapter.class.getSimpleName();
private Context mContext;
private List<RealmItem> mResult = new ArrayList<>();
private LayoutInflater inflater;
private boolean needsFilter = false;
public ItemFilterAdapter(Context mContext) {
this.mContext = mContext;
}
public void setNeedsFilter(boolean needsFilter) {
this.needsFilter = needsFilter;
}
#Override
public int getCount() {
return mResult.size();
}
#Override
public Object getItem(int position) {
return mResult.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (view == null)
view = inflater.inflate(R.layout.item_update_autocomplete, parent, false);
RealmItem item = mResult.get(position);
TextView updateName = (TextView) view.findViewById(R.id.update_name);
TextView updateAddress = (TextView) view.findViewById(R.id.update_address);
if (item != null) {
updateName.setText(item.getName());
try {
JSONObject obj = new JSONObject(item.getPayload());
String address = obj.getString("address");
updateAddress.setText(address);
} catch (Exception e) {
e.printStackTrace();
}
}
return view;
}
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
return null;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults filterResults) {
if (constraint != null) {
mResult = filterDealers(constraint.toString());
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
}
#NonNull
private List<RealmItem> filterDealers(String query) {
Realm mRealm = RealmUtils.getRealmInstance(mContext);
if (!needsFilter) {
return mRealm.where(RealmItem.class)
.contains("name", query, Case.INSENSITIVE)
.equalTo("id", [array of Ids]) <-- This is where I need it
.findAll();
} else {
return mRealm.where(RealmItem.class)
.contains("name", query, Case.INSENSITIVE)
.notEqualTo("activeStatus", "inactive")
.equalTo("id", [array of Ids]) <-- This is where I need it
.findAll();
}
}
}
More like looking for something like this .equalTo("id", [array of Ids]) <-- This is where I need it or anything nice to make filtering seemlessly smooth 😉

Aren't you basically just looking for the in() operator, added in 1.1.0?
public RealmQuery<E> in(
String fieldName, Integer[] values)

Related

Android listview error when tying to search using SearchView

The list generating successfully but when i am trying to filter using search view then getting this error. just after type one later got this error.
Getting this Error
Process: com.galaxybd.eams, PID: 7836
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
at com.galaxybd.eams.test.AD.getCount(AD.java:35)
at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:811)
at android.widget.AbsListView$AdapterDataSetObserver.onChanged(AbsListView.java:6140)
at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
at com.galaxybd.eams.test.AD$ValueFilter.publishResults(AD.java:102)
at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:282)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
The list generating successfully but when i am trying to filter using search view then getting this error. just after type one later got this error.
Adapter
public class AD extends BaseAdapter implements Filterable {
private Context context;
private List<DM> beanList;
private LayoutInflater inflater;
List<DM> mStringFilterList;
ValueFilter valueFilter;
public AD(Context context, List<DM> beanList) {
// TODO Auto-generated constructor stub
this.context = context;
this.beanList = beanList;
mStringFilterList = beanList;
}
#Override
public int getCount() {
return beanList.size();
}
#Override
public Object getItem(int i) {
return beanList.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (inflater == null) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (view == null) {
view = inflater.inflate(R.layout.row_tlist, null);
}
TextView tvName= view.findViewById(R.id.tvName);
TextView tvDesig= view.findViewById(R.id.tvDesig);
TextView tvCompany= view.findViewById(R.id.tvCompany);
DM bean = beanList.get(i);
tvName.setText(bean.getDmName());
tvDesig.setText(bean.getDmDesig());
tvCompany.setText(bean.getDmCom());
return view;
}
#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) {
ArrayList<DM> filterList = new ArrayList<DM>();
for (int i = 0; i < mStringFilterList.size(); i++) {
if ((mStringFilterList.get(i).getDmName().toUpperCase()).contains(constraint.toString().toUpperCase())) {
DM bean = new DM(mStringFilterList.get(i).getDmName(), mStringFilterList.get(i).getDmDesig(), mStringFilterList.get(i).getDmCom());
filterList.add(bean);
}
}
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) {
beanList = (ArrayList<DM>) results.values;
notifyDataSetChanged();
}
}}
The list generating successfully but when i am trying to filter using search view then getting this error. just after type one later got this error.
Data Model
public class DM {
String dmName,dmDesig,dmCom;
public DM(String dmName, String dmDesig, String dmCom) {
this.dmName = dmName;
this.dmDesig = dmDesig;
this.dmCom = dmCom;
}
public String getDmName() {
return dmName;
}
public void setDmName(String dmName) {
this.dmName = dmName;
}
public String getDmDesig() {
return dmDesig;
}
public void setDmDesig(String dmDesig) {
this.dmDesig = dmDesig;
}
public String getDmCom() {
return dmCom;
}
public void setDmCom(String dmCom) {
this.dmCom = dmCom;
}}
The list generating successfully but when i am trying to filter using search view then getting this error. just after type one later got this error.
Main Activity
public class listtest extends AppCompatActivity {
private SearchView tSearchView;
private ListView tList;
private ArrayList<DM> sARRAYLIST=new ArrayList<>();
private AD ad;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listtest);
tSearchView=findViewById(R.id.tSearchView);
tList=findViewById(R.id.tList);
new LIST_GENARATE().execute();
tSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
return false;
}
#Override
public boolean onQueryTextChange(String s) {
if(ad!=null){
ad.getFilter().filter(s);
}
return false;
}
});
}
//AsynTask
private class LIST_GENARATE extends AsyncTask<String, String, String>
{
DM dm;
String qNAME =null;
String qDESIG =null;
String qCOM =null;
private String msg;
final Animation myRotation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.animationfile);
protected void onPreExecute()
{
}
#Override
protected String doInBackground(String... params) {
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection("jdbc:oracle:thin:#172.30.0.16:1521:GALAXYBD", "GIM", "GALAXYGIM");
Statement st = con.createStatement();
ResultSet lv= st.executeQuery("SELECT INITCAP(CUSTODIAN) NAM,(SELECT INITCAP(EI.DESIG_NAME) FROM GHRMS.EMP_INFO EI WHERE EI.EMP_ID=DD.CUS_ID) DESIG,(SELECT INITCAP(EI.COMPANY_NAME) FROM GHRMS.EMP_INFO EI WHERE EI.EMP_ID=DD.CUS_ID) COMPAN,CUS_ID,(SELECT EI.EMP_CODE FROM GHRMS.EMP_INFO EI WHERE EI.EMP_ID=DD.CUS_ID) EMP_CODE,COUNT(ASSET_ID) AID FROM GIM.DASHBOARD_DATA DD GROUP BY CUSTODIAN,CUS_ID ORDER BY COUNT(ASSET_ID) DESC");
while (lv.next())
{
dm=new DM(qNAME,qDESIG,qCOM);
dm.setDmName(lv.getString(1));
dm.setDmDesig(lv.getString(2));
dm.setDmCom(lv.getString(3));
sARRAYLIST.add(dm);
}
con.close();
st.close();
lv.close();
}
catch (Exception e) {
msg=e.toString();
}
return null;
}
protected void onPostExecute(String result)
{
if (TextUtils.isEmpty(msg))
{
ad=new AD(getApplicationContext(),sARRAYLIST);
tList.setAdapter(ad);
}else
{
SuperToast.create(listtest.this, "Network Error\n"+msg, SuperToast.Duration.EXTRA_LONG, Style.getStyle(Style.RED, SuperToast.Animations.FLYIN)).show();
}
}
}}

AutoCompleteTextView in android using Firebase and Filterable inteface

I am working on an android app where I am going to store school names under one node for e.g. schools. I want to implement Autocomplete functionality for the schoolnames, .i.e when user starts typing school name he/she will see the schoolnames starting/matching the particular condition(top 5 records matching the condition can be shown). Assume that each schoolname is a String.
public class SchoolsAutoCompleteAdapter extends BaseAdapter implements Filterable {
private static final int MAX_RESULTS = 10;
private Context mContext;
FirebaseDatabase database;
DatabaseReference myRef;
private List<School> schools = new ArrayList<School>();
public SchoolsAutoCompleteAdapter(Context mContext) {
this.mContext = mContext;
database=FirebaseDatabase.getInstance();
myRef=database.getReference("mschools");
schools=new ArrayList<>();
}
#Override
public int getCount() {
return schools.size();
}
#Override
public School getItem(int position) {
return schools.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.school_autocomplete_list_row, parent, false);
}
((TextView) convertView.findViewById(R.id.autocomplete_email)).setText(getItem(position).getEmail());
((TextView) convertView.findViewById(R.id.autocomplete_name)).setText(getItem(position).getName());
return convertView;
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
schools = findSchools(constraint.toString());
// Assign the data to the FilterResults
filterResults.values = schools;
filterResults.count = schools.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}};
return filter;
}
private List<School> findSchools(String studentUsername) {
final List<School> schools=new ArrayList<>();
Query abc= myRef.orderByChild("schoolname").startAt(studentUsername).endAt(studentUsername+"\uf8ff").limitToFirst(5);
abc.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot childDataSnapshot : dataSnapshot.getChildren()) {
//schools.add(childDataSnapshot.getValue(School.class));
School s=childDataSnapshot.getValue(School.class);
schools.add(new School(s.getEmail(),s.getName()));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return schools;
}
}
As per the code return schools line is executing before the onDataChange method as firebase executes asynchronously .So it is returning empty record.
Some relational database can be used to implement this functionality, but I don't want to add extra database to implement just this functionality. If I keep breakpoint at return schools; line, I am able to see the result. Can I implement this scenario using Firebase or am I missing something?

Realm access from incorrect thread error in recyclerview

i want to show the result of a realm query in my recycler view, using a Filter.
So I've implemented this code:
public class ListAirportFragment extends Fragment
{
Realm realm;
List<AirportR>airports = new ArrayList<>();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_list_airport, container, false);
RealmConfiguration defaultConfig = new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build();
realm = Realm.getInstance(defaultConfig);
RecyclerView recyclerView = (RecyclerView)rootView.findViewById(R.id.recyclerview);
SearchView searchView = (SearchView)rootView.findViewById(R.id.searchview);
recyclerView.setHasFixedSize(true);
//final RealmResults<AirportR> airps = realm.where(AirportR.class).findAllAsync();
final RealmResults<AirportR> airps = realm.where(AirportR.class).findAll();
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
final AirportAdapter adapter = new AirportAdapter(airports,getActivity());
recyclerView.setAdapter(adapter);
realm.executeTransaction(new Realm.Transaction()
{
#Override
public void execute(Realm realm)
{
for (int i = 0; i<airps.size();i++)
{
airports.add(airps.get(i));
adapter.notifyDataSetChanged();
}
}
});
/* airps.addChangeListener(new RealmChangeListener<RealmResults<AirportR>>() {
#Override
public void onChange(RealmResults<AirportR> element)
{
for (int i = 0; i<element.size();i++)
{
airports.add(element.get(i));
adapter.notifyDataSetChanged();
}
}
}); */
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener()
{
#Override
public boolean onQueryTextSubmit(String query)
{
return false;
}
#Override
public boolean onQueryTextChange(String newText)
{
adapter.getFilter().filter(newText);
return true;
}
});
return rootView;
}
}
and this is my adapter:
public class AirportAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable
{
private List<AirportR>originalAirports;
private List <AirportR>listAirports;
private Context context;
public AirportAdapter(List<AirportR> airports, Context context)
{
this.originalAirports = airports;
this.listAirports = airports;
this.context = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.airport_show, parent,false);
AirportClass holder = new AirportClass(view);
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
AirportR airportR = originalAirports.get(position);
String country = airportR.getIsoCountry().toLowerCase()+".png";
int imgID = context.getResources().getIdentifier(country , "drawable", context.getPackageName());
AirportClass mHolder = (AirportClass)holder;
mHolder.image.setImageResource(imgID);
mHolder.country.setText(airportR.getIsoCountry());
mHolder.name.setText(airportR.getName());
}
#Override
public int getItemCount()
{
return listAirports.size();
}
#Override
public Filter getFilter()
{
return new Filter()
{
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
List<AirportR> filteredResults = null;
if (constraint.length() == 0)
{
filteredResults = originalAirports;
}
else
{
filteredResults = getFilteredResults(constraint.toString().toLowerCase());
}
Log.d("const",String.valueOf(constraint.length()));
FilterResults results = new FilterResults();
results.values = filteredResults;
return results;
}
protected List<AirportR> getFilteredResults(String constraint)
{
List<AirportR> results = new ArrayList<>();
for (AirportR item : originalAirports)
{
if (item.getName().toLowerCase().contains(constraint))
{
results.add(item);
}
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
listAirports = (List<AirportR>)results.values;
AirportAdapter.this.notifyDataSetChanged();
}
};
}
private class AirportClass extends RecyclerView.ViewHolder
{
TextView name, country;
ImageView image;
public AirportClass(View itemView)
{
super(itemView);
name = (TextView)itemView.findViewById(R.id.name);
country = (TextView)itemView.findViewById(R.id.country);
image = (ImageView)itemView.findViewById(R.id.imageView);
}
}
}
the problem is: when I try to put something to searchView it crashes and, with the debugger, I see this error message:
W/Filter: An exception occured during performFiltering()!
java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
What's the problem? Before I've user findAllAsync() method to get the result and after I use simple findAll() but I get the same error message...does it not the same thread? Or there are other problems in the filter?
Thanks
The problem is that you query for airports on the UI thread, and later tries to access these same objects in another thread. This is not allowed.
The method performFiltering runs on a worker thread. When it calls getFilteredResults, it tries to access a property(item.getName()) of a Realm Object that was queried on another thread(UI). As the error says, Realm objects can only be accessed on the thread they were created.
If you want to be able to access the objects you queried for in another thread, you might want to get a deep copy of them, so that they are detached from Realm. In your example, after querying for airports, you could use the method copyFromRealm() to get a list of regular Airport objects and use it from thereon.

How to convert autocomplete textview selected item into Place Object

I have an autocomplete textview in my app that would let user enter address and the textview uses Places Api to show them different places that start with those letters the user is using. I implemented the Places Api and the user is able to see places but when they select a place I would like to retrieve that place and use it according to my needs(like retrieving the LatLng, and stuff like that) but the place object is String object and I don't know how to retrieve it as Place or convert the String object to Place object. I tried the following so far to no avail.
This is my adapter class.
class GooglePlacesAutocompleteAdapter extends ArrayAdapter implements Filterable {
private ArrayList resultList;
public GooglePlacesAutocompleteAdapter(Context context, int list, int textViewResourceId) {
super(context, list, textViewResourceId);
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public String getItem(int index) {
return resultList.get(index).toString();
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
resultList = autocomplete(constraint.toString());
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return filter;
}
}
msg.setAdapter(new GooglePlacesAutocompleteAdapter(this, R.layout.search_results_list_item, R.id.tvSearchResultItem));
msg.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String selectedPlace = (String) parent.getItemAtPosition(position);
Place pl = new Place() {
#Override
public String getId() {
return null;
}
#Override
public List<Integer> getPlaceTypes() {
return null;
}
#Override
public CharSequence getAddress() {
return null;
}
#Override
public Locale getLocale() {
return null;
}
#Override
public CharSequence getName() {
return selectedPlace;
}
#Override
public LatLng getLatLng() {
return null;
}
#Override
public LatLngBounds getViewport() {
return null;
}
#Override
public Uri getWebsiteUri() {
return null;
}
#Override
public CharSequence getPhoneNumber() {
return null;
}
#Override
public float getRating() {
return 0;
}
#Override
public int getPriceLevel() {
return 0;
}
#Override
public Place freeze() {
return null;
}
#Override
public boolean isDataValid() {
return true;
}
};
LatLng selectedltlng = getSelectedLatLng(pl);
Log.e("selected", selectedltlng.toString());
//Place selectedPlace = Places.GeoDataApi.getPlaceById(mGoogleApiClient, );
//Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
private LatLng getSelectedLatLng(Place placeStringe) {
LatLng placeltlg = placeStringe.getLatLng();
Log.e("selcete", placeltlg.toString());
return placeltlg;
}
});
But this throws a null pointer exception.
I also tried to cast the string object as Object like this
Object selectedPlace = (Object) parent.getItemAtPosition(position);
Place plac = (Place)selectedPlace;
But this throws cast exception. How can I do that in an efficient way?
I made a city selector a week ago. It use custom extended AutoCompleteTextView widget. May be this helps you. This widget allows user to enter chars (grey color) and then show list of matched cities. User select a city from the list (it became black) and then selected city saved in cityData object and could be retrived by .getCity() call.
Here is the code:
public class CityAutoCompleteEditText extends AutoCompleteTextView implements View.OnFocusChangeListener, TextWatcherAdapter.TextWatcherListener {
// City object (code, name, type, level and so on)
private OKTMOUnit cityData;
// One of the constructors. You need to implement all with init() inside
public CityAutoCompleteEditText(Context context) {
super(context);
init(context, null);
}
// Initial data setter
public void setCity(OKTMOUnit city) {
setText(city.toString());
setTextColor(resource_black);
cityData = city;
}
// Data getter
public OKTMOUnit getCity() {
return cityData;
}
private void init(Context context, AttributeSet attrs) {
......
super.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// On click - set color black and init cityData
cityData = (OKTMOUnit) getAdapter().getItem(position);
setTextColor(resource_black);
}
});
.....
}
#Override
public void onTextChanged(EditText view, String text) {
// Text changed by hand - grey it and clear cityData
cityData = null;
setTextColor(resource_grey);
}
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (cityData == null) {
setText("");
}
}
}
And adapter:
public class CityAutoCompleteAdapter extends BaseAdapter implements Filterable {
private static final int MAX_RESULTS = 20;
private Context mContext;
private ArrayList<OKTMOUnit> mData;
public CityAutoCompleteAdapter(Context context) {
mData = new ArrayList<>();
mContext = context;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public OKTMOUnit getItem(int index) {
return mData.get(index);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.autocomplete_dropdown_item, parent, false);
}
if (convertView != null) {
((TextView) convertView.findViewById(R.id.autoCompleteCity)).setText(getItem(position).getFullCity());
((TextView) convertView.findViewById(R.id.autoCompleteRegion)).setText(getItem(position).getFullRegion());
}
return convertView;
}
}
ADD: I removed public Filter getFilter() {} code from adapter to simplify it.
try this It is done with Model Class u can get place name,address,lat,long I found very useful http://coderzpassion.com/android-working-google-places-api/

Android: Custom Filter not showing results, .clear() and .addAll() are there

For my custom Adapter, I want to be able to filter the items in a ListView. I implemented Filterable, and created a Filter.
Here's my publishResults:
#Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
MainActivity.this.filteredPetList.clear();
MainActivity.this.filteredPetList.addAll((ArrayList<Pet>)results.values);
notifyDataSetChanged();
}
If I check filteredPetList after
MainActivity.this.filteredPetList.addAll((ArrayList<Pet>)results.values);
, results show just fine, but the adapter won't update the view.
Any ideas what I'm doing wrong? I'm probably doing something stupid again.
-Edit-
In my adapter I overrode notifyDataSetChanged, to see if it is actually called. It says:
#Override
public void notifyDataSetChanged()
{
System.out.println("Notified...");
super.notifyDataSetChanged();
}
I do get a result whenever I try to filter, notifyDataSetChanged gets called. I also checked if my filteredPetList was actually being changed outside the Filter, and it is.
It's just that the views don't update for some reason...
-Edit- Added full Adapter code:
private class PetAdapter extends BaseAdapter implements Filterable
{
private final Object lock = new Object();
List<Row> rows;
ArrayList<Integer> petIds;
boolean clickable;
PetFilter petFilter;
public PetAdapter(ArrayList<Pet> petList, boolean clickable)
{
this.clickable = clickable;
rows = new ArrayList<Row>();
this.petIds= new ArrayList<Integer>();
for(Pet p : petList)
{
rows.add(new ImageTextTextRow(LayoutInflater.from(MainActivity.this), p.getPetImageId(), p.getPetName(), p.getPetFood()));
petIds.add(p.getPetId());
}
}
#Override
public boolean isEnabled(int position)
{
//Can do that certain items ARE enabled by using the position
return clickable;
}
#Override
public int getViewTypeCount()
{
return RowType.values().length;
}
#Override
public int getItemViewType(int position)
{
return rows.get(position).getViewType();
}
#Override
public int getCount()
{
return rows.size();
}
#Override
public Object getItem(int position)
{
return rows.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
return rows.get(position).getView(convertView);
}
/**
*
* #param position
* #return
*/
public int getSelectedPetId(int position)
{
return petIds.get(position);
}
#Override
public Filter getFilter()
{
if (petFilter == null)
petFilter = new PetFilter();
return petFilter;
}
private class PetFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
FilterResults results = new FilterResults();
if (MainActivity.this.filteredPetList == null)
{
synchronized (PetAdapter.this.lock)
{
MainActivity.this.filteredPetList = new ArrayList<Pet>(MainActivity.this.originalPetList);
}
}
if (constraint == null || constraint.length() == 0)
{
synchronized (PetAdapter.this.lock)
{
results.values = MainActivity.this.originalPetList;
results.count = MainActivity.this.originalPetList.size();
}
}
else
{
String constraintString = constraint.toString().toLowerCase(Locale.ENGLISH);
final ArrayList<Pet> items = MainActivity.this.filteredPetList;
final int count = items.size();
final ArrayList<Pet> newItems = new ArrayList<Pet>(count);
for (int i = 0; i < count; i++)
{
final Pet item = items.get(i);
final String itemName = item.getPetName().toLowerCase(Locale.ENGLISH);
if (itemName.startsWith(constraintString))
{
newItems.add(item);
}
else
{
final String[] words = itemName.split(" ");
final int wordCount = words.length;
for (int k = 0; k < wordCount; k++)
{
if (words[k].startsWith(constraintString))
{
newItems.add(item);
break;
}
}
}
}
results.values = newItems;
results.count = newItems.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
MainActivity.this.filteredPetList.clear();
MainActivity.this.filteredPetList.addAll((ArrayList<Pet>)results.values);
if (results.count > 0)
PetAdapter.this.notifyDataSetChanged();
else
PetAdapter.this.notifyDataSetInvalidated();
}
}
}
Thanks in advance!
Tregan
I found a solution thanks to Luksprog, see his comment on my question.
I have changed my Adapter around and now it it's working fine. I also added some more code, and cleaned up, so it's a little bit different than before, but it should be clear what I changed when you read Luksprog's comment and then my code.
private class PetAdapter extends BaseAdapter implements Filterable
{
private final Object lock = new Object();
private List<Row> rows;
private ArrayList<Integer> petIds;
private boolean clickable;
private ArrayList<Pet> filteredPetList;
private PetFilter petFilter;
public PetAdapter(boolean clickable)
{
this.rows = new ArrayList<Row>();
this.petIds = new ArrayList<Integer>();
this.clickable = clickable;
this.filteredPetList = new ArrayList<Pet>(MainActivity.this.petList);
createPetRows();
}
#Override
public boolean isEnabled(int position)
{
//Can do that certain items ARE enabled/disabled by using the position
return clickable;
}
#Override
public int getViewTypeCount()
{
return RowType.values().length;
}
#Override
public int getItemViewType(int position)
{
return rows.get(position).getViewType();
}
#Override
public int getCount()
{
return rows.size();
}
#Override
public Object getItem(int position)
{
return rows.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
return rows.get(position).getView(convertView);
}
/**
* Retrieve the petId for the selected row
* #param position the position in the list
* #return the petId that belongs to that row
*/
public int getSelectedPetId(int position)
{
return petIds.get(position);
}
/**
* Creates rows that are displayed from items in the filteredPetList
* Also adds petIds to a List, so that the selected pet can be found
*/
private void createPetRows()
{
//Clear all current data
rows.clear();
petIds.clear();
for(Pet p : filteredPetList)
{
rows.add(new ImageTextTextRow(LayoutInflater.from(MainActivity.this), p.getPetImageId(), p.getPetName(), p.getPetFood()));
petIds.add(p.getPetId());
}
}
#Override
public Filter getFilter()
{
if (petFilter == null)
petFilter = new PetFilter();
return petFilter;
}
/**
* Custom Filter for our PetAdapter
* #author Bas
*
*/
private class PetFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
FilterResults results = new FilterResults();
if (PetAdapter.this.filteredPetList == null)
{
synchronized (PetAdapter.this.lock)
{
PetAdapter.this.filteredPetList = new ArrayList<Pet>(MainActivity.this.petList);
}
}
//No constraint is sent to filter by so we're going to send back the original array
if (constraint == null || constraint.length() == 0)
{
synchronized (PetAdapter.this.lock)
{
results.values = MainActivity.this.petList;
results.count = MainActivity.this.petList.size();
}
}
else
{
String constraintString = constraint.toString().toLowerCase(Locale.ENGLISH);
ArrayList<Pet> itemsToCheck = MainActivity.this.petList;
final ArrayList<Pet> newFilteredPets = new ArrayList<Pet>(itemsToCheck.size());
for(Pet p : itemsToCheck)
{
final String petName = p.getPetName().toLowerCase(Locale.ENGLISH);
final String petFood = p.getPetFood().toLowerCase(Locale.ENGLISH);
// First match against the whole, non-splitted value
if (petName.startsWith(constraintString) || petFood.startsWith(constraintString))
newFilteredPets.add(p);
else
{
final String[] petNameWords = petName.split(" ");
final String[] petFoodWords = petFood.split(" ");
for (String s : petNameWords)
{
if (s.startsWith(constraintString))
{
newFilteredPets.add(p);
break;
}
}
for (String s : petFoodWords)
{
if (s.startsWith(constraintString))
{
newFilteredPets.add(p);
break;
}
}
}
}
//Set the result
results.values = newFilteredPets;
results.count = newFilteredPets.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
//Add the results to our filteredPetList
PetAdapter.this.filteredPetList.clear();
PetAdapter.this.filteredPetList.addAll((ArrayList<Pet>)results.values);
//Create rows for every filtered Pet
createPetRows();
//Notify the Adapter
if (results.count > 0)
PetAdapter.this.notifyDataSetChanged();
else
PetAdapter.this.notifyDataSetInvalidated();
//Update the tvResults TextView with the amount of results found
TextView tvResults = (TextView)findViewById(R.id.tvResults);
if(results.count < MainActivity.this.petList.size())
{
tvResults.setText(results.count + " results found.");
tvResults.setVisibility(View.VISIBLE);
}
else
{
tvResults.setText("");
tvResults.setVisibility(View.GONE);
}
}
}
}
May you only need call notifyDataSetChanged() from your adapter.
I'll just give you my working custom filter code:
public class NaamFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FillArray();
FilterResults results = new FilterResults();
List<String> filtered = new ArrayList<String>();
for (String naam : namenarraylist) {
if(naam.toLowerCase().contains(constraint.toString().toLowerCase())) {
filtered.add(naam);
}
}
results.values = filtered;
results.count = filtered.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
if (results.count == 0) {
namenarraylist.clear();
notifyDataSetChanged();
} else {
namenarraylist = (ArrayList<String>) results.values;
notifyDataSetChanged();
}
}
}

Categories

Resources