Android ListPreference don't save the selection - java

i show a selectable list with all launchable installed apps. I wan't to save the selections, but the ListPreference saved all listed entries.
Where is my mistake?
Here is my ListPreference:
public class SettingsSelectsApps extends ListPreference {
private String separator;
private static final String DEFAULT_SEPARATOR = "\u0001\u0007\u001D\u0007\u0001";
private boolean[] entryChecked;
public SettingsSelectsApps(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
loadEntries();
entryChecked = new boolean[getEntries().length];
separator = DEFAULT_SEPARATOR;
}
public SettingsSelectsApps(Context context) {
this(context, null);
}
private void loadEntries() {
final Context context = getContext();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> pkgAppsList = context.getPackageManager().queryIntentActivities( mainIntent, 0);
CharSequence[] entries = new CharSequence[pkgAppsList.size()];
CharSequence[] entryValues = new CharSequence[pkgAppsList.size()];
int j = 0;
for ( ResolveInfo P : pkgAppsList ) {
entryValues[j] = (CharSequence) P.getClass().getName();
entries[j] = P.loadLabel(context.getPackageManager());
++j;
};
setEntries(entries);
setEntryValues(entryValues);
}
#Override
protected void onPrepareDialogBuilder(Builder builder) {
CharSequence[] entries = getEntries();
CharSequence[] entryValues = getEntryValues();
if (entries == null || entryValues == null || entries.length != entryValues.length) {
throw new IllegalStateException(
"MultiSelectListPreference requires an entries array and an entryValues "
+ "array which are both the same length");
}
restoreCheckedEntries();
OnMultiChoiceClickListener listener = new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean val) {
entryChecked[which] = val;
}
};
builder.setMultiChoiceItems(entries, entryChecked, listener);
}
private CharSequence[] unpack(CharSequence val) {
if (val == null || "".equals(val)) {
return new CharSequence[0];
} else {
return ((String) val).split(separator);
}
}
public CharSequence[] getCheckedValues() {
return unpack(getValue());
}
private void restoreCheckedEntries() {
CharSequence[] entryValues = getEntryValues();
CharSequence[] vals = unpack(getValue());
if (vals != null) {
List<CharSequence> valuesList = Arrays.asList(vals);
for (int i = 0; i < entryValues.length; i++) {
CharSequence entry = entryValues[i];
entryChecked[i] = valuesList.contains(entry);
}
}
SharedPreferences prefs = getSharedPreferences();
try{
Map<String,?> keys = prefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
}
} catch(NullPointerException e) {
Log.d("map values","Error: "+e);
}
}
#Override
protected void onDialogClosed(boolean positiveResult) {
List<CharSequence> values = new ArrayList<CharSequence>();
CharSequence[] entryValues = getEntryValues();
if (positiveResult && entryValues != null) {
for (int i = 0; i < entryValues.length; i++) {
if (entryChecked[i] == true) {
String val = (String) entryValues[i];
values.add(val);
}
}
String value = join(values, separator);
setSummary(prepareSummary(values));
setValueAndEvent(value);
}
}
private void setValueAndEvent(String value) {
if (callChangeListener(unpack(value))) {
setValue(value);
}
}
private CharSequence prepareSummary(List<CharSequence> joined) {
List<String> titles = new ArrayList<String>();
CharSequence[] entryTitle = getEntries();
CharSequence[] entryValues = getEntryValues();
int ix = 0;
for (CharSequence value : entryValues) {
if (joined.contains(value)) {
titles.add((String) entryTitle[ix]);
}
ix += 1;
}
return join(titles, ", ");
}
#Override
protected Object onGetDefaultValue(TypedArray typedArray, int index) {
return typedArray.getTextArray(index);
}
#Override
protected void onSetInitialValue(boolean restoreValue,
Object rawDefaultValue) {
String value = null;
CharSequence[] defaultValue;
if (rawDefaultValue == null) {
defaultValue = new CharSequence[0];
} else {
defaultValue = (CharSequence[]) rawDefaultValue;
}
List<CharSequence> joined = Arrays.asList(defaultValue);
String joinedDefaultValue = join(joined, separator);
if (restoreValue) {
value = getPersistedString(joinedDefaultValue);
} else {
value = joinedDefaultValue;
}
setSummary(prepareSummary(Arrays.asList(unpack(value))));
setValueAndEvent(value);
}
protected static String join(Iterable<?> iterable, String separator) {
Iterator<?> oIter;
if (iterable == null || (!(oIter = iterable.iterator()).hasNext()))
return "";
StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next()));
while (oIter.hasNext())
oBuilder.append(separator).append(oIter.next());
return oBuilder.toString();
}
}

