Code Monkey home page Code Monkey logo

tokenautocomplete's Introduction

Android CI License Maven Central

Version 3.0

The 3.0.1 version is now available! This should resolve a number of text handling issues and lay the groundwork for better support of mixed text and token input. If you're still on 2.*, you can find the docs for 2.0.8 here.

Upgrading from 2.* to 3.0

For most developers, the migration should be fairly simple. Here are the likely issues you'll need to resolve:

  1. The view now inherits from AppCompatAutoCompleteTextView. You probably don't need to make any changes for this, but you will need to include the Android support library if you are not already.

  2. setTokenDeleteStyle has been removed. Something similar to the Clear style has been hardcoded in. This feature never worked reliably and caused a lot of crashes.

  3. addObject has been renamed addObjectAsync. removeObject has been renamed removeObjectAsync. There are also addObjectSync/removeObjectSync versions that can be called from the UI thread and guarantee that getObjects will include these changes on the next call.

  4. setAllowDuplicates(false) has been made more flexible to deal with issues around different kinds of equality. If you need the 2.* version of the behavior, add this to your TokenCompleteTextView subclass:

@Override
public boolean shouldIgnoreToken(T token) {
    return getObjects().contains(token);
}
  1. TokenListener has a new method you will need to add:
public interface TokenListener<T> {
    void onTokenAdded(T token);
    void onTokenRemoved(T token);
    void onTokenIgnored(T token);
}
  1. convertSerializableArrayToObjectArray has been renamed convertSerializableObjectsToTypedObjects.

You may also find that the vertical alignment of your tokens has changed. It appears that the app compat text view layout is slightly different than the normal one. You will likely find that you need to adjust the baseline values for your token views.

There have been a number of under the hood changes to the text handling, so if you've been directly accessing the text or using your own tokenizer, you may need to make more changes than this.

Upgrading from 1.* to 2.0

There is one breaking change from 1.* to 2.0. You need to extend TokenCompleteTextView<Object> instead of TokenCompleteTextView.

TokenAutoComplete

TokenAutoComplete is an Android Gmail style token auto-complete text field and filter. It's designed to have an extremely simple API to make it easy for anyone to implement this functionality while still exposing enough customization to let you make it awesome.

Support for Android 4.0.3 (API 14) and up. If you need support for earlier versions of Android, version 1.2.1 is the most recent version that supports Android 2.2 (API 8) and up.

Focused TokenAutoCompleteTextView example

Unfocused TokenAutoCompleteTextView example

Setup

Gradle

dependencies {
    implementation "com.splitwise:tokenautocomplete:3.0.1@aar"
}

Maven

<dependency>
  <groupId>com.splitwise</groupId>
  <artifactId>tokenautocomplete</artifactId>
  <version>3.0.1</version>
  <type>aar</type>
</dependency>

No build tools

Download the jar file and add it to your project

If you would like to get the most recent code in a jar, clone the project and run ./gradlew jar from the root folder. This will build a tokenautocomplete.jar in library/build/libs/.

You may also add the library as an Android Library to your project. All the library files live in library.

Creating your auto complete view

If you'd rather just start with a working example, clone the project and take a look.

For a basic token auto complete view, you'll need to

  1. Subclass TokenCompleteTextView
  2. Create a layout and activity for your completion view

Subclass TokenCompleteTextView

You'll need to provide your own implementations for getViewForObject and defaultObject. You should return a view that displays the token from getViewForObject. In defaultObject, you need to guess what the user meant with their completion. This is usually from the user typing something and hitting "," - see the way gmail for Android handles this for example. Here's a simple example:

public class ContactsCompletionView extends TokenCompleteTextView<Person> {
    public ContactsCompletionView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected View getViewForObject(Person person) {

        LayoutInflater l = (LayoutInflater) getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        TextView view = (TextView) l.inflate(R.layout.contact_token, (ViewGroup) getParent(), false);
        view.setText(person.getEmail());

        return view;
    }

    @Override
    protected Person defaultObject(String completionText) {
        //Oversimplified example of guessing if we have an email or not
        int index = completionText.indexOf('@');
        if (index == -1) {
            return new Person(completionText, completionText.replace(" ", "") + "@example.com");
        } else {
            return new Person(completionText.substring(0, index), completionText);
        }
    }
}

Layout code for contact_token

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/token_background"
    android:padding="5dp"
    android:textColor="@android:color/white"
    android:textSize="18sp" />

Token background drawable

<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="#ffafafaf" />
    <corners android:radius="5dp" />
</shape>

Person object code

public class Person implements Serializable {
    private String name;
    private String email;

    public Person(String n, String e) { name = n; email = e; }

    public String getName() { return name; }
    public String getEmail() { return email; }

    @Override
    public String toString() { return name; }
}

Note that the class implements Serializable. In order to restore the view state properly, the TokenCompleteTextView needs to be able to save and restore your objects from disk. If your objects cannot be made Serializable, please look at restoring the view state.

Create a layout and activity for your completion view

I'm adding some very stupid "contacts" to the app so you can see it work, but you should read data from the contacts data provider in a real app.

Activity code

public class TokenActivity extends Activity {
    ContactsCompletionView completionView;
    Person[] people;
    ArrayAdapter<Person> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        people = new Person[]{
                new Person("Marshall Weir", "[email protected]"),
                new Person("Margaret Smith", "[email protected]"),
                new Person("Max Jordan", "[email protected]"),
                new Person("Meg Peterson", "[email protected]"),
                new Person("Amanda Johnson", "[email protected]"),
                new Person("Terry Anderson", "[email protected]")
        };

