How do I get MultiAutoCompleteTextView tokenizer similar to Facebook app? - android

I am creating an application which has a 'To' field just like in Facebook app's "New Message" feature.
After selecting an item from the drop down list, I create an imagespan and add it to the MultiAutoCompleteTextView. I have used SpaceTokenizer for this view . The problem is when I click on backspace, the cursor first moves to the empty space (i.e., space Tokenizer) and then when I click on the backspace again, the whole word gets deleted....I want to delete the whole word on my first click of backspace just like facebook app...
Here is my code for SpaceTokenizer
multiContentText.setTokenizer(new Tokenizer(){
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
if(i>0){
Log.d("textchar ",""+text.charAt(i - 1));
}
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ' || text.charAt(i - 1) == '\n') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ' || text.charAt(i - 1) == '\n') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ' || text.charAt(i - 1) == '\n') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ' || text.charAt(i - 1) == '\n') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text+" ";
}
}
}
});
I am using this code to create a TextView in my multi-ContentText
SpannableStringBuilder ssb = new SpannableStringBuilder(multiContentText.getText());
String c="text from the list";
TextView textView = (TextView) inflater.inflate(R.layout.chips_edittext, null);
textView.setText(c); // set text
int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
textView.measure(spec, spec);
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(textView.getWidth(), textView.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
canvas.translate(-textView.getScrollX(), -textView.getScrollY());
textView.draw(canvas);
textView.setDrawingCacheEnabled(true);
Bitmap cacheBmp = textView.getDrawingCache();
Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
textView.destroyDrawingCache(); // destory drawable
// create bitmap drawable for imagespan
BitmapDrawable bmpDrawable = new BitmapDrawable(viewBmp);
bmpDrawable.setBounds(0, 0,bmpDrawable.getIntrinsicWidth(),bmpDrawable.getIntrinsicHeight());
// create and set imagespan
ssb.setSpan(new ImageSpan(bmpDrawable),0 ,c.length() , Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// set chips span
multiContentText.setText(ssb);
multiContentText.setSelection(multiContentText.getText().length());
I am not sure whether the space Tokenizer is the right option for this type of behavior...Any help or pointers will be grateful...
Here is the screenshot for better understanding....
I have a text followed by a space and then a cursor...If I hit backspace, it first moves to the empty space and only when I hit backspace again the whole text is deleted....
Here is the another screenshot ..
Here the cursor is not exactly in between the two TextViews unlike in facebook app which again causes some issues in inserting the text...

Found the solution....
Add this textwatcher to the multiautocompletetextview
private TextWatcher textWather = new TextWatcher() {
int noOfCharAdded=0;int noOfCharDeleted=0;
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
startIdx=start;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
noOfCharAdded=after;
noOfCharDeleted=count;
}
#Override
public void afterTextChanged(Editable s) {
Editable buffer = s;
int start = multiContentText.getSelectionStart()<0?0:multiContentText.getSelectionStart();
int end = multiContentText.getSelectionEnd()<0?0:multiContentText.getSelectionEnd();
if(noOfCharAdded==0 && noOfCharDeleted==1){ //if space is deleted
if (start == end && delPrevText) {
ImageSpan link[] = buffer.getSpans(start, end,ImageSpan.class);
if (link.length > 0) {
buffer.replace(buffer.getSpanStart(link[0]),buffer.getSpanEnd(link[0]),"");
buffer.removeSpan(link[0]);
}
}
delPrevText=true;
multiContentText.setSelection(multiContentText.getText().length());
}
else if(noOfCharAdded==0 && noOfCharDeleted>1){//if the whole word is deleted
if(buffer.length()>0){
if(start<buffer.length()){
delPrevText=false;
if(buffer.charAt(start)==' '){
buffer.replace(start,start+1,"");
}
}
}
}
}
};

Try adding a TextWatcher to the MultiAutoCompleteTextView.
Save the current text and check if the last space was deleted.
If so, remove the last token.

Editable buffer = s;
int start = multiContentText.getSelectionStart()<0?0:multiContentText.getSelectionStart();
int end = multiContentText.getSelectionEnd()<0?0:multiContentText.getSelectionEnd();
if(noOfCharAdded==0 && noOfCharDeleted==1){ //if space is deleted
if (start == end && delPrevText) {
ImageSpan link[] = buffer.getSpans(start, end,ImageSpan.class);
if (link.length > 0) {
for(int i=0;i<contentArray.size();i++){
JSONObject jo=contentArray.get(i);
try {
int keyValue=jo.getInt("startIndx");//No i18N
if(keyValue==buffer.getSpanStart(link[0])){
jo.put("isRemoved", true);
contentArray.set(i,jo);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
buffer.replace(buffer.getSpanStart(link[0]),buffer.getSpanEnd(link[0]),"");
buffer.removeSpan(link[0]);
}
}
delPrevText=true;
multiContentText.setSelection(multiContentText.getText().length());
}
else if(noOfCharAdded==0 && noOfCharDeleted>1){//if the whole word is deleted
if(buffer.length()>0){
if(start<buffer.length()){
delPrevText=false;
for(int i=0;i<contentArray.size();i++){
JSONObject jo=contentArray.get(i);
try {
int keyValue=jo.getInt("startIndx");//No i18N
if(keyValue==start){
jo.put("isRemoved", true);
contentArray.set(i,jo);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(buffer.charAt(start)==' '){
buffer.replace(start,start+1,"");
}
}
}
}

Related

MultiAutoCompleteTextView doesn't accept blank spaces

My problem is that when I suggest sentences in MultiAutoCompleteTextView, when I press spacebar, the suggestions disappear.
Example:
Suggested words:
THE ROCK THE BALL THERMAL
If I write "THE", all sentences are displayed, but if I write "THE " (with blank space) the suggestions are dismissed.
I want that if you write "THE ", the elements "THE ROCK" and "THE BALL" are displayed in suggested words.
Thanks.
You should implement MultiAutoCompleteTextView.Tokenizer and create a spaceTokenizer as below. Then set multiAutoCompleteTextView.setTokenizer(new SpaceTokenizer());
public class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
}
Have a look at this post:
https://groups.google.com/forum/#!topic/android-developers/OrsN2xRpDmA
I just ran into a similar problem and wrote a simple multi word derivation. It defaults to a "," separator but you can set it whatever you like using the "setSeparator" method.
or
this stack-overflow answer might be helpful cause it experiences the same problem:
AutoCompleteTextView backed by CursorLoader

#Mention using Multiautocompletetextview

What i want to do
i want to mention both people and group in a chat activity.when the user types # i want populate the list which contains no of user and channels
so that he can mention both people and channels
What i have done
i took help from this question Android: Autocomplete TextView Similar To The Facebook App
here i have included my code
1.Arraylist which has users
ArrayList<People> users = new ArrayList<User>();
for (People user : SocketSingleton.userMap.values()) {
if (user.getId() != loggedUserId) {
users.add(user);
}
}
2.Arraylist which has groups list
ArrayList<Group> groups = new ArrayList<Group>();
for (Group channel : SocketSingleton.listgroups.values()) {
groups.add(channel);
}
3.Adapters for view
final UserAdapter Adapter = new UserAdapter(getActivity(), R.layout.all_user_list_item, users);
final GroupAdapter Adapter1 = new GroupAdapter(getActivity(), R.layout.all_groups_list_item, Groups);
4.MultiAutocompletetextview
textinput = (MultiAutoCompleteTextView) view.findViewById(R.id.message_input);
textinput.setAdapter(Adapter);
textinput.setThreshold(0);
textinput.setTokenizer(new MultiAutoCompleteTextView.Tokenizer() {
#Override
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
#Override
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != '#') {
i--;
}
//Check if token really started with #, else we don't have a valid token
if (i < 1 || text.charAt(i - 1) != '#') {
return cursor;
}
return i;
}
#Override
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
});
textinput.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Layout layout = textinput.getLayout();
int pos = textinput.getSelectionStart();
int line = layout.getLineForOffset(pos);
int baseline = layout.getLineBaseline(line);
int bottom = textinput.getHeight();
textinput.setDropDownVerticalOffset(baseline - bottom);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
textinput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int id, KeyEvent event) {
if (id == R.id.send || id == EditorInfo.IME_NULL) {
messageSend();
return true;
}
return false;
}
});
Problem i have
with this code i can able to mention only people. i can only set either adapter1 or adapter2 for Autocomplete tectview. So it is only displaying either users or groups.
i want to display all list in single adapter so that the user can mention peoples and groups
i am new to this please help me to find a way
There is no built in support for multiple data types with MultiAutoCompleteTextView. However there are several open source libraries that allow you to do just that.
I've had the same requirement in the project I'm working on and here is our open source solution, feel free to check it out:
https://github.com/Teamwork/android-multiautocomplete
You can achieve that by doing this:
MultiAutoComplete autoComplete = new MultiAutoComplete.Builder()
...
.addTypeAdapter(typeAdapter1)
.addTypeAdapter(typeAdapter2)
.build();
You can also specify the type of filter and handle ('#' or '#' or any other) you want to use for each type adapter.

How to show dropdown only when inserting # character on MultiAutoCompleteTextView in android

I have a MultiAutoCompleteTextView. It works fine. But I want to show suggestion dropdown only when user type # on it (like tagging user in facebook app). I have no idea how to do it. Here is my code :
mChatbox = (MultiAutoCompleteTextView) findViewById(R.id.chatbox);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line, userList);
mChatBox.setAdapter(adapter);
mChatBox.setTokenizer(new SpaceTokenizer());
public class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
Create a custom Text view extending MultiAutoCompleteTextView -> override enoughToFilter() -> set the threshold to 0 (the bold variable in the below given code) :
public boolean enoughToFilter() {
Editable text = getText();
int end = getSelectionEnd();
if (end < 0 || mTokenizer == null) {
return false;
}
int start = mTokenizer.findTokenStart(text, end);
if (end - start >= mThreshold && start != -1) {
return true;
} else {
return false;
}
}
Using this code you'll see the auto suggested list on press of #
If you want to detect your string starts with '#' for mention (tag) someone or '#' for hashTag, then do query or filter with it, you could follow this code belows:
#Override
public void onTextChanged(CharSequence s, int start, int before, final int count) {
if (s.length() > 0) {
// Todo: query mentions
Matcher mentionMatcher = Pattern.compile("#([A-Za-z0-9_-]+)").matcher(s.toString());
// while matching
while (mentionMatcher.find()) {
yourSearchText = s.toString().substring(mentionMatcher.start() + 1, mentionMatcher.end());
// do query with yourSearchText below
}
}
}
It references from the link Multiautocompletetextview, Show autocomplete drop down only when user presses a key after '#' key (like mention in FB app) please scroll down to find #Phuong Sala answer.
I got a solution by myself. I create custom view which extends MultiAutoCompleteTextView and override performFiltering in it. Check if first char is "#", then filter the next chars after it. Otherwise, replace chars with "*" to avoid filtering. Here is my code.
#Override
protected void performFiltering(CharSequence text, int start, int end, int keyCode) {
if (text.charAt(start) == '#') {
start = start + 1;
} else {
text = text.subSequence(0, start);
for (int i = start; i < end; i++) {
text = text + "*";
}
}
super.performFiltering(text, start, end, keyCode);
}

