10

I am working on this app where users can enter a location in an autoCompleteTextView and it will suggest locations based on the Google Places Api as is described here.

However i would like to restrict the amount of request send by the app by only sending requests if the user stops typing for a certain amount of time. Does anyone know how to do this?

hholtman
  • 141
  • 7

4 Answers4

14

Similar to this answer but slightly more concise and without introducing extra state. You also don't need the threshold checks since performFiltering is only called when filtering is actually required.

Subclassing AutoCompleteTextView seems to be the only way to go, since you cannot override/replace the TextWatcher added by AutoCompleteTextView.

public class DelayAutoCompleteTextView extends AutoCompleteTextView {           
    public DelayAutoCompleteTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            DelayAutoCompleteTextView.super.performFiltering((CharSequence) msg.obj, msg.arg1);
        }
    };

    @Override
    protected void performFiltering(CharSequence text, int keyCode) {
        mHandler.removeMessages(0);
        mHandler.sendMessageDelayed(mHandler.obtainMessage(0, keyCode, 0, text), 750);
    }
}
Community
  • 1
  • 1
Jan Berkel
  • 3,373
  • 1
  • 30
  • 23
  • I am using an adapter for the AutoCompleteTextView that implements the Filterable interface and has its own performFiltering method. Can I achieve the above functionality using this setup? – Price Dec 02 '14 at 07:48
  • I think you'd lose any characters typed between when the delayed message is scheduled and when the `handleMessage()` gets called, unless the `AutoCompleteTextView` hands you a reference to the internal `CharSequence` and not a copy of the text so far. Probably safer to have the `handleMessage()` method pull the text from the View when it calls `performFiltering()` – Colin M. Feb 11 '15 at 18:27
  • @ColinM., there is only a split-second delay between the two events. So I guess it's probably okay to filter based on the text captured when the message was scheduled – Price Jul 11 '15 at 06:40
4

I have found a solution to the above problem which is working quite nicely. I have extended the AutoCompleteTextView in the following way. If anyone knows how to further improve this code please let me know.

public class MyAutoCompleteTextView extends AutoCompleteTextView {

// initialization
int threshold;
int delay = 750;
Handler handler = new Handler();
Runnable run;

// constructor
public MyAutoCompleteTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
protected void performFiltering(final CharSequence text, final int keyCode) {
    // get threshold
    threshold = this.getThreshold();

    // perform filter on null to hide dropdown
    doFiltering(null, keyCode);

    // stop execution of previous handler
    handler.removeCallbacks(run);

    // creation of new runnable and prevent filtering of texts which length
    // does not meet threshold
    run = new Runnable() {
        public void run() {
            if (text.length() > threshold) {
                doFiltering(text, keyCode);
            }
        }
    };

    // restart handler
    handler.postDelayed(run, delay);
}

// starts the actual filtering
private void doFiltering(CharSequence text, int keyCode) {
    super.performFiltering(text, keyCode);
}

}
hholtman
  • 141
  • 7
  • 1
    Thanks for sharing. I used this for the Google Place API at https://developers.google.com/places/training/autocomplete-android – Mark Pazon Apr 16 '13 at 15:34
0

You could use a TextWatcher, and monitor how long between text entries. I personally don't like the idea of sub-classing native Android controls (though, it is certainly a valid approach, just not my preferred approach).

The docs: http://developer.android.com/reference/android/text/TextWatcher.html

and a SO link: How to use the TextWatcher class in Android?

Community
  • 1
  • 1
Booger
  • 18,579
  • 7
  • 55
  • 72
0

Similar to the answers above, I found I needed to subclass AutoCompleteTextView. However, because internet is a bit slow where I live, I found that removing the delayed handlers every time the user pressed a key was a problem. So basically if I only ran the filter maybe 500ms after the user stopped typing, it might take seconds for the results to appear, which would make the user annoyed.

So instead, I do not clear the handler every time, and let filtering run every couple hundred ms. My code is much less clean Jan Berkel's. I'm sure you can clean it up a bit I'm too lazy, but performance is better in areas with slow internet IMO.

public class CustomCompleteView extends AutoCompleteTextView{

Handler handler=new Handler();
Runnable r;
boolean cleartogo=false;
boolean pending =false;
CharSequence btext; 
int bkeyCode;
public CustomCompleteView(Context context) {
    super(context);
    // TODO Auto-generated constructor stub
}

public CustomCompleteView(Context context, AttributeSet attrs)
{
    super(context,attrs);
}
public CustomCompleteView(Context context, AttributeSet attrs, int defStyle)
{
    super(context,attrs,defStyle);
}

@Override
public void performFiltering(CharSequence text, int keyCode){
    if (cleartogo){
        cleartogo=false;
        Log.d(MainActivity.DTAG,"Going to filter on " + text.toString());
        pending=false;
        super.performFiltering(btext, bkeyCode);
    }
    else
    {
        Log.d(MainActivity.DTAG,"Filtering rejected, too soon");
        btext=text;
        bkeyCode=keyCode;
        if (!pending){
        if (r==null)
            r=new MyRunnable(this);
        //try{handler.removeCallbacks(r);} catch (Exception ex){};
        handler.postDelayed(r, 500);
        pending=true;}
    }

}

private class MyRunnable implements Runnable {


CustomCompleteView bc;


    MyRunnable(CustomCompleteView c ) {
        this.bc=c;

    }

    public void run() {
        Log.d(MainActivity.DTAG,"Special Runnable running");
        cleartogo=true;
        bc.performFiltering(btext, bkeyCode);


    }
}

}
D2TheC
  • 2,203
  • 20
  • 23