        adapter = new ArrayAdapter<Person>(this, android.R.layout.simple_list_item_1, people);

        completionView = (ContactsCompletionView)findViewById(R.id.searchView);
        completionView.setAdapter(adapter);
    }
}

Layout code

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.yourpackagename.ContactsCompletionView
        android:id="@+id/searchView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

That's it! You can grab the objects the user tokenized with getObjects() on the TokenCompleteTextView when you need to get the data out.

Setting a prefix prompt

If you have a short prompt like "To: ", you can probably get away with setting a drawable on the left side of the TokenCompleteTextView. If you have something longer, you will probably not want your prefix to take up the whole height of the view. If you would like to have a prefix that only indents the first line, you should use setPrefix. This code is a little quirky when restoring the activity, so you want to make sure it only gets called on a fresh start in onCreate:

if (savedInstanceState == null) {
    completionView.setPrefix("Your bestest friends: ");
}

Custom filtering

If you've used the gmail auto complete, you know that it doesn't use the default "toString" filtering you get with an ArrayAdapter.

I've added my own FilteredArrayAdapter to the jar file that is a subclass of ArrayAdapter but has some good hooks for custom filtering. You'll want to be fairly efficient in this as it gets called a lot, but it's a simple process to add a custom filter. If you are using the TokenActivity above, you simply replace the line

adapter = new ArrayAdapter<Person>(this, android.R.layout.simple_list_item_1, people);

with

adapter = new FilteredArrayAdapter<Person>(this, android.R.layout.simple_list_item_1, people) {
    @Override
    protected boolean keepObject(Person obj, String mask) {
        mask = mask.toLowerCase();
        return obj.getName().toLowerCase().startsWith(mask) || obj.getEmail().toLowerCase().startsWith(mask);
    }
};

Duplicate objects

In addition to custom filtering, you may want to make sure you don't get duplicate tokens. In your TokenCompleteTextView subclass, override shouldIgnoreToken:

@Override
public boolean shouldIgnoreToken(T token) {
    return getObjects().contains(token);
}

Any text the user entered for the duplicate token will be cleared. You can implement whatever matching behavior you need. This implementation assumes the equals method on your token objects is a reasonable comparison.

Responding to user selections

If you're solving a similar problem to Splitwise, you need to handle users adding and removing tokens. I've provided a simple interface to get these events and allow you to respond to them in the TokenCompleteTextView:

public static interface TokenListener<T> {
    public void onTokenAdded(T token);
    public void onTokenRemoved(T token);
    public void onTokenIgnored(T token)
}

We can modify the TokenActivity to see how these callbacks work:

public class TokenActivity extends Activity implements TokenCompleteTextView.TokenListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        /* code from the initial example */

        completionView.setTokenListener(this);
    }

    @Override
    public void onTokenAdded(Person token) {
        System.out.println("Added: " + token);
    }

    @Override
    public void onTokenRemoved(Person token) {
        System.out.println("Removed: " + token);
    }

    @Override
    public void onTokenIgnored(Person token) {
        System.out.println("Ignored: " + token);
    }
}

In Splitwise we use these callbacks to handle users selecting a group when adding an expense. When a user adds a group to an expense, we remove all the users in the group and the other groups from the array adapter. A user should only be able to select one group and it would be redundant to add users in the group to the expense again.

Programatically add and remove objects

You may want to prefill the list with objects. For example when replying to an email, you would want the To: and CC: fields to have the correct emails in them. You can use addObjectSync to put these tokens in. You can also remove objects programatically with removeObjectSync though this will remove all objects that return true when calling equals on them. If you have copies in the array, you may need to take special care with this.

The Sync versions of these methods must be called from the UI thread. There are also addObjectAsync and removeObjectAsync that can be called from any thread, but will not update the view or data immediately. Finally, there is a clearAsync function to empty the EditText and remove all the objects.

Letting users click to select and delete tokens

There are four different styles of click handling build into the project. Call setTokenClickStyle to change the behavior. If you need more control over how click behavior works, please see issue #350.

TokenCompleteTextView.TokenClickStyle.None

This is the default, even though it doesn't match the Gmail behavior. When the user clicks on a token, the view will move the cursor in front of or after the token. Users should not be able to get the cursor in the token as this causes confusing behavior.

TokenCompleteTextView.TokenClickStyle.Delete

When the user clicks on a token, the token will be removed from the field. If you need some kind of confirmation, handle it with the onTokenRemoved callback and re-add the token if the user changes their mind.

TokenCompleteTextView.TokenClickStyle.Select

This behavior most closely matches the Gmail token field behavior, but I did not make it the default to simplify the initial tutorial. The first click on a token will unselect any currently selected token views, then it will call setSelected(true) on the selected token.

TokenCompleteTextView.TokenClickStyle.SelectDeselect

This works the same as Select except that a second click on the token will deselect it and call setSelected(false).

Showing token selected state

If you want to change the colors of the token when it is selected, you will need to add appropriate drawables to your project. In the test project, we have the following:

token_background.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/token_default" android:state_selected="false" />
    <item android:drawable="@drawable/token_selected" android:state_selected="true" />
</selector>

token_default.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <stroke
        android:width="1dp"
        android:color="#ffd4d4d4" />
    <solid android:color="#ffafafaf" />

    <corners android:radius="3dp"/>
</shape>

token_selected.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <stroke
        android:width="1dp"
        android:color="#ffa4a4a4" />
    <solid android:color="#ff7a7a7a" />

    <corners android:radius="3dp"/>
</shape>

If you need more detailed view customization like changing a picture in the token or resizing the token, you will need to provide a custom view to use in the layout you inflate in getViewForObject and override setSelected in that view. You can then make appropriate changes to the view.