MultiAutoCompleteTextView strings getting "," after selecting string from drop down?

i integrated MultiAutoCompleteTextView in to my dictionary app so now after integrating it.
i got a problem like this as show in second screen shot...
so now i don't want that ","
as show in second screen shots after !
i am getting "," like this
!,
plz help me
thank q
String[] str={"!","\"","#","$","$1","%","'","+",",","/"};
MultiAutoCompleteTextView mt=(MultiAutoCompleteTextView)findViewById(R.id.searchEditText);
mt.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
ArrayAdapter<String> adp=new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,str);
mt.setThreshold(1);
mt.setAdapter(adp);
The comma is coming from CommaTokenizer() called in this line mt.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
To remove the comma you need to implement your own Tokenizer. This is an example:
public class SpaceTokenizer implements Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
}
PS: I copied the code from this answer

More than one delimiter for MultiAutoCompleteTextView?

I am using a MultiAutoCompleteTextView which shows suggestions for user input. It only works when the items are separated by one or more spaces, but doesn't when a new line (i.e. button "enter" is pressed) is the delimiter.
The code so far (I think I got it from stackoverflow some time ago):
public class SpaceTokenizer implements Tokenizer {
#Override
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
#Override
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
#Override
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
}
I tried to implement something like "... || text.charAt(i) == '\n' ..." where I thought appropriate, but that didn't work.
So I would be very thankful for suggestions!
The answer to my question can be found here:
Android and the CommaTokenizer

Categories

Resources