I believe you use code from this gist which works great except for the separator. I don't know why did the author choose the separator to be "\u0001\u0007\u001D\u0007\u0001".
Once I tried to change the separator to any other string, for example ",," everything worked.
The separator string chosen seems to cause android to be unable to parse the shared preferences xml document.

Related

How and where exactly need to change to accept normal character search from the accented content without changing the UI android

I am using search filtered list. The list contains accented characters.
If I type Cam, it should support and accept Càm but it's not working. I am clueless where exactly I need to give to work in Adapter class.
Here is the code.
public class MainActivity extends AppCompatActivity {
private HighlightArrayAdapter mHighlightArrayAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Listview sample data
String products[] = {"Càmdoón", "córean", "Lamià", "dell", "HTC One X", "HTC Wildfire S", "HTC Sense", "HTC Sensàtion XE",
"iPhone 4S", "Samsóng Galàxy Note 800",
"Samsung Galàxy S3", "MacBook Air", "Màc Mini", "MàcBook Pro"};
ListView listView = (ListView) findViewById(R.id.listview);
EditText editText = (EditText) findViewById(R.id.inputSearch);
// Adding items to listview
mHighlightArrayAdapter = new HighlightArrayAdapter(this, R.layout.list_item, R.id.product_name, products);
listView.setAdapter(mHighlightArrayAdapter);
// Enabling Search Filter
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
mHighlightArrayAdapter.getFilter().filter(cs);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
}
#Override
public void afterTextChanged(Editable arg0) {
}
});
}
}
//HighlightArrayAdapter.
public class HighlightArrayAdapter extends ArrayAdapter<String> {
private final LayoutInflater mInflater;
private final Context mContext;
private final int mResource;
private List<String> mObjects;
private int mFieldId = 0;
private ArrayList<String> mOriginalValues;
private ArrayFilter mFilter;
private final Object mLock = new Object();
private String mSearchText; // this var for highlight
Pattern mPattern;
public HighlightArrayAdapter(Context context, int resource, int textViewResourceId, String[] objects) {
super(context, resource, textViewResourceId, objects);
mContext = context;
mInflater = LayoutInflater.from(context);
mResource = resource;
mObjects = Arrays.asList(objects);
mFieldId = textViewResourceId;
}
#Override
public Context getContext() {
return mContext;
}
#Override
public int getCount() {
return mObjects.size();
}
#Override
public String getItem(int position) {
return mObjects.get(position);
}
#Override
public int getPosition(String item) {
return mObjects.indexOf(item);
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
private class ArrayFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
mSearchText = "";
ArrayList<String> list;
synchronized (mLock) {
list = new ArrayList<>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
mSearchText = prefixString;
ArrayList<String> values;
synchronized (mLock) {
values = new ArrayList<>(mOriginalValues);
}
final int count = values.size();
final ArrayList<String> newValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
final String value = values.get(i);
final String valueText = value.toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString) || valueText.contains(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) || words[k].contains(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<String>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(mResource, parent, false);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
// If no custom field is assigned, assume the whole resource is a TextView
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
}
} catch (ClassCastException e) {
Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"ArrayAdapter requires the resource ID to be a TextView", e);
}
// HIGHLIGHT...
String fullText = getItem(position);
if (mSearchText != null && !mSearchText.isEmpty()) {
int startPos = fullText.toLowerCase(Locale.US).indexOf(mSearchText.toLowerCase(Locale.US));
int endPos = startPos + mSearchText.length();
if (startPos != -1) {
//Spannable spannable = new SpannableString(removeAccents(fullText)); // i used removeAccents but not worked.
Spannable spannable = new SpannableString(fullText);
ColorStateList blueColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{Color.BLUE});
TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, blueColor, null);
spannable.setSpan(highlightSpan, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setText(spannable);
} else {
text.setText(fullText);
}
} else {
text.setText(fullText);
}
return view;
}
/* public static String removeAccents(String text) {
return text == null ? null : Normalizer.normalize(text, Normalizer.Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}*/
/*private SpannableStringBuilder createHighlightedString(String nodeText, int highlightColor) {
SpannableStringBuilder returnValue = new SpannableStringBuilder(nodeText);
String lowercaseNodeText = nodeText.toLowerCase();
Matcher matcher = mSearchText.matcher(lowercaseNodeText);
while (matcher.find()) {
returnValue.setSpan(new ForegroundColorSpan(highlightColor), matcher.start(0),
matcher.end(0), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
}
return returnValue;
}*/
}
Here is the screenshot.
Scenario 1: (This is working)
Scenario 2: ( This is not working when I type normal character of a):
Scenario 3: (This is working when I type accented character):
So how to make Scenario 2 to work when I give normal character search in word to support the accented character list to accept.
I used InCombiningDiacriticalMarks but it's not working i am clueless where exactly need to give.
Kindly help me please in adapter class.
You should match your filtered list to a diactritics-less String.
public static String removeDiacritics(String input) {
String out = "" + input;
out = out.replaceAll(" ", "");
out = out.replaceAll("[èéêë]", "e");
out = out.replaceAll("[ûù]", "u");
out = out.replaceAll("[ïî]", "i");
out = out.replaceAll("[àâ]", "a");
out = out.replaceAll("Ô", "o");
out = out.replaceAll("[ÈÉÊË]", "E");
out = out.replaceAll("[ÛÙ]", "U");
out = out.replaceAll("[ÏÎ]", "I");
out = out.replaceAll("[ÀÂ]", "A");
out = out.replaceAll("Ô", "O");
out = out.replaceAll("-", "");
return out;
}
This way you will not be matching "Cam" with "Càm" anymore, but "Cam" with "Cam". You should also transform your strings to lower (or upper) case to be Upper-case permissive.
hope it helps!