Example custom view

In a view implementation (see com.tokenautocomplete.TokenTextView):

public class TokenTextView extends TextView {

    ...

    @Override
    public void setSelected(boolean selected) {
        super.setSelected(selected);
        setCompoundDrawablesWithIntrinsicBounds(0, 0, selected ? R.drawable.close_x : 0, 0);
    }
}

contact_token.xml

<com.tokenautocomplete.TokenTextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/token_background"
    android:textColor="@android:color/white"
    android:textSize="14sp"
    android:maxLines="1"
    android:ellipsize="end"
    android:padding="4dp"
    tools:text="Glenda Jönsson" />

Inflate your custom view:

public class ContactsCompletionView extends TokenCompleteTextView<Person> {

    ...

    @Override
    protected View getViewForObject(Person person) {
        LayoutInflater l = (LayoutInflater)getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        TokenTextView token = (TokenTextView) l.inflate(R.layout.contact_token, (ViewGroup) getParent(), false);
        token.setText(person.getEmail());
        return token;
    }
}

Restoring the view state

If your token objects implement Serializable or Parcelable, the TokenCompleteTextView will automatically handle onSaveInstanceState and onRestoreInstanceState. If you cannot make your objects Serializable or Parcelable, you should override getSerializableObjects and convertSerializableObjectsToTypedObjects. getSerializableObjects should return an array of Serializable objects that can be used to rebuild your original objects when restoring the view state. convertSerializableObjectsToTypedObjects should take an array of Serializable objects and use them to rebuild your token objects.

We use something similar to this at splitwise to avoid saving complicated object graphs:

@Override
protected ArrayList<Object> convertSerializableObjectsToTypedObjects(ArrayList<Serializable> sers) {
    ArrayList<Object> objs = new ArrayList<Object>();
    for (Serializable s: sers) {
        if (s instanceof Long) {
            Contact c = Contact.loadFromDatabase((Long)s);
            objs.add(c);
        } else {
            objs.add(s);
        }
    }

    return objs;
}

@Override
protected ArrayList<Serializable> getSerializableObjects() {
    ArrayList<Serializable> s = new ArrayList<Serializable>();
    for (Object obj: getObjects()) {
        if (obj instanceof Serializable) {
            s.add((Serializable)obj);
        } else {
            //obj is a Contact
            s.add(((Contact)obj).getId());
        }
    }
    return s;
}

Other options

  • Turn off making a best guess when converting text into a token
performBestGuess(false);
  • Prevent the TokenCompleteTextView collapsing to a single line when it loses focus
allowCollapse(false);
  • Change the set of characters that complete a token
setTokenizer(new CharacterTokenizer(Arrays.asList('.', ','), ","));
  • Detect tokens based on their first character
//Detect @usernames and #hashtags
setTokenizer(new TagTokenizer(Arrays.asList('@', '#')));
  • Change the number of characters required to start showing suggestions
setThreshold(1);
  • Limit the total number of tokens in the field
setTokenLimit(10);
  • Prevent specific tokens from being deleted by overriding isTokenRemovable on your completion view

Experimental mixed freeform text and token input support

These options should allow you to build something similar to a Tweet composing view, but is likely to still have some edge cases with unusual behavior.

  • Allow mixed text and token input
preventFreeFormText(false);
  • Get the string value of the text content of the view, including reasonable string representations of the tokens. If getContextText is not using an acceptable string representation of the token, you can override tokenToString to change how the token is represented.
getContentText();

License

Copyright (c) 2013, 2014 splitwise, Wouter Dullaert

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

tokenautocomplete's People

Contributors

a-mehta avatar ahuang13 avatar alexcustos avatar arriolac avatar bfichter avatar bryant1410 avatar cketti avatar geoffquest avatar goooler avatar j-jamet avatar jchaager avatar jospint avatar madhur avatar madsonic avatar marcelbucz avatar mdellavo avatar mgod avatar micaelomota avatar prattpratt avatar robhor avatar ross avatar rossimo avatar ruckus avatar scottyab avatar sirrah avatar snarfed avatar st1hy avatar sweggersen avatar wdullaer avatar wjwarren avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tokenautocomplete's Issues

Selected token custom layout

Hi. I am using your library in my project. HOwever, I am having trouble at one place. My token contains only Person's name. When the token is not selected, the token's background is white and it's text color is black. On the other hand, if the token is not selected/default, the token's background should be black and it's text color should be white colored. I used a selector xml to change the token's background, however, I don't know how to change the text's color.

The selector xml is working perfectly fine and the background is changing when the token and unselected, but, the text's color remains same.

I used below setSelected() method to change the text's color. But it's not working.

    @Override
public void setSelected(boolean selected) {
    super.setSelected(selected);
    TextView tv = (TextView) findViewById(R.id.token_name);
    if(selected) {
        tv.setTextColor(Color.WHITE);
    } else {
        tv.setTextColor(Color.BLACK);
    }
}

Please help me out.

override getText() method

Please tell me how to get raw text o TokenCompletetextView. rigth now when I call getText.toString i get string that looks like that: "Hello ,, who was your weekend? did you see ,, today?"

It would be very handy to have customize getText method. But in meanwhile how should i approach replacing the double comma with contacts from the list that would support duplicates?

Showing an image loaded from the network in the token

What I would like to do is display an image right next to text as a token. This image is being loaded from the network but when I update the View I don't see the image reflected. I believe this is because a drawable is being created for the token View and thus cannot be updated.

Add a prefix prompt to the field

I'd like to be able to easily configure something like this:

token

I can make something like this work by adding padding to the left side of the field, but I want the tokens to wrap underneath the prompt as new lines are added.

