Mehdi bahmanpour
Mehdi bahmanpour

Reputation: 614

EditText lag when searching a list in a textWatcher

Here is my TextWatcher :

searchBox.addTextChangedListener(new TextWatcher() {

            @Override
            public void beforeTextChanged( CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(final CharSequence charSequence, int i, int i1, int i2) {
                ArrayList<ProductInformation> newList = new ArrayList<>();
                for (ProductInformation info : currentList)
                    if (info.getName().contains(charSequence))
                        newList.add(info);
                productListAdapter.setNewData(newList);

            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });

After adding this listener to my editText its lagging when typing . I guess the problem is searching in list ( list has 40 to 60 items in it ) .

What can i do ?

Upvotes: 1

Views: 996

Answers (2)

Tam Huynh
Tam Huynh

Reputation: 2487

A real-time text search has some requirements for better performance:

Requirement

  • You shouldn't start to search immediately because user typing speed is quite fast, and next result immediately overrides the last one. Multiple search process runs but only the last result will be shown is wasted on performance.

  • Try to make the search asynchronous, even if it's a network request or just a local search so it won't harm the UI and can be canceled easily.

Solution

Use Timer and TimerTask for delay and cancel during typing

private Timer mRequestDelayTimer;
private TimerTask mSearchTask;

@Override
public void onTextChanged(final CharSequence query, int i, int i1, int i2) {
    if (mRequestDelayTimer != null)
        mRequestDelayTimer.cancel();

    // If you do a async search, stop it here
    stopSearch();

    mRequestDelayTimer = new Timer();
    mSearchTask = new TimerTask() {
        @Override
        public void run() {
            startSearch(query);
        }
    };
    mRequestDelayTimer.schedule(mSearchTask, 500);
}

private synchronized void startSearch(String query) {
    // This is not run in UI thread. Use activity.runOnUiThread if you want to update the UI
    // Call an AsyncTask to do an async search or run a sync loop search here

    //ArrayList<ProductInformation> newList = new ArrayList<>();
    //for (ProductInformation info : currentList)
    //    if (info.getName().contains(charSequence))
    //        newList.add(info);
    //productListAdapter.setNewData(newList);
}

Explanation

If each character typed in is within 500ms, it doesn't call startSearch because the timer keeps delaying.

If user rest for more than 500ms, startSearch is called off the UI thread. Then if the user starts to type while the search is still in progress, it will be canceled.

If you are doing the search and update result in an ArrayAdapter, it has a Filter process built-in, you can custom it by overriding getFilter. This filter automatically runs in the background, do the cancelation for each character enter, very handy. Ref: Custom getFilter in custom ArrayAdapter in android

Your actual implementation may be different, but the idea is the same

Upvotes: 1

Ghulam Moinul Quadir
Ghulam Moinul Quadir

Reputation: 1648

You can avoid lagging of search time with the logic that if time difference between two characters entered is greater than a specific time then you start searching in the list to update productListAdapter and if not then stay idle.

Here is the source code:

        startTime = System.currentTimeMillis();
        endTime = System.currentTimeMillis();
        searchBox.addTextChangedListener(new TextWatcher() {

        @Override
        public void beforeTextChanged( CharSequence charSequence, int i, int i1, int i2) {

        }

        @Override
        public void onTextChanged(final CharSequence charSequence, int i, int i1, int i2) {
            endTime = System.currentTimeMillis();
            if(endTime - startTime >= 1000) {
                ArrayList<ProductInformation> newList = new ArrayList<>();
                for (ProductInformation info : currentList)
                    if (info.getName().contains(charSequence))
                        newList.add(info);
                productListAdapter.setNewData(newList);
            }
            startTime = endTime;
        }

        @Override
        public void afterTextChanged(Editable editable) {

        }
    });

Upvotes: 1

Related Questions