How to create a conversation from the messages in inbox,sent,draft database of android

See the picture below and i want to create a conversation like this i can do the designing part but i need to create a database for the messages to be in conversation format
See the picture below and i want to create a conversation like this i can do the designing part but i need to create a database for the messages to be in conversation format
messaging.java
< private static String LIST_SEPARATOR = "!##$%^&*1234__/-|:..:|-/__*4321&^%$##!";
public List<String> phno= new ArrayList<>();
public List<String> nam=new ArrayList<>();
public List<String> message=new ArrayList<>();
public List<String> time=new ArrayList<>();
public List<String> type=new ArrayList<>();
public List<String> lstMsg=new ArrayList<>();
public List<String> lstTime=new ArrayList<>();
public List<String> lstType=new ArrayList<>();
public List<String> tempMsg=new ArrayList<>();
public List<String> tempTime=new ArrayList<>();
public List<String> tempKind=new ArrayList<>();
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String address;
String[] reqCols = new String[]{"_id", "thread_id", "address", "person", "date", "body", "type"};
Uri URI;
ContentResolver cr = getContentResolver();
String[] columns = new String[]{"address", "person", "date", "body", "type"};
Cursor cursor1;
int i,c=0;
//gets phone numbers
//inbox
URI = Uri.parse("content://sms/inbox");
cursor1 = cr.query(URI, reqCols, null, null, null);
if (cursor1.getCount() > 0) {
while (cursor1.moveToNext()) {
address = cursor1.getString(cursor1.getColumnIndex(columns[0]));
for (i = 0; i < phno.size(); i++){
if(address.equals(phno.get(i))) {
c++;
break;
}
}
if(c==0)
phno.add(address);
else
c=0;
}
}
cursor1.close();
//sentbox
URI = Uri.parse("content://sms/sent");
cursor1 = cr.query(URI,reqCols, null, null, null);
if (cursor1.getCount() > 0) {
while (cursor1.moveToNext()){
address = cursor1.getString(cursor1.getColumnIndex(columns[0]));
for (i = 0; i < phno.size(); i++){
if(address.equals(phno.get(i))) {
c++;
break;
}
}
if(c==0)
phno.add(address);
else
c=0;
}
}
cursor1.close();
//Draft
URI = Uri.parse("content://sms/draft");
cursor1 = cr.query(URI,reqCols, null, null, null);
if (cursor1.getCount() > 0) {
while (cursor1.moveToNext()){
address = cursor1.getString(cursor1.getColumnIndex(columns[0]));
for (i = 0; i < phno.size(); i++){
if(address.equals(phno.get(i))) {
c++;
break;
}
}
if(c==0)
phno.add(address);
else
c=0;
}
}
cursor1.close();
//gets contact name
for(i=0;i<phno.size();i++){
nam.add(getContactName(this,phno.get(i)));
}
for(String temp:phno){
URI = Uri.parse("content://sms/inbox");
cursor1 = cr.query(URI, reqCols, null, null, null);
if (cursor1.getCount() > 0) {
while (cursor1.moveToNext()) {
address = cursor1.getString(cursor1.getColumnIndex(columns[0]));
if(address.equals(temp)) {
tempTime.add(cursor1.getString(cursor1.getColumnIndex(columns[2])));// adds date
tempMsg.add(cursor1.getString(cursor1.getColumnIndex(columns[3])));// adds message
tempKind.add(cursor1.getString(cursor1.getColumnIndex(columns[4])));
}
}
}
cursor1.close();
URI = Uri.parse("content://sms/sent");
cursor1 = cr.query(URI, reqCols, null, null, null);
if (cursor1.getCount() > 0) {
while (cursor1.moveToNext()) {
address = cursor1.getString(cursor1.getColumnIndex(columns[0]));
if(address.equals(temp)) {
tempTime.add(cursor1.getString(cursor1.getColumnIndex(columns[2])));// adds date
tempMsg.add(cursor1.getString(cursor1.getColumnIndex(columns[3])));// adds message
tempKind.add(cursor1.getString(cursor1.getColumnIndex(columns[4])));
}
}
}
cursor1.close();
URI = Uri.parse("content://sms/draft");
cursor1 = cr.query(URI, reqCols, null, null, null);
if (cursor1.getCount() > 0) {
while (cursor1.moveToNext()) {
address = cursor1.getString(cursor1.getColumnIndex(columns[0]));
if(address.equals(temp)) {
tempTime.add(cursor1.getString(cursor1.getColumnIndex(columns[2])));// adds date
tempMsg.add(cursor1.getString(cursor1.getColumnIndex(columns[3])));// adds message
tempKind.add(cursor1.getString(cursor1.getColumnIndex(columns[4])));// adds type
}
}
}
cursor1.close();
sort();
lstTime.add(tempTime.get(tempTime.size()-1));
lstMsg.add(tempMsg.get(tempMsg.size()-1));
lstType.add(tempKind.get(tempKind.size()-1));
time.add(convertListToString(tempTime));
message.add(convertListToString(tempMsg));
type.add(convertListToString(tempKind));
}
finalSort();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
//finds contact name
public static String getContactName(Context context, String phoneNumber) {
ContentResolver cr = context.getContentResolver();
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor cursor = cr.query(uri, new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
if (cursor == null) {
return null;
}
String contactName = null;
if(cursor.moveToFirst()) {
contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
}
if(cursor != null && !cursor.isClosed()) {
cursor.close();
}
return contactName;
}
// Converts ArrayList to string
public static String convertListToString(List<String> stringList) {
StringBuffer stringBuffer = new StringBuffer();
for (String str : stringList) {
stringBuffer.append(str).append(LIST_SEPARATOR);
}
// Remove last separator
int lastIndex = stringBuffer.lastIndexOf(LIST_SEPARATOR);
stringBuffer.delete(lastIndex, lastIndex + LIST_SEPARATOR.length() + 1);
return stringBuffer.toString();
}
// Converts String to ArrayList
public static List<String> convertStringToList(String str) {
return Arrays.asList(str.split(LIST_SEPARATOR));
}
//Lists Sorting
public void sort(){
for(int j=0;j<tempTime.size();j++){
for(int i=j+1;i<tempTime.size();i++){
if((tempTime.get(i)).compareTo(tempTime.get(j))<0){
String t1 = tempTime.get(j);
String t2 = tempKind.get(j);
String t3 = tempMsg.get(j);
tempTime.set( j, tempTime.get(i));
tempTime.set(i,t1);
tempKind.set( j, tempKind.get(i));
tempKind.set(i,t2);
tempMsg.set( j, tempMsg.get(i));
tempMsg.set(i,t3);
}
}
}
}
//Final
//Lists Sorting
public void finalSort(){
for(int j=0;j<lstTime.size();j++){
for(int i=j+1;i<lstTime.size();i++){
if((lstTime.get(i)).compareTo(lstTime.get(j))<0){
String t1 = lstTime.get(j);
String t2 = lstMsg.get(j);
String t3 = lstType.get(j);
String t4 = time.get(j);
String t5 = message.get(j);
String t6 = type.get(j);
String t7 = phno.get(j);
String t8 = nam.get(j);
lstTime.set( j, lstTime.get(i));
lstTime.set(i,t1);
lstMsg.set( j, lstMsg.get(i));
lstMsg.set(i,t2);
lstType.set( j, lstType.get(i));
lstType.set(i,t3);
time.set( j, time.get(i));
time.set(i,t4);
message.set( j, message.get(i));
message.set(i,t5);
type.set( j, type.get(i));
type.set(i,t6);
phno.set( j, phno.get(i));
phno.set(i,t7);
nam.set( j, nam.get(i));
nam.set(i,t8);
}
}
}
}

Disable Checkbox click in multiselectListPreference in android

I am using mutliselectlistpreference that extends DialogPreference in my application. And i have not use any adapter for building the UI. Please find the below image.
The issue here is I am able to persist the CheckBox checked for Monday and Tuesday but i am not able to make the items read only wherein user will not able to unchecked the items. I want to make both items grey out. could you please help me out on this ?
#Override
protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
if (entries == null || entryValues == null) {
throw new IllegalStateException(
"MultiSelectListPreference requires an entries array and an entryValues array.");
}
checked = new boolean[entryValues.length];
List<CharSequence> entryValuesList = Arrays.asList(entryValues);
List<CharSequence> entriesList = Arrays.asList(entries);
for (int i = 0; i < entryValues.length; ++i) {
if("Monday".equals(entriesList.get(i).toString())){
checked[i]=true;
}
}
if (values != null) {
for (String value : values) {
int index = entryValuesList.indexOf(value);
if (index != -1) {
checked[index] = true;
}
}
}
builder.setMultiChoiceItems(entries, checked,
new OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which,
boolean isChecked) {
checked[which] = isChecked;
}
});
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && entryValues != null) {
for (int i = 0; i < entryValues.length; ++i) {
if (checked[i]) {
newValues.add(entryValues[i].toString());
}
}
if (callChangeListener(newValues)) {
setValues(newValues);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
CharSequence[] array = a.getTextArray(index);
Set<String> set = new HashSet<String>();
for (CharSequence item : array) {
set.add(item.toString());
}
return set;
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue,
Object defaultValue) {
#SuppressWarnings("unchecked")
Set<String> defaultValues = (Set<String>) defaultValue;
setValues((restorePersistedValue ? getPersistedStringSet(values)
: defaultValues));
}
private Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
String key = getKey();
//String value = getSharedPreferences().getString("4", "Generic");
return getSharedPreferences().getStringSet(key, defaultReturnValue);
}
private boolean persistStringSet(Set<String> values) {
if (shouldPersist()) {
// Shouldn't store null
if (values == getPersistedStringSet(null)) {
return true;
}
}
SharedPreferences.Editor editor = getEditor();
editor.putStringSet(getKey(), values);
editor.apply();
return true;
}
#Override
protected Parcelable onSaveInstanceState() {
if (isPersistent()) {
return super.onSaveInstanceState();
} else {
throw new IllegalStateException("Must always be persistent");
}
}
You can use them;
checkBox.setEnabled(true); // enable checkbox
checkBox.setEnabled(false); // disable checkbox
Also you can disable them after check the checkBox with this code:
checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){
#Override
public void onCheckedChanged(CompoundButton arg0, boolean isChecked) {
if (isChecked){
checkBox.setEnabled(false); // disable checkbox
}
}
});