Add tokens horizontally instead of vertical stacking

Hi, first off the library is great! I had to modify the adapter heavily, but other than that almost everything works out of the box. I was wondering if there is an easy way to have the tokens added horizontally to the right of the last one added. And if there are too many to display it will add a scrollbar to scroll horizontally. The big thing I need is to only use one line. Right now the behavior is to stack the overflow tokens vertically into several lines.

I have tried using the singleline="true" combined with scrollHorizontally="true" but that doesn't seem to work which is what I normally use for TextView. I have also tried wrapping it in a HorizontalScrollView, but that gives bizarre results. Any clues on what to look at?

How to mimic touching on items like on Gmail/Hangouts, with different layouts ?

Gmail & Hangouts apps allow to touch the items in order to (optionally) delete them.
You tap on an item, and instead of the photo you get an "X" which means that if you click again, it will get removed.

In this library , using "setTokenClickStyle" , when clicking on an item, I get a different background .
How can I make the library work like on Gmail/Hangouts in this regard, so that the layout would change instead of the background

User Added Tokens Not In Adapter

I'm using the tokens for hashtags. I have a preloaded FilteredArrayAdapter with a list of suggestions but I also want on the space key press to have whatever the user has typed in to be set as a token. Is there a way to create this functionality?

Didn't find class runtime error.

I'm fairly certain I've followed the example from the "Creating your auto complete view" section correctly. However when I call the activity with the ContactsCompletionView it throws java.lang.RuntimeException: Unable to start activity ComponentInfo{com.lucas.example/com.lucas.example.AddMarkerDetailsActivity}: android.view.InflateException: Binary XML file line #30: Error inflating class com.tokenautocomplete.ContactsCompletionView. What is it I'm doing wrong? The full trace for the error is below.

05-12 21:28:39.373: E/AndroidRuntime(5809): FATAL EXCEPTION: main
05-12 21:28:39.373: E/AndroidRuntime(5809): Process: com.lucas.example, PID: 5809
05-12 21:28:39.373: E/AndroidRuntime(5809): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.lucas.example/com.lucas.example.AddMarkerDetailsActivity}: android.view.InflateException: Binary XML file line #30: Error inflating class com.tokenautocomplete.ContactsCompletionView
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.ActivityThread.access$800(ActivityThread.java:135)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.os.Handler.dispatchMessage(Handler.java:102)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.os.Looper.loop(Looper.java:136)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.ActivityThread.main(ActivityThread.java:5017)
05-12 21:28:39.373: E/AndroidRuntime(5809): at java.lang.reflect.Method.invokeNative(Native Method)
05-12 21:28:39.373: E/AndroidRuntime(5809): at java.lang.reflect.Method.invoke(Method.java:515)
05-12 21:28:39.373: E/AndroidRuntime(5809): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
05-12 21:28:39.373: E/AndroidRuntime(5809): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
05-12 21:28:39.373: E/AndroidRuntime(5809): at dalvik.system.NativeStart.main(Native Method)
05-12 21:28:39.373: E/AndroidRuntime(5809): Caused by: android.view.InflateException: Binary XML file line #30: Error inflating class com.tokenautocomplete.ContactsCompletionView
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:707)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
05-12 21:28:39.373: E/AndroidRuntime(5809): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.Activity.setContentView(Activity.java:1929)
05-12 21:28:39.373: E/AndroidRuntime(5809): at com.lucas.example.AddMarkerDetailsActivity.onCreate(AddMarkerDetailsActivity.java:35)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.Activity.performCreate(Activity.java:5231)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
05-12 21:28:39.373: E/AndroidRuntime(5809): ... 11 more
05-12 21:28:39.373: E/AndroidRuntime(5809): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.tokenautocomplete.ContactsCompletionView" on path: DexPathList[[zip file "/data/app/com.lucas.example-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.lucas.example-1, /vendor/lib, /system/lib]]
05-12 21:28:39.373: E/AndroidRuntime(5809): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
05-12 21:28:39.373: E/AndroidRuntime(5809): at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
05-12 21:28:39.373: E/AndroidRuntime(5809): at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.view.LayoutInflater.createView(LayoutInflater.java:559)
05-12 21:28:39.373: E/AndroidRuntime(5809): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:696)

Tokens with expandable views get cut off if last item

Double clicking tokens to remove wasn't working out for me since it is really easy to trigger with normal horizontal scrolling behavior. Instead I have made it so that if you click a token it will create a "close button" on the right which the user can then press to close. When the button is revealed it pushes all tokens on its right over slightly to accommodate for its width. If the token is deselected then the button becomes hidden again.

Through much hacking and fighting with issues caused by nested clickable buttons (TokenImageSpan.onClick() does not play nice) I finally got a working version. The only issue I've found is if the user has entered enough tokens to trigger horizontal scrolling (using singleLine), then they aren't able to click the last token's button. The Button is created but pushed off the widget, and there's no space added to scroll over more to see it. I can see the left edge of it if I enable the debug layout boundaries view, but still can't scroll to it.

I've tried invalidating the view to have it redraw everything but nothing seems to work. Any idea? Not sure if this bug is with the library or the android widget you extended. If you have any suggestions on implementing a clean version of this I'd be all ears as well. Right now it's very ugly and I have to compare the X value of the motion event to the button boundaries since I couldn't get the click to trigger otherwise. Thanks.

Collapse field when not focused

The Android Gmail To field collapses back down to one line and displays "+x" at the end of the list, where x is the number of tokens hidden by collapsing. These tokens appear again when the field is focused

Bug: Text not being reset after item selection

