You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EditText结合TextWatcher搜索列表时输入卡顿,求解决方案

Fixing Search Lag in Your TextWatcher for a 40-60 Item List

Hey there, let's tackle that input lag issue you're seeing. Even with a small list (40-60 items), your current setup can cause stutters—here's why and how to fix it:

Why the Lag Happens

Your onTextChanged callback runs on the main thread, which handles all UI updates. Every time the user types a character, you:

  • Iterate through the entire list synchronously
  • Create a new ArrayList
  • Call setNewData which triggers a full RecyclerView refresh
    Even with 60 items, this repeated main-thread work adds up and blocks UI rendering, causing the lag you're seeing.

Solution 1: Use RecyclerView's Built-in Filterable Interface

The Filterable interface is designed exactly for this scenario—it runs the filtering logic on a background thread automatically, so your main thread stays free for UI work. Here's how to implement it:

Step 1: Update your Adapter to implement Filterable

public class ProductListAdapter extends RecyclerView.Adapter<ProductListAdapter.ViewHolder> implements Filterable {
    private List<ProductInformation> originalList;
    private List<ProductInformation> filteredList;

    // Constructor: Initialize originalList and filteredList
    public ProductListAdapter(List<ProductInformation> productList) {
        this.originalList = new ArrayList<>(productList);
        this.filteredList = new ArrayList<>(productList);
    }

    // ... Your existing ViewHolder and onBindViewHolder code ...

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                List<ProductInformation> filtered = new ArrayList<>();

                // Filter logic runs on background thread
                if (constraint == null || constraint.length() == 0) {
                    filtered.addAll(originalList);
                } else {
                    String query = constraint.toString().toLowerCase().trim();
                    for (ProductInformation info : originalList) {
                        if (info.getName().toLowerCase().contains(query)) {
                            filtered.add(info);
                        }
                    }
                }

                results.values = filtered;
                results.count = filtered.size();
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                filteredList.clear();
                filteredList.addAll((List<ProductInformation>) results.values);
                notifyDataSetChanged(); // Runs on main thread
            }
        };
    }

    // Optional: Add a method to update the original list if needed
    public void updateOriginalList(List<ProductInformation> newList) {
        this.originalList = new ArrayList<>(newList);
        getFilter().filter(""); // Reset filter
    }
}

Step 2: Update your TextWatcher to use the filter

searchBox.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        // Just trigger the filter—no heavy work here!
        productListAdapter.getFilter().filter(charSequence);
    }

    @Override
    public void afterTextChanged(Editable editable) {}
});

Solution 2: Add Debouncing to Reduce Unnecessary Searches

Even with background filtering, triggering a search on every single character (like when a user types fast) can be overkill. Add a small delay (300ms is a good starting point) to wait until the user stops typing before running the search. Here's how to do it with a Handler:

private Handler searchHandler = new Handler(Looper.getMainLooper());
private Runnable searchRunnable;

searchBox.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        // Cancel any pending search runnable
        if (searchRunnable != null) {
            searchHandler.removeCallbacks(searchRunnable);
        }

        // Create a new runnable to perform the search after delay
        final String query = charSequence.toString();
        searchRunnable = () -> {
            // Use the filter from Solution 1, or your optimized search logic here
            productListAdapter.getFilter().filter(query);
        };

        // Post the runnable with 300ms delay
        searchHandler.postDelayed(searchRunnable, 300);
    }

    @Override
    public void afterTextChanged(Editable editable) {}
});

Solution 3: Optimize the Search Logic Further

  • Pre-process names: Convert all product names to lowercase once (when you load the list) instead of every time you check contains. This saves repeated string processing.
  • Use DiffUtil for smarter UI updates: Instead of notifyDataSetChanged() (which refreshes all items), use DiffUtil to calculate the difference between the old and new list, then only update the changed items. This reduces UI rendering overhead.

Quick Win: Avoid Creating a New ArrayList Every Time

If you stick with your original approach, at least reuse a single ArrayList instead of creating a new one on every text change. This reduces garbage collection overhead:

// Declare this as a class variable
private ArrayList<ProductInformation> tempList = new ArrayList<>();

@Override
public void onTextChanged(final CharSequence charSequence, int i, int i1, int i2) {
    tempList.clear();
    String query = charSequence.toString().toLowerCase();
    for (ProductInformation info : currentList) {
        if (info.getName().toLowerCase().contains(query)) {
            tempList.add(info);
        }
    }
    productListAdapter.setNewData(tempList);
}

Any of these solutions should eliminate the lag you're seeing—start with Solution 1 (Filterable) since it's the most idiomatic Android approach, then add debouncing if you still want to reduce unnecessary filter runs.

内容的提问来源于stack exchange,提问作者Mehdi bahmanpour

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:59:39