How to perform a Trie search using an autocompleteTextView in android?

I need to implement an AutocompleteTextView with a Trie search. I am using a Vertex class and a Trie class for the Trie tree data structure and an adapter for customize the view of the dropdown box of the autocompleteTextView.
Here are all the classes I am using:
The Vertex class:
public class Vertex {
private HashMap<Character, Vertex> vertexSons;
private List<Integer> wordsIndexList;
private List<Integer> prefixesIndexList;
private int wordsNumber;
private int prefixesNumber;
public Vertex() {
vertexSons = new HashMap<Character, Vertex>();
wordsIndexList = new ArrayList<Integer>();
prefixesIndexList = new ArrayList<Integer>();
wordsNumber = 0;
prefixesNumber = 0;
}
public boolean hasWords() {
if (wordsNumber > 0) {
return true;
}
return false;
}
public boolean hasPrefixes() {
if (prefixesNumber > 0) {
return true;
}
return false;
}
public void addVertexSon(Character character) {
vertexSons.put(character, new Vertex());
}
public void addIndexToWordsIndexList(int index) {
wordsIndexList.add(index);
}
public void addIndexToPrefixesIndexList(int index) {
prefixesIndexList.add(index);
}
public HashMap<Character, Vertex> getVertexSons() {
return vertexSons;
}
public List<Integer> getWordsIndexList() {
return wordsIndexList;
}
public List<Integer> getPrefixesIndexList() {
return prefixesIndexList;
}
public int getWordsNumber() {
return wordsNumber;
}
public int getPrefixesNumber() {
return prefixesNumber;
}
public void increaseWordsNumber() {
wordsNumber++;
}
public void increasePrefixesNumber() {
prefixesNumber++;
}
}
The Trie class:
public class Trie {
private Vertex rootVertex;
public Trie(List<Trieable> objectList, Locale locale) {
rootVertex = new Vertex();
for (int i = 0; i<objectList.size(); i++) {
String word = objectList.get(i).toString().toLowerCase(locale);
addWord(rootVertex, word, i);
}
}
public Vertex getRootVertex() {
return rootVertex;
}
public void addWord(Vertex vertex, String word, int index) {
if (word.isEmpty()) {
vertex.addIndexToWordsIndexList(index);
vertex.increaseWordsNumber();
}
else {
vertex.addIndexToPrefixesIndexList(index);
vertex.increasePrefixesNumber();
Character fChar = word.charAt(0);
HashMap<Character, Vertex> vertexSons = vertex.getVertexSons();
if (!vertexSons.containsKey(fChar)) {
vertex.addVertexSon(fChar);
}
word = (word.length() == 1) ? "" : word.substring(1);
addWord(vertexSons.get(fChar), word, index);
}
}
public List<Integer> getWordsIndexes(Vertex vertex, String word) {
if (word.isEmpty()) {
return vertex.getWordsIndexList();
}
else {
Character fChar = word.charAt(0);
if (!(vertex.getVertexSons().containsKey(fChar))) {
return null;
}
else {
word = (word.length() == 1) ? "" : word.substring(1);
return getWordsIndexes(vertex.getVertexSons().get(fChar), word);
}
}
}
public List<Integer> getPrefixesIndexes(Vertex vertex, String prefix) {
if (prefix.isEmpty()) {
return vertex.getWordsIndexList();
}
else {
Character fChar = prefix.charAt(0);
if (!(vertex.getVertexSons().containsKey(fChar))) {
return null;
}
else {
prefix = (prefix.length() == 1) ? "" : prefix.substring(1);
return getWordsIndexes(vertex.getVertexSons().get(fChar), prefix);
}
}
}
}
The Adapter class:
public class MunicipalitySearchAdapter extends ArrayAdapter<Municipality> {
private ArrayList<Municipality> municipalities;
private ArrayList<Municipality> allMunicipalities;
private ArrayList<Municipality> suggestedMunicipalities;
private List<Trieable> triableList;
private Trie municipalityTrie;
private int viewResourceId;
#SuppressWarnings("unchecked")
public MunicipalitySearchAdapter(Context context, int viewResourceId, ArrayList<Municipality> municipalities) {
super(context, viewResourceId, municipalities);
this.municipalities = municipalities;
this.allMunicipalities = (ArrayList<Municipality>) this.municipalities.clone();
this.suggestedMunicipalities = new ArrayList<Municipality>();
this.viewResourceId = viewResourceId;
this.triableList = new ArrayList<Trieable>();
for (Municipality mun : allMunicipalities) {
triableList.add(mun);
}
municipalityTrie = new Trie(triableList, Locale.ITALY);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(this.viewResourceId, null);
}
Municipality municipality = municipalities.get(position);
if (municipality != null) {
TextView munNameTxtView = (TextView) v.findViewById(R.id.name);
TextView proSignTxtView = (TextView) v.findViewById(R.id.sign);
TextView regNameTxtView = (TextView) v.findViewById(R.id.regionName);
if (munNameTxtView != null) {
munNameTxtView.setText(municipality.getName());
}
if (proSignTxtView != null) {
proSignTxtView.setText(municipality.getProvinceSign());
}
if (regNameTxtView != null) {
regNameTxtView.setText(municipality.getRegionName());
}
}
return v;
}
#Override
public Filter getFilter() {
return municipalityFilter;
}
Filter municipalityFilter = new Filter() {
#Override
public String convertResultToString(Object resultValue) {
String str = ((Municipality) (resultValue)).getName();
return str;
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
String constraintString = constraint.toString().trim();
suggestedMunicipalities.clear();
List<Integer> wordsIndexesList = municipalityTrie.getWordsIndexes(municipalityTrie.getRootVertex(), constraintString);
for (int index : wordsIndexesList) {
suggestedMunicipalities.add(allMunicipalities.get(index));
}
List<Integer> prefixesIndexesList = municipalityTrie.getPrefixesIndexes(municipalityTrie.getRootVertex(), constraintString);
for (int index : prefixesIndexesList) {
suggestedMunicipalities.add(allMunicipalities.get(index));
}
FilterResults filterRes = new FilterResults();
filterRes.values = suggestedMunicipalities;
filterRes.count = suggestedMunicipalities.size();
return filterRes;
}
else {
return new FilterResults();
}
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
#SuppressWarnings("unchecked")
ArrayList<Municipality> filteredMunicipalities = (ArrayList<Municipality>) results.values;
ArrayList<Municipality> supportMunicipalitiesList = new ArrayList<Municipality>();
clear();
for (Municipality mun : filteredMunicipalities) {
supportMunicipalitiesList.add(mun);
}
Iterator<Municipality> municipalityIterator = supportMunicipalitiesList.iterator();
while (municipalityIterator.hasNext()) {
Municipality municipality = municipalityIterator.next();
add(municipality);
}
notifyDataSetChanged();
}
}
};
}
I get a null pointer at this line (in the Filter performFltering() method implementation inside of the adapter):
for (int index : wordsIndexesList) {
suggestedMunicipalities.add(allMunicipalities.get(index));
}
Do you have any possible solutions to the problem? I can't fix it out. What am I missing or mistaking?
EDIT: I have found the problem, I needed to set the constraint toLowerCase(). Now it works. However. Why do I see the autocomplete suggestions ONLY when I type the FULL word? (in my case the name of a municipality).
It seems that my Trie doesn't return the PrefixIndexes which I can use for populating the List of suggestions. But I can't find out the problem. Any idea?
Thanks again!
To fix typing of FULL word issue mentioned by the author, getWordsIndexes should be replaced by getPrefixesIndexes, and getWordsIndexList - by getPrefixesIndexList in getPrefixesIndexes() method of Trie class:
public List<Integer> getPrefixesIndexes(Vertex vertex, String prefix)
{
if (prefix.isEmpty())
{
return vertex.getPrefixesIndexList();
}
else
{
Character fChar = prefix.charAt(0);
if (!(vertex.getVertexSons().containsKey(fChar)))
{
return null;
}
else
{
prefix = (prefix.length() == 1) ? "" : prefix.substring(1);
return getPrefixesIndexes(vertex.getVertexSons().get(fChar), prefix);
}
}
}