On Galaxy S3 with Android 4.3, there is a weird bug:

  1. open the sample project.
  2. start typing "ma" .
  3. choose the last item ("Max Jordan") .
  4. you see just the new item being added, no text next to it (which is what it should be).
  5. continue typing - write "t"

Expected result: seeing the new item, and only the new text next to it.
Actual result: seeing the new item, and the text "mat" next to it.

note that for some reason, I can't confirm this on other devices, like the Nexus 4 (has the same Android version).

The bug might be related to another bug I've noticed: when choosing an item and then pressing backspace, it doesn't remove the item AND its text. only the item. So if you typed "ma", chose "Max Jordan", and pressed backspace, the old text of "ma" is shown.


EDIT: Another 2 bugs I've found is on Galaxy S2 with Android v4.0.4 :
The first bug is:
1.enter "ma".
2. choose "Max Jordan".
3.press backspace.

Expected result: it should delete the item that was added.
Actual result: the suggestions list popup appears, as if you've typed "ma" (showing 3 items on the list to choose from). However, only the new item is shown .

the second bug is a rare crashing bug, which I cannot reproduce but I think is related to focus.
Here's the log:
04-03 09:46:31.985: E/AndroidRuntime(21519): FATAL EXCEPTION: main
04-03 09:46:31.985: E/AndroidRuntime(21519): java.lang.IndexOutOfBoundsException: setSpan (3 ... 3) ends beyond length 2
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:945)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:527)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:520)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.Selection.setSelection(Selection.java:76)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.Selection.setSelection(Selection.java:87)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.widget.EditText.setSelection(EditText.java:108)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.tokenautocomplete.library.TokenCompleteTextView.onSelectionChanged(TokenCompleteTextView.java:456)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.widget.TextView.spanChange(TextView.java:8409)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.widget.TextView$ChangeWatcher.onSpanChanged(TextView.java:8740)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.SpannableStringBuilder.sendSpanChanged(SpannableStringBuilder.java:927)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:583)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:520)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.Selection.setSelection(Selection.java:78)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.text.Selection.setSelection(Selection.java:87)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.widget.EditText.setSelection(EditText.java:108)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.tokenautocomplete.library.TokenCompleteTextView.handleFocus(TokenCompleteTextView.java:524)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.tokenautocomplete.library.TokenCompleteTextView.onFocusChanged(TokenCompleteTextView.java:540)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.View.handleFocusGainInternal(View.java:3796)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.View.requestFocus(View.java:5489)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.View.requestFocus(View.java:5439)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.View.requestFocus(View.java:5417)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.View.onTouchEvent(View.java:6643)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.widget.TextView.onTouchEvent(TextView.java:9010)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.tokenautocomplete.library.TokenCompleteTextView.onTouchEvent(TokenCompleteTextView.java:422)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.View.dispatchTouchEvent(View.java:5717)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1962)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1731)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1962)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1731)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1962)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1731)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1962)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1731)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1962)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1731)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2064)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1400)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.app.Activity.dispatchTouchEvent(Activity.java:2369)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2012)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.View.dispatchPointerEvent(View.java:5897)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3115)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2657)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewRootImpl.processInputEvents(ViewRootImpl.java:1021)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2666)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.os.Handler.dispatchMessage(Handler.java:99)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.os.Looper.loop(Looper.java:137)
04-03 09:46:31.985: E/AndroidRuntime(21519): at android.app.ActivityThread.main(ActivityThread.java:4511)
04-03 09:46:31.985: E/AndroidRuntime(21519): at java.lang.reflect.Method.invokeNative(Native Method)
04-03 09:46:31.985: E/AndroidRuntime(21519): at java.lang.reflect.Method.invoke(Method.java:511)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
04-03 09:46:31.985: E/AndroidRuntime(21519): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
04-03 09:46:31.985: E/AndroidRuntime(21519): at dalvik.system.NativeStart.main(Native Method)

allowDuplicates(false) not working with SimpleCursorAdapter

I think that i find a little bug.
allowDuplicates(false) appear dont work when is used with a SimpleCursorAdapter.

For reproduce it, this is my code.

Custom TokenCompleteTextView

public class ContactCompleteTextView extends TokenCompleteTextView {

    public ContactCompleteTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        allowDuplicates(false);
        setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.Delete);
    }

    @Override
    protected View getViewForObject(Object object) {
        if (object != null) {
            Cursor c = (Cursor) object;

            LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE);
            LinearLayout view = (LinearLayout) inflater.inflate(R.layout.contact_token,
                    (ViewGroup) ContactCompleteTextView.this.getParent(), false);

            String name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            ((TextView) view.findViewById(R.id.name)).setText(name);
            return view;
        }
        return null;
    }

    @Override
    protected Object defaultObject(String completionText) {
        // Avoid creation
        return null;
      }
  }

Initialization of adapter and view

        final String contactIdCol = ContactsContract.CommonDataKinds.Phone._ID;
        final String contactNameCol = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME;
        final String contactPhoneCol = ContactsContract.CommonDataKinds.Phone.NUMBER;

        int[] displayViews = new int[] { android.R.id.text1, android.R.id.text2 };
        String[] displayCols = new String[] {
                contactNameCol,
                contactPhoneCol
        };

        final String[] queryProjection = new String[] {
                contactIdCol,
                contactNameCol,
                contactPhoneCol
        };

        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_2, null, displayCols, displayViews, 0);

        adapter.setFilterQueryProvider(new FilterQueryProvider() {
            public Cursor runQuery(CharSequence constraint) {
                return getContentResolver().query(
                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        queryProjection,
                        contactNameCol + " LIKE '%" + constraint + "%'",
                        null, null);
            }
        });

        mTtvContacts.setAdapter(adapter);

