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
}
Related
I'm trying to implement search filter with getFilter method in my custom adapter.
There are two models of search through the list.
search for title of an item,
search for genre[] of an item.
(But I'm just talking about genre here).
The below code is working fine, but what I need is just a little more customized way to search in genre[].
What I'm trying to achieve is: (What I want):
multi-keyword searching, each category separated with,. example: genre1,genre2
(What I have done):
When I type genre1,genre2 then it gives the items that have this sequence in genres, it won't give genre1,genre3,genre2 , or just genre1.
Can you help, please?
getFilter:
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
dataList = (List<ProductLocal>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<ProductLocal> filteredList = new ArrayList<>();
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < dataListFilter.size(); i++) {
ProductLocal dataNames = dataListFilter.get(i);
String genreStr = "";
for (String str : dataNames.getGenre()) {
genreStr += str + ",";
}
if (dataNames.getTitle().toLowerCase().startsWith(constraint.toString())
|| genreStr.toLowerCase().contains(constraint.toString())) {
filteredList.add(dataNames);
}
}
results.count = filteredList.size();
results.values = filteredList;
return results;
}
};
return filter;
}
AFAIU you want a song to match the fiter if any of these 2 conditions are met:
Song's title starts with whole search text
The song has a genre that contains a part of the search text separated by , (comma)
If this is so, try following search logic:
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
FilterResults results = new FilterResults();
List<ProductLocal> filteredList = new ArrayList<>();
String searchText = constraint.toString().toLowerCase();
String[] split = searchText.split(",");
ArrayList<String> searchGenres = new ArrayList<String>(split.length);
for (int i = 0; i < split.length; i++)
{
// remove spaces
String trim = split[i].trim();
// skip empty entries
if (trim.length() > 0)
searchGenres.add(trim);
}
for (ProductLocal dataNames : dataListFilter)
{
// filter by title
if (dataNames.getTitle().toLowerCase().startsWith(searchText))
{
filteredList.add(dataNames);
}
else
{
// filter by genres
// search for at least one common genre between song and search text
outer:
for (String songGenre : dataNames.getGenre())
{
for (String searchGenre : searchGenres)
{
if (songGenre.toLowerCase().contains(searchGenre))
{
filteredList.add(dataNames);
break outer;
}
}
}
}
}
results.count = filteredList.size();
results.values = filteredList;
return results;
}
The only tricky place here is break outer;. This is a Java syntax to break out from two for loops at the same time. The idea here is that if we've found a match by some genre between constraint and a song, we add the song to the filteredList and don't want to add it again if there are more matching genres.
P.S. if filtering by many search genres becomes a performance issue, you may consider building single RegEx to match all search terms at once or implementing Aho–Corasick algorithm
Please try by splitting constraint string like below:
#Override
public Filter getFilter() {
#SuppressWarnings("UnnecessaryLocalVariable")
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
dataList = (List<ProductLocal>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<ProductLocal> filteredList = new ArrayList<>();
for (int i = 0; i < dataListFilter.size(); i++) {
ProductLocal dataNames = dataListFilter.get(i);
int count = 0;
String[] terms = constraint.toString().split(",");
for (int j = 0; j < terms.length; j++) {
String term = terms[j];
for (String str : dataNames.getGenre()) {
if (str.equals(term) || str.contains(term)) {
count++;
if (count == terms.length) {
filteredList.add(dataNames);
}
}
}
}
}
results.count = filteredList.size();
results.values = filteredList;
return results;
}
};
return filter;
}
You can split search term by ,
String[] searmTearmArray= serachTerm.split(",");
now using for loop you can search these terms.
I have an ArrayList where I only want to show the Items that contain the strings "dish" or "sideDish". At first I had a for-loop in the getItem function but it wasn't good for the performance so I tried to do it with a filter.
public Filter getFilter(){
return mFilter;
}
private class ItemFilter extends Filter{
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString= constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final List<CanteenMenu> list = listData;
int count = list.size();
final List<CanteenMenu> nlist = new ArrayList<CanteenMenu>(count);
String filterableString;
for(int i = 0; i < count; i++){
filterableString = list.get(i);
if(filterableString.toLowerCase().contains("dish" || "sideDish")){
nlist.add(filterableString);
}
}
results.values = nlist;
results.count = nlist.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredData = (ArrayList<CanteenMenu>) results.values;
}
}
The problems are:
- the or || doens't work
- the filterableString = list.get(i); gives the error icompatible types
- nlist.add(filterableString); gives the error list cannot be applied to string
Can anyone help?
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 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,
I would like to make a custom ListPreference to let the user choose from his apps. This currently works great. Except:
Filter out system apps (I've filtered by (info.activityInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1 but it still displays system apps)
Icons (Except "check all" item; I guess I need to manipulate the Layout somehow?; Found the solution: http://www.devmil.de/?p=63)
preferences.xml
<com.example.gui.preference.ApplicationListPreference
android:key="pref_key_choose_apps"
android:dependency="pref_key_enable_server"
android:title="#string/choose_apps"
android:dialogTitle="#string/choose_apps"
android:entries="#array/pref_entries_choose_apps"
android:entryValues="#array/pref_values_choose_apps"
example:checkAll="#ALL#" />
arrays.xml
<string-array name="pref_values_choose_apps">
<item>#ALL#</item>
</string-array>
<string-array name="pref_entries_choose_apps">
<item>All</item>
</string-array>
attr.xml
<declare-styleable name="ApplicationListPreference">
<attr format="string" name="checkAll" />
</declare-styleable>
ApplicationListPreference:
public class ApplicationListPreference extends ListPreference {
private final static String SEPARATOR = ";";
private String checkAllKey = null;
private boolean[] mClickedDialogEntryIndices;
public ApplicationListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
checkAllKey = context.obtainStyledAttributes(attrs, R.styleable.ApplicationListPreference).getString(R.styleable.ApplicationListPreference_checkAll);
List<CharSequence> entries = new ArrayList<CharSequence>();
for (CharSequence entry : getEntries()) {
entries.add(entry);
}
List<CharSequence> entryValues = new ArrayList<CharSequence>();
for (CharSequence entryValue : getEntryValues()) {
entryValues.add(entryValue);
}
Intent intentFilter = new Intent(Intent.ACTION_MAIN, null);
intentFilter.addCategory(Intent.CATEGORY_LAUNCHER);
PackageManager pm = context.getPackageManager();
List<ResolveInfo> appList = pm.queryIntentActivities(intentFilter, PackageManager.PERMISSION_GRANTED);
Collections.sort(appList, new ResolveInfo.DisplayNameComparator(pm));
for (ResolveInfo info : appList) {
if ((info.activityInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1) {
entryValues.add(info.activityInfo.packageName);
entries.add(info.loadLabel(pm).toString());
}
}
setEntries(entries.toArray(new CharSequence[entries.size()]));
setEntryValues(entryValues.toArray(new CharSequence[entryValues.size()]));
}
public ApplicationListPreference(Context context) {
this(context, null);
}
#Override
public void setEntries(CharSequence[] entries) {
super.setEntries(entries);
mClickedDialogEntryIndices = new boolean[entries.length];
}
#Override
protected void onPrepareDialogBuilder(Builder builder) {
CharSequence[] entries = getEntries();
CharSequence[] entryValues = getEntryValues();
if (entries == null || entryValues == null || entries.length != entryValues.length) {
throw new IllegalStateException("Irregular array length");
}
restoreCheckedEntries();
builder.setMultiChoiceItems(entries, mClickedDialogEntryIndices, new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean val) {
if (isCheckAllValue(which) == true) {
checkAll(dialog, val);
}
mClickedDialogEntryIndices[which] = val;
}
});
}
private boolean isCheckAllValue(int which) {
final CharSequence[] entryValues = getEntryValues();
if (checkAllKey != null) {
return entryValues[which].equals(checkAllKey);
}
return false;
}
private void checkAll(DialogInterface dialog, boolean val) {
ListView lv = ((AlertDialog) dialog).getListView();
int size = lv.getCount();
for (int i = 0; i < size; i++) {
lv.setItemChecked(i, val);
mClickedDialogEntryIndices[i] = val;
}
}
public String[] parseStoredValue(CharSequence val) {
if (val == null || "".equals(val)) {
return null;
}
return ((String) val).split(SEPARATOR);
}
private void restoreCheckedEntries() {
CharSequence[] entryValues = getEntryValues();
String[] vals = parseStoredValue(getValue());
if (vals != null) {
List<String> valuesList = Arrays.asList(vals);
for (int i = 0; i < entryValues.length; i++) {
CharSequence entry = entryValues[i];
if (valuesList.contains(entry)) {
mClickedDialogEntryIndices[i] = true;
}
}
}
}
#Override
protected void onDialogClosed(boolean positiveResult) {
ArrayList<String> values = new ArrayList<String>();
CharSequence[] entryValues = getEntryValues();
if (positiveResult && entryValues != null) {
for (int i = 0; i < entryValues.length; i++) {
if (mClickedDialogEntryIndices[i] == true) {
String val = (String) entryValues[i];
if (checkAllKey == null || (val.equals(checkAllKey) == false)) {
values.add(val);
}
}
}
String value = join(values);
if (callChangeListener(value)) {
setValue(value);
}
}
}
protected static String join(Iterable<? extends Object> pColl) {
Iterator< ? extends Object > oIter;
if (pColl == null || (!(oIter = pColl.iterator()).hasNext())) {
return "";
}
StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next()));
while (oIter.hasNext()) {
oBuilder.append(SEPARATOR).append(oIter.next());
}
return oBuilder.toString();
}
public static boolean contains(String straw, String haystack) {
for (String val : haystack.split(SEPARATOR)) {
if (val.equals(straw)) {
return true;
}
}
return false;
}
}
For the system apps filtering, you should be checking the flags in the ApplicationInfo instead of ActivityInfo. Try modifying this:
(info.activityInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1
to
(info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1