How can I speed up an AutocompleteTextView in Android?

Hi everyone I have an adapter which extends the ArrayAdapter class and overrides some Filterable methods. I am using this Adapter to perform some filtering while the user types inside an AutocompleteTextView. But I saw that if you type a bit fast the updanting of the filtered items becomes very slow. This is the adapter class:
public class MunicipalitySearchAdapter extends ArrayAdapter<Municipality> {
private ArrayList<Municipality> municipalities;
private ArrayList<Municipality> allMunicipalities;
private ArrayList<Municipality> suggestedMunicipalities;
private int viewResourceId;
#SuppressWarnings("unchecked")
public MunicipalitySearchAdapter(Context context, int viewResourceId, ArrayList<Municipality> municipalities) {
super(context, viewResourceId, municipalities);
this.municipalities = municipalities;
this.allMunicipalities = (ArrayList<Municipality>) this.municipalities.clone();
this.suggestedMunicipalities = new ArrayList<Municipality>();
this.viewResourceId = viewResourceId;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(this.viewResourceId, null);
}
Municipality municipality = municipalities.get(position);
if (municipality != null) {
TextView munNameTxtView = (TextView) v.findViewById(R.id.name);
TextView proSignTxtView = (TextView) v.findViewById(R.id.sign);
if (munNameTxtView != null) {
munNameTxtView.setText(municipality.getName());
}
if (proSignTxtView != null) {
proSignTxtView.setText(municipality.getProvinceSign());
}
}
return v;
}
#Override
public Filter getFilter() {
return municipalityFilter;
}
Filter municipalityFilter = new Filter() {
#Override
public String convertResultToString(Object resultValue) {
String str = ((Municipality) (resultValue)).getName();
return str;
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
suggestedMunicipalities.clear();
for (Municipality municipality : allMunicipalities) {
if (municipality.getName().toLowerCase(Locale.getDefault()).startsWith(constraint.toString().toLowerCase(Locale.getDefault()))) {
suggestedMunicipalities.add(municipality);
}
}
FilterResults filterRes = new FilterResults();
filterRes.values = suggestedMunicipalities;
filterRes.count = suggestedMunicipalities.size();
return filterRes;
}
else {
return new FilterResults();
}
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
#SuppressWarnings("unchecked")
ArrayList<Municipality> filteredMunicipalities = (ArrayList<Municipality>) results.values;
ArrayList<Municipality> supportMunicipalitiesList = new ArrayList<Municipality>();
clear();
for (Municipality mun : filteredMunicipalities) {
supportMunicipalitiesList.add(mun);
}
Iterator<Municipality> municipalityIterator = supportMunicipalitiesList.iterator();
while (municipalityIterator.hasNext()) {
Municipality municipality = municipalityIterator.next();
add(municipality);
}
notifyDataSetChanged();
}
}
};
}
I would like to ask if someone knows how to increase the performance of this kind of AutocompleteTextView and make the updating a faster. What should I do? Thanks!
EDIT: I have createed this classes:
Vertex:
public class Vertex {
private HashMap<Character, Vertex> vertexSons;
private List<Integer> wordsIndexList;
private List<Integer> prefixesIndexList;
private int wordsNumber;
private int prefixesNumber;
public Vertex() {
vertexSons = new HashMap<Character, Vertex>();
wordsIndexList = new ArrayList<Integer>();
prefixesIndexList = new ArrayList<Integer>();
wordsNumber = 0;
prefixesNumber = 0;
}
public boolean hasWords() {
if (wordsNumber > 0) {
return true;
}
return false;
}
public boolean hasPrefixes() {
if (prefixesNumber > 0) {
return true;
}
return false;
}
public void addVertexSon(Character character) {
vertexSons.put(character, new Vertex());
}
public void addIndexToWordsIndexList(int index) {
wordsIndexList.add(index);
}
public void addIndexToPrefixesIndexList(int index) {
prefixesIndexList.add(index);
}
public HashMap<Character, Vertex> getVertexSons() {
return vertexSons;
}
public List<Integer> getWordsIndexList() {
return wordsIndexList;
}
public List<Integer> getPrefixesIndexList() {
return prefixesIndexList;
}
public int getWordsNumber() {
return wordsNumber;
}
public int getPrefixesNumber() {
return prefixesNumber;
}
public void increaseWordsNumber() {
wordsNumber++;
}
public void increasePrefixesNumber() {
prefixesNumber++;
}
}
And Trie:
public class Trie {
private Vertex rootVertex;
public Trie(List<Trieable> objectList, Locale locale) {
rootVertex = new Vertex();
for (int i = 0; i<objectList.size(); i++) {
String word = objectList.get(i).toString().toLowerCase(locale);
addWord(rootVertex, word, i);
}
}
public Vertex getRootVertex() {
return rootVertex;
}
public void addWord(Vertex vertex, String word, int index) {
if (word.isEmpty()) {
vertex.addIndexToWordsIndexList(index);
vertex.increaseWordsNumber();
}
else {
vertex.addIndexToPrefixesIndexList(index);
vertex.increasePrefixesNumber();
Character fChar = word.charAt(0);
HashMap<Character, Vertex> vertexSons = vertex.getVertexSons();
if (!vertexSons.containsKey(fChar)) {
vertex.addVertexSon(fChar);
}
word = (word.length() == 1) ? "" : word.substring(1);
addWord(vertexSons.get(fChar), word, index);
}
}
public List<Integer> getWordsIndexes(Vertex vertex, String word) {
if (word.isEmpty()) {
return vertex.getWordsIndexList();
}
else {
Character fChar = word.charAt(0);
if (!(vertex.getVertexSons().containsKey(fChar))) {
return null;
}
else {
word = (word.length() == 1) ? "" : word.substring(1);
return getWordsIndexes(vertex.getVertexSons().get(fChar), word);
}
}
}
public List<Integer> getPrefixesIndexes(Vertex vertex, String prefix) {
if (prefix.isEmpty()) {
return vertex.getWordsIndexList();
}
else {
Character fChar = prefix.charAt(0);
if (!(vertex.getVertexSons().containsKey(fChar))) {
return null;
}
else {
prefix = (prefix.length() == 1) ? "" : prefix.substring(1);
return getWordsIndexes(vertex.getVertexSons().get(fChar), prefix);
}
}
}
}
And edited my Filter like this:
Filter municipalityFilter = new Filter() {
#Override
public String convertResultToString(Object resultValue) {
String str = ((Municipality) (resultValue)).getName();
return str;
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
String constraintString = constraint.toString().trim();
suggestedMunicipalities.clear();
List<Integer> wordsIndexesList = municipalityTrie.getWordsIndexes(municipalityTrie.getRootVertex(), constraintString);
for (int index : wordsIndexesList) {
suggestedMunicipalities.add(allMunicipalities.get(index));
}
List<Integer> prefixesIndexesList = municipalityTrie.getPrefixesIndexes(municipalityTrie.getRootVertex(), constraintString);
for (int index : prefixesIndexesList) {
suggestedMunicipalities.add(allMunicipalities.get(index));
}
FilterResults filterRes = new FilterResults();
filterRes.values = suggestedMunicipalities;
filterRes.count = suggestedMunicipalities.size();
return filterRes;
}
else {
return new FilterResults();
}
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
#SuppressWarnings("unchecked")
ArrayList<Municipality> filteredMunicipalities = (ArrayList<Municipality>) results.values;
ArrayList<Municipality> supportMunicipalitiesList = new ArrayList<Municipality>();
clear();
for (Municipality mun : filteredMunicipalities) {
supportMunicipalitiesList.add(mun);
}
Iterator<Municipality> municipalityIterator = supportMunicipalitiesList.iterator();
while (municipalityIterator.hasNext()) {
Municipality municipality = municipalityIterator.next();
add(municipality);
}
notifyDataSetChanged();
}
}
};
Now I get a null pointer warning when I type in the AutoCompleteTextView at this line:
List<Integer> wordsIndexesList = municipalityTrie.getWordsIndexes(municipalityTrie.getRootVertex(), constraintString);
for (int index : wordsIndexesList) {
suggestedMunicipalities.add(allMunicipalities.get(index));
}
In the for (int index : wordsIndexesList) statement. What should I do? Thanks!
You should look into using a trie, it would be perfect for auto complete.
Here is what one looks like:
As you get more characters, you can traverse further down the tree, and the number of possible options will get smaller and smaller.
This would be significantly faster than looking over your entire list each time.
Edit: After reflecting on my answer I think a much easier solution would be to just use any kind of sorted map. Checkout this answer for an example.

Categories