NullPointerException in TextView.getExtendedPaddingTop in API Level 8

How to reproduce:

  • Run the example project in emulator with API Level 8
  • If the view is focused, select other focusable view first
  • Click (touch) the view
  • And then...
    FATAL EXCEPTION: main
    java.lang.NullPointerException
        at android.widget.TextView.getExtendedPaddingTop(TextView.java:1240)
        at android.widget.TextView.getTotalPaddingTop(TextView.java:1318)
        at com.tokenautocomplete.TokenCompleteTextView$TextPositionCompatibilityAPI8.getLineAtCoordinate(TokenCompleteTextView.java:879)
        at com.tokenautocomplete.TokenCompleteTextView$TextPositionCompatibilityAPI8.getOffsetForPosition(TokenCompleteTextView.java:865)
        at com.tokenautocomplete.TokenCompleteTextView.onTouchEvent(TokenCompleteTextView.java:295)
        at android.view.View.dispatchTouchEvent(View.java:3766)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1671)
        at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
        at android.app.Activity.dispatchTouchEvent(Activity.java:2086)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1655)
        at android.view.ViewRoot.handleMessage(ViewRoot.java:1785)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:123)
        at android.app.ActivityThread.main(ActivityThread.java:4627)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:521)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
        at dalvik.system.NativeStart.main(Native Method)

Random crash when deleting a token

A user got this crash 3 times, but I'm unable to reproduce now. I think this happens randomly.

Crash log below:

Android: 4.4.2
Manufacturer: motorola
Model: XT1033

java.lang.IndexOutOfBoundsException: setSpan (23 ... 23) ends beyond length 22
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1016)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:592)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:588)
at android.text.Selection.setSelection(Selection.java:76)
at android.text.Selection.setSelection(Selection.java:87)
at android.widget.EditText.setSelection(EditText.java:94)
at com.tokenautocomplete.TokenCompleteTextView.onSelectionChanged(Unknown Source)
at android.widget.TextView.spanChange(TextView.java:7528)
at android.widget.TextView$ChangeWatcher.onSpanChanged(TextView.java:9215)
at android.text.SpannableStringBuilder.sendSpanChanged(SpannableStringBuilder.java:999)
at android.text.SpannableStringBuilder.sendToSpanWatchers(SpannableStringBuilder.java:567)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:500)
at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:212)
at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:30)
at android.view.inputmethod.BaseInputConnection.deleteSurroundingText(BaseInputConnection.java:244)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:382)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:77)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5102)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)

Dont do anything in defaultObject()

Hello

I want dont do anything when the user create a text and press enter, My app only can take values of the adapter. Repeat, i dont want create any token if this donts exist.

What i can do it?
Im trying return null without sucess, the app crash.

Thanks.

(possibly) need to override buildSpanForObject

first off, awesome stuff. it's working almost perfectly out of the box.

i'm backing the TokenCompleteTextView with a SimpleCursorAdapter querying the phone's local contacts via, ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI

it's working almost perfectly, but the thing i'm running in to is that results in the tokens being Cursor's. that mostly works but gets a little ugly in a few of ways.

  1. it's keeping the cursors around for a long time and not providing a good place to close/get rid of them and in some cases (like deleting tokens) accesses them after their source has been closed causing exceptions.
  2. it makes things like defaultObject a little tricky since to match everything else it has to return a Cursor (implementation, which can be done, but feels wrong)
  3. Cursor isn't Serializable, this can be worked around by overriding the to/from objects stuff, but circles back to point 2 in having to make fake cursors.

it seems like the easiest solution would be to override buildSpanForObject and turn the cursor in to a simple "Contact" object and then call super with that simplified object. i.e.

@Override
protected TokenImageSpan buildSpanForObject(Object obj) {
    Cursor cursor = (Cursor)obj;
    Contact contact = new Contact(cursor);
    return super.buildSpanForObject(contact);
}

my theory is that will have everything after that point working with a Contact in the TokenImageSpan and in objects.

i haven't yet tested that, but i plan to in a few minutes. figured i'd file this in case i'm on the wrong track or there's a better way to go about it.

best,

-rm

View state is not restored when leaving the app followed by returning to it

Problem:
The View state is not properly restored in the event that the user leaves the app and then returns back to it.
Observation:
If one token is present in the View before leaving the app, upon returning the TokenAutoComplete View displays two commas " ,, ".
Expected:
Should display the token.

Remove token is unreliable when focus is in another field

Remove token does not work properly when another field is focused. The view state is corrupted if the Remove Token button is used when focus is in another field.

Steps to reproduce in the example app:

  1. Add a token (for a total of 3).
  2. Focus second field.
  3. Touch Remove Token.

The +2 is not changed to reflect the removal of a token.

Another error I saw:
If I add two tokens (for a total of four), focus the other field, and then try to remove a token, I am left with 3 tokens. The Remove Token button no longer works after that series of actions though.

TokenDeleteStyle.Clear leaves undeletable characters behind after deleting token.

Reproduction: type, select a completion, tap delete on softkeyboard a few times to delete characters. Eventually you will be unable to delete any more characters leaving one or two behind.

Leaving field and returning seems to allow you to delete the characters.

100% reproducible on Nexus 5 softkeyboard, unable to reproduce on emulator with emulated keyboard or softkeyboard.

Switching to TokenDeleteStyle._Parent seems to work around the problem

Single line not working correctly

Hello. This is great library, but I have some problem, when I want that TockenView were singleLine. This issue looks like follows (screenshot).
Can you help me to fix this? Thank you.
bug

