Vaibs
Vaibs

Reputation: 1138

Text change should be detected after

In a AutoCompleteTextView I want to show the auto complete list after there is a pause of 1 sec by user in typing. I tried using handler

handler.postDelayed( new Runnable() {

            @Override
            public void run() {

                // TODO Auto-generated method stub
                if ( text.length() >= 3 ) {
                    // do something                     

                } else if ( text.length() == 0 ) {
                    // do something

                }
            }

        }, 1000 );

This code is a part of onTextChanged. So what it is happening is whenever there is a text change at that moment postDelayed is called and inside code will be called after a second. So how can I prevent that so the inner code is only called when there is a puase of 1 sec by user in typing.

e.g: If I type Ind (pause of 1 sec) then inner code execute should happen. But I type India then inner code should not execute for Ind, Indi, India. Need your suggestions.

Upvotes: 1

Views: 321

Answers (4)

John
John

Reputation: 7826

I just stood at the same problem.
I am calling Googles Geocode suggestion API onTextChanged() and that caused many issues. Users typing would cause masses of API requests, most of them not required and that uses up the API (costs money) and reduces the experience the user receives from the app.

I considered the solutions here but I did not like any of them in the end, they seemed unclean or like a hack.
I did not want to make further calls into the UI to get the current value of the View (again) or check system times.

The original question actually already solves the problem, it just causes multiple runnables to be alive when a user types.
So the question should be: "How to remove my anonymous runnable before it causes trouble ?"

All you have to do it use the original approach (the delayed call using the anonymous runnable) and whenever the onTextChanged function is called you REMOVE any previous runnables.
You can also extend the code and check if the value really changed (like using global previous value String) so the runnable is not killed if the user entered a char and removed it again.

So all you need to do: 1. Make the Handler a variable of the class instead of creating it inside the onTextChanged.
2. At the begin of onTextChanged remove the runnables from the handler

Here is a fully functional example:

    Handler onchangeHandler =  new Handler();
    String last_onTextChanged=""; // may speed up some cases of user input, can be removed
    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i2, int i3)
    {
        final String value = charSequence.toString();
        if (value.length() < 3) {autoCompleteAdapter.clear();return;} // threshold of 3 chars

        if (!last_onTextChanged.equals(value))
            onchangeHandler.removeCallbacksAndMessages(null);
        else
            return;
    last_onTextChanged=value;
    onchangeHandler.postDelayed(new Runnable()
    {
        @Override
        public void run()
        {
            // RUN CODE HERE, fill your autoCompleteAdapter etc.
        }
    },800);

}

Upvotes: 1

Longerian
Longerian

Reputation: 723

you can also cancel the previous Runnable msg in message queue first in onTextChanged()

Upvotes: 0

Nickolaus
Nickolaus

Reputation: 4835

Create two static variables holding timestamps.
lastTimeStamp and currentTimeStamp, then you can do something like this:

        @Override
        public void run() {
            if (currentTimeStamp - lastTimeStamp > 1000) {
               // TODO Auto-generated method stub
               if ( text.length() >= 3 ) {
                  // do something                     

               } else if ( text.length() == 0 ) {
                     // do something

               }
            }
        }

New approach:

At the beginning of your onTextChanged method, put a current TimeStamp in a class-variable.
Thereafter create an AsyncTask in the onTextChanged, which is just doing a Thread.sleep(1000) in the doInBackground-method.
Then you make an if-statement in the postExcute method, checking if the difference between TimeStamp in the class-variable and the current TimeStamp, if this is larger than 1000 post your handler.

Upvotes: 2

tundundun
tundundun

Reputation: 644

There are 2 options:

  1. Use Timer and invoke cancel() on previous tasks when you are going to run the next one.

  2. Extend Runnable and pass in a string for this autocompletion. If this string is not equal to current string in view (that means user changed the string), then you should not display autocompletion.

Code example:

abstract class MyRunnable {
    private String str;

    public MyRunnable(String str) {
        this.str = str;
    }
}


...

handler.postDelayed(new MyRunnable(currentViewStr) {

    @Override
    public void run() {
        if (currentViewStr.equals(str)) {
            // show autocompletion
        }
    }
}, 1000);

Upvotes: 0

Related Questions