Getting java.io.NotSerializableException when clicking on send button

Hi, thanks for this wonderful library. I am implementing it in one of my apps. However, I am having trouble in a few things.

  1. How do you change the layout of drop down list. I want to show names and photos there. For now I am showing just names by returning their names from overridden toString method.

    @Override
    public String toString() { return mContactFirstName; }
    
  2. The token I am adding the the text box have an attribute which is the instance of a custom drawable class. This custom drawable class gives the bitmap of people. Now I add these tokens and click on send button I am getting "java.io.NotSerializableException" because of that custom drawable class. How can I fix it?

    Caused by: java.io.NotSerializableException: com.example.demo.customview.CustomDrawable
        at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1535)
        at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1847)
    

Thanks.

add an object programmatically

i'm currently working on extending this to allow adding objects programmatically, e.g. if the user selected a contact in another activity and we're auto-filling that contact. i'd love any help you can offer!

thanks for TokenAutoComplete, btw, it's great!

Issue when field in landscape

There is an issue when the field is on landscape - instead of name you see comma. This only happens when on landscape and the field is opened to write text.

Perform completion gives a wrong token at times

I try to add token using performComplition if the given text is a valid email Id.
Here is the code I use:
ContactsCompletionView mToField;
isValidEmail(mToField.getText().toString().substring(4)) {
mToField.performCompletion();
}
Many times I get a token for the first contact in my contact list. Why is that?

For example, I start typing [email protected] – this id doesn’t exist in my contact list. when I call performcompletion – it adds [email protected] – which is the first record in my contact list. No idea why is this happening.

Calling API Level 16 in `invalidate()`

In invalidate() method there are calls to methods belong to API Level 16:

setShadowLayer(getShadowRadius(), getShadowDx(), getShadowDy(), getShadowColor());

I thing the conditional-if should check against 16 instead of 14.

onTokenAdded and onTokenRemoved don't work

Whenever I add or remove a token to the view, neither of these are called regardless if the object is added programmatically or manually.

We are targeting 18 and we are running on 19.

Input type, no completion, leaving before completion, remove all objects, custom delimiter

I don't know if I should split this into 5 issues or just wrap them into this one issue :).

  1. Calling setInputType(int) in init() makes android:inputType in XML layout ignored, doesn't it? Sometime I want to set my inputType in the XML layout.
  2. Suggestion: how about adding an option to make user input as it be, so if the user doesn't click any item in the completion list, the token is the entered one.
  3. When user leaves the view before making it a token (by pressing ",") it does not become a token.
  4. Suggestion: how about adding such removeAllObjects() method besides removeObject(Object).
  5. Suggestion: how about making the delimiter (currently ",") as an option/variable.

TokenDeleteStyle does not work as it should be.

  1. "Clear" sometimes like "PartialCompletion" : not always remove all the token text but left the original text used for completion. the behavior deleting after the space following the Token and deleting just after the Token are different...
  2. if now have two Tokens, just delete the first one after the space following the Token, will delete all the tokens...

3."ToString": also not always work as expected.sometimes like "PartialCompletion"

Suggestions can't be seen.

I have tried to use TokenCompleteTextView in a fragment dialog. As i entered more and more contacts the suggestions that appeared decreased and finally it disappeared and resulted in a crash. The LogCat's reason for the crash is : "OpenGLRenderer(6185): GL_INVALID_VALUE". Maybe if you give support for maxLines, there can be workaround so that atleast a minimum number of suggestions will be visible always. I hope you guys will resolve this as early as possible.

Keyboard fails to come up when view is focused the first time

My TokenAutoCompleteView does not open the keyboard when it receives focus from a tap. The cursor shows up in the view, but the keyboard will only open if you tap inside it a second time. Has anyone else seen this issue and/or know how to resolve it?

I added an OnFocusChangeListener using myAutoCompleteView.setOnFocusChangeListener() and it reports that the view receives focus, but still no keyboard.

Use generics instead of unchecked casts for more type-safety

I've changed the library (and the example app) to use generics to enforce type-safety at compile time. This keeps you from having to cast every Object that comes back from the view. The app code becomes much cleaner at the expense of generic use and some object casting in the library.

Let me know what you think, I could create a pull request if you like the changes.

https://github.com/octanner/TokenAutoComplete/commit/6b4685711a2134cee97998888ed2ecaf39494e67

Crash when removing objects after loss of focus

Hi, thanks for this library. We're hitting a crash in updateCountSpan that occurs when we remove objects after the view has lost focus. On loss of focus the text is collapsed to a single line, creating a CountSpan to cover the not-visible text entries. When we subsequently remove the objects from the view, each object causes the CountSpan count to be decremented. Because the CountSpan only covered a subset of text entries, this causes the count to go below zero, which causes text.getSpanStart() in updateCountSpan to return -1, and then text.delete() crashes.

NullPointerException if currenctCompletionText is called in a TextWatcher onTextChanged method

11-06 10:09:41.435  18658-18658/com.tokenautocomplete E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tokenautocomplete/com.tokenautocomplete.TokenActivity}: java.lang.IndexOutOfBoundsException: getChars (4 ... 0) has end before start
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.IndexOutOfBoundsException: getChars (4 ... 0) has end before start
            at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1009)
            at android.text.SpannableStringBuilder.getChars(SpannableStringBuilder.java:913)
            at android.text.TextUtils.getChars(TextUtils.java:77)
            at android.text.TextUtils.substring(TextUtils.java:263)
            at com.tokenautocomplete.TokenCompleteTextView.currentCompletionText(TokenCompleteTextView.java:241)
            at com.tokenautocomplete.ContactsCompletionView$1.onTextChanged(ContactsCompletionView.java:31)
            at android.widget.TextView.sendOnTextChanged(TextView.java:7304)
            at android.widget.TextView.handleTextChanged(TextView.java:7363)
            at android.widget.TextView$ChangeWatcher.onTextChanged(TextView.java:9062)
            at android.text.SpannableStringBuilder.sendTextChanged(SpannableStringBuilder.java:962)
            at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:496)
            at android.text.SpannableStringBuilder.insert(SpannableStringBuilder.java:207)
            at android.text.SpannableStringBuilder.insert(SpannableStringBuilder.java:30)
            at com.tokenautocomplete.TokenCompleteTextView.updateHint(TokenCompleteTextView.java:708)
            at com.tokenautocomplete.TokenCompleteTextView.onRestoreInstanceState(TokenCompleteTextView.java:1079)
            at android.view.View.dispatchRestoreInstanceState(View.java:12284)
            at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2623)
            at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2623)
            at android.view.View.restoreHierarchyState(View.java:12262)
            at com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:1647)
            at android.app.Activity.onRestoreInstanceState(Activity.java:938)
            at android.app.Activity.performRestoreInstanceState(Activity.java:910)
            at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1138)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2189)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)

Error when removing token when the field does not have focus

An IndexOutOfBoundsException is thrown when using example app. This happens when the focus is on the second field, and you remove a token.

Steps to reproduce in the example app:

  1. Focus second field.
  2. Touch Remove Token.
  3. Focus back into the autocomplete field.
  4. Focus second field.
 java.lang.IndexOutOfBoundsException: replace (8 ... 8) ends beyond length 7
            at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1016)
            at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:441)
            at android.text.SpannableStringBuilder.insert(SpannableStringBuilder.java:207)
            at android.text.SpannableStringBuilder.insert(SpannableStringBuilder.java:30)
            at com.tokenautocomplete.TokenCompleteTextView.handleFocus(TokenCompleteTextView.java:474)
            at com.tokenautocomplete.TokenCompleteTextView.onFocusChanged(TokenCompleteTextView.java:527)
            at android.view.View.clearFocusInternal(View.java:4728)
            at android.view.View.unFocus(View.java:4762)
            at android.view.ViewGroup.requestChildFocus(ViewGroup.java:624)
            at android.view.View.handleFocusGainInternal(View.java:4610)
            at android.view.View.requestFocusNoSearch(View.java:6999)
            at android.view.View.requestFocus(View.java:6978)
            ...

Clear all the objects of TokenAutoComplete

How can I clear all the objects of the TokenAutoComplete?

I try this code but it doesn't delete the objects.

for (int i = 0; i < multi_txt_toggle.getObjects().size(); i++) {
multi_txt_toggle.removeObject(multi_txt_toggle.getObjects().get(i));
}

PS: multi_txt_toggle is the TokenCompleteTextView

Allow clicking on a token to deleted or select the token

clicking on the token currently selects the image span like text. I'd like to be able to customize the behavior to allow the token to either be immediately deleted or get selected, then a second click or text edit deletes the token.

disallowing "just any string" to be created as a token.

I would like to suggest adding support for disallowing certain strings from being formatted into tokens. That is, I want the user to only choose from the contact list, or type in properly formed emails or phone numbers. No "John Smith" unless that's on the contact list.
Right now, I can't return null from defaultObject(), so I flagged the contact (person object) as "bad", and checked onTokenAdded() for the flag and just removeObject() on true. It is a silly work around which I'm sure can be better handled in the original library.

height calculation and keyboard unhappy

TokenAutoComplete is pretty unhappy in my app after synching and merging this morning. the view height calculation seems to be off (see screenshot), and the keyboard consistently hides after typing a letter.

i stripped down my XML layout params and dropped the prefix, hint, and allowDuplicates(false), but i see the same behavior.

828d386 was the last happy commit for me; the problems started somewhere between 9dfb73c and head.

not urgent on my end, but figured you'd want a heads up.

device-2013-10-11-121215

Crash on long press back button

Whenever I long press the back button, it results in a crash:

07-07 20:50:51.034    7325-7325/in.co.madhur.vocabbuilder E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: in.co.madhur.vocabbuilder, PID: 7325
    java.lang.IndexOutOfBoundsException: setSpan (5 ... 5) ends beyond length 4
            at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1016)
            at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:592)
            at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:588)
            at android.text.Selection.setSelection(Selection.java:76)
            at android.text.Selection.setSelection(Selection.java:87)
            at android.widget.EditText.setSelection(EditText.java:94)
            at com.tokenautocomplete.TokenCompleteTextView.onSelectionChanged(TokenCompleteTextView.java:464)
            at android.widget.TextView.spanChange(TextView.java:7517)
            at android.widget.TextView$ChangeWatcher.onSpanChanged(TextView.java:9208)
            at android.text.SpannableStringBuilder.sendSpanChanged(SpannableStringBuilder.java:999)
            at android.text.SpannableStringBuilder.sendToSpanWatchers(SpannableStringBuilder.java:567)
            at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:500)
            at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:212)
            at android.text.SpannableStringBuilder.delete(SpannableStringBuilder.java:30)
            at android.view.inputmethod.BaseInputConnection.deleteSurroundingText(BaseInputConnection.java:243)
            at android.view.inputmethod.InputConnectionWrapper.deleteSurroundingText(InputConnectionWrapper.java:66)
            at com.tokenautocomplete.TokenCompleteTextView$TokenInputConnection.deleteSurroundingText(TokenCompleteTextView.java:1248)
            at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:382)
            at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:77)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
            at dalvik.system.NativeStart.main(